日志组件

项目开发时,日志系统是必不可少的东西,不管是开发过程中的调试还是生产环境中的问题跟踪,都离不开日志。在日常的工作中,接触过了Commons logging,JDK logging,Log4J,slf4J还有logback,大概知道怎么用,但对很多细节都不太了解,下面系统地整理一遍。


日志接口与日志实现

首先要明确的是,Apache的commons logging和另外一个slf4j是日志控件,是日志接口,本身并没有具体的日志功能,它们需要和日志实现框架一起使用。

当然,实现了具体日志功能的日志框架也能单独使用,也就是说,JDK logging,Log4J和logback都能单独使用,但是这样就会造成在一个项目中使用了多个日志实现框架时,需要维护和管理不同的日志系统,非常麻烦。比如,不同的日志框架打印格式和日志级别不同,配置文件也不同。而日志接口框架起到这样一个作用:它可以将不同的日志实现框架整合起来使用。例如,你可以在项目中使用log4j,也可以使用logback,最终都通过slf4j表现出来,通过一个配置文件去控制日志打印格式和日志级别。

除了这两个日志接口控件之外,其他的都是能实现具体日志功能的日志系统。

常用的组合有:

  • Commons logging + JDK logging
  • Commons logging + Log4J
  • slf4J + Log4J
  • slf4J + logback。

在最简单的小系统中,可能只需要Commons logging + JDK logging就可以应对了。而在大系统中,具体的日志实现经常用到的是log4j和logback。

在早期大部分系统都使用的是Commons logging + Log4J,后来slf4J + Log4J使用较多,再然后则slf4J + logback用得比较多。

Commons logging + JDK logging/Log4J

先说Commons logging 与JDK logging/ Log4J的组合。

Commons logging使用时要引入commons-logging包,然后根据引入的具体的日志系统的包来选择具体的日志实现。只要在项目中添加了log4j的包,commons-logging就会自动选择log4j进行日志输出。

在进行日志输出时可以通过log4j.properties或log4j.xml进行日志系统配置,控制日志输出位置和输出级别等。

如果没有引入log4j的包,commons-logging就会使用JDK自带的logging实现日志功能。下面举几个简单的例子:

不引入log4j的包,默认使用JDK的日志输出功能:

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
public class LogTest {
    Log logger = LogFactory.getLog(LogTest.class);
    @Test
    public void test1(){
        System.out.println("test1");
        logger.debug("debug");
        logger.debug("info");
        logger.debug("error");
    }
}

当然只能输出info级别以上的信息,因为其默认级别就是info。

引入log4j的包,不提供log4j.properties配置文件。控制台输出:

log4j:WARN No appenders could be found for logger (cn.tonghao.component.LogTest).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

添加log4j.properties配置文件:

log4j.rootLogger=DEBUG,console        
#输出到控制台
log4j.appender.console=org.apache.log4j.ConsoleAppender    
#编码格式
log4j.appender.console.encoding=UTF-8
#设置输出样式
log4j.appender.console.layout=org.apache.log4j.PatternLayout   
#日志输出信息格式为
log4j.appender.console.layout.ConversionPattern=[%-d{yyyy-MM-dd HH:mm:ss}]-[%t-%5p]-[%C-%M(%L)]: %m%n   

后正常输出日志信息。

这里需要说明一点:当log4j.properties放在类路径下时,LoggerFactory会自动去加载该配置文件。如果放在其他地方,则需要在web.xml中配置监听器,如下:

 <context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>classpath:log4j.properties</param-value>
  </context-param>
  <!--检测日志配置  文件变化-->
  <context-param>
    <param-name>log4jRefreshInterval</param-name>
    <param-value>60000</param-value>
  </context-param>
  <!--配置监听器-->
  <listener>
    <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
  </listener>

注意,检测日志配置文件变化的参数作用是在修改log4j.properties之后配置文件动态生效,生效时间就是配置的检测时间。

slf4j + Log4j/logback

然后说说slf4j。

Slf4j除了可以整合不同的日志实现组件外,还提供占位符,即可以用{}来代替变量,避免设置打印消息时进行字符串拼接。如logger.info(“xxx,{}”,a)打印出来就是xxx,a。

slf4j提供各种抽象接口,日志应该基于slf4j的API进行日志打印,这样无论迁移到那个项目,只需要配一个实现类log4j or logback,都能正常打印日志。所以在选择日志框架的时候,一般选用slf4j,当然,要选择一个具体实现的日志框架。slf4j的实现类不能有多个,不然会产生冲突。实现类用log4j还是logback好呢?其实这两个日志实现框架的作者是同一个人,logback是对log4j的升级改进。在大多数情况下,logback的性能都优于log4j。

要使用slf4j+log4j,需要引入几个包,分别是:slf4j-api,log4j的jar包,和两者绑定jar包slf4j-log4j12。Logback则需要slf4j-api,logback-core和logback-classic。如果要使用slf4j+logback,而项目中又有其他地方直接使用log4j的怎么办呢?可以加入log4j-over-slf4j,把旧的日志log4j适配到slf4j,这时候,再使用logback就可以了。同样,也可以通过引入jcl-over-slf4j来适配commons-logging系统。如:

<dependency> 
    <groupId>org.slf4j</groupId> 
    <artifactId>slf4j-api</artifactId> 
    <version>${org.slf4j-version}</version> 
</dependency> 
<dependency> 
    <groupId>org.slf4j</groupId> 
    <artifactId>jcl-over-slf4j</artifactId> 
    <version>${org.slf4j-version}</version> 
</dependency> 
<dependency> 
    <groupId>org.slf4j</groupId> 
    <artifactId>log4j-over-slf4j</artifactId> 
    <version>${org.slf4j-version}</version> 
</dependency> 
<dependency> 
    <groupId>org.slf4j</groupId> 
    <artifactId>jul-to-slf4j</artifactId> 
    <version>${org.slf4j-version}</version> 
</dependency> 
<dependency> 
    <groupId>org.jboss.logging</groupId> 
    <artifactId>jboss-logging</artifactId> 
    <version>3.1.4.GA</version> 
</dependency> 

使用logback时,如果没有配置logback.xml,则系统默认打印DEBUG级别日志。系统启动的时候会自动扫描classpath路径下的logback.xml和logback-test.xml,如果找到就使用配置文件的配置。下面是一个logback.xml配置模板:

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
<property name="LOG_HOME" value="/home" />
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_JOME}/testFile.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/TestWeb.log.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>30</MaxHistory>
<TimeBasedFileNamingAndTriggeringPolicy   class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
   <maxFileSize>50MB</maxFileSize>
</TimeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>

<!-- 日志输出级别 -->
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>

上面说过,系统启动时自动扫描类路径,按logback-groovy,logback-test,logback的顺序就行查找,找到了就使用其作为配置文件。但如果想使用自己指定的配置文件怎么办呢?方法是:根目录下保留一个最简单的logback.xml,然后在spring配置文件如spring-log.xml中配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="loggingInitialization"
          class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="targetClass"
                  value="cn.tonghao.environment.LogConfigurerSupportMultiEnvironment"/>
        <property name="targetMethod" value="registLogConfiguration"/>
        <property name="arguments">
            <list>
                <value>classpath:config/log/${logback.name}.xml</value>
            </list>
        </property>
    </bean>
</beans>

通过LogConfigurerSupportMultiEnvironment类重新初始化指定的logback配置文件。

public class LogConfigurerSupportMultiEnvironment {

	public static void registLogConfiguration(String logConfigLocation) throws FileNotFoundException, JoranException {
		String resolvedLocation = SystemPropertyUtils.resolvePlaceholders(logConfigLocation);
		if (resolvedLocation.toLowerCase().endsWith(".xml")) {
			URL url = ResourceUtils.getURL(resolvedLocation);
			LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
			loggerContext.reset();
			JoranConfigurator joranConfigurator = new JoranConfigurator();
			joranConfigurator.setContext(loggerContext);
			joranConfigurator.doConfigure(url);
		}
	}
}

比如重新初始化logback-dev.xml(其中tomcat_base_path为tomcat配置文件中的变量):

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <property name = "path_bath" value="/home"/>

    <!-- 控制台输出日志 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%msg%n</pattern>
        </encoder>
    </appender>

    <!-- 文件输出日志 (文件大小策略进行文件输出,超过指定大小对文件备份) -->
    <appender name="FILE"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>${path_bath}/${tomcat_app_base}/ly.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>
                ${path_bath}/${tomcat_app_base}/ly.%d{yyyy-MM-dd}-%i.log
            </FileNamePattern>
            <maxHistory>30</maxHistory>
            <TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>50MB</maxFileSize>
            </TimeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{32} -  %msg%n</Pattern>
        </encoder>
    </appender>

    <!--业务监控日志-->
    <appender name="BUS"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>${path_bath}/${tomcat_app_base}/lyBus.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>
                ${path_bath}/${tomcat_app_base}/lyBus.%d{yyyy-MM-dd}-%i.log
            </FileNamePattern>
            <maxHistory>30</maxHistory>
            <TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>50MB</maxFileSize>
            </TimeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{32} -  %msg%n</Pattern>
        </encoder>
    </appender>

    <logger name="lyBusLog" level="INFO" additivity="false">
        <appender-ref ref="BUS"/>
    </logger>

    <root level="INFO">
        <appender-ref ref="FILE"/>
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

参考文档:

[1]. http://blog.csdn.net/tianlincao/article/details/6101630
[2]. http://www.cnblogs.com/huayu0815/p/5341712.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值