java日志系统-- slf4j + log4j

2/10/2017来源:ASP.NET技巧人气:433

java日志系统-- slf4j + log4j

  在学校学习过程中,或者自己刚开始编程时,我经常通过System.out来输出各种结果。由于程序的规模很小,再者不是线上的应用,所以这种方法简单、快捷有效。 在学习Hadoop过程中,第一次接触log4j,但是当时并没有在意这些细节。   工作后,遇到问题或者调试时会通过日志来看程序的运行状态,尤其是把error stack输出后,能够很快的定位问题,对于解决问题起到很大的作用。   这篇文章,通过“为什么使用 SLF4J 而不是 Log4J 来做 Java 日志 ”这样的问题来带入,再通过slf4+log4j来实践日志配置。其他的日志记录库,如java.util.logging、logback等,待有时间在进行学习和总结。

1. slf4j-api、slf4j-log4j12以及log4j之间的关系

    slf4j : Simple Logging Facade for Java,为java提供的简单日志门面,更底层一点说就是接口。允许用户以自己的洗好,在工程中通过slf4j接入不同的日志系统。更直观一点,slf4j是个数据线,一端嵌入程序,另一端连接日志系统,从而实现将程序中的信息导入到日志系统并记录。          由此可见,slf4j就是众多接口的集合,不负责具体的日志实现,只在编译时负责寻找合适的日志系统进行绑定。具体的接口全部都在slf4j-api中定义。     下图比较清晰地描述他们之间的关系。

 (1) 应用层使用slf4j-api作为日志接入的接口;     (2) 编译时,slf4j-api中public final class LoggerFactory类中 PRivate final static void bind()方法会寻找具体的日志实现类绑定,主要通过 StaticLoggerBinder.getSingleton() 语句调用。     (3) slf4j-log4j12 是连接slf4j-api 和log4j的中间适配器,它实现了slf4j-api中StaticLoggerBinder接口,从而使得在编译时绑定的是slf4j-log4j12的getSingleton()方法。     (4) log4j是具体的日志系统,通过slf4j-log4j12初始化log4j,达到最终日志输出。

2. 为什么使用 SLF4J 而不是 Log4J 来做 Java 日志

(1) 最为重要的原因

    由第一个问题,我们可以知道slf4j就是各种接口的集合,对外暴露相同的接口,用户可以使用自己指定的日志系统。一句话总结,slf4j让你的代码独立于任何特定的日志系统API。     这样的特性,尤其适合于公共的库的开发。例如,你的工程使用log4j,需要包含一个common-utils的库,而这个库还依赖logback日志记录库,那么你的工程还需要包含他们。然而,如果common-utils库使用slf4j,那么你可以继续使用你的日志记录库,而不需要痛哭地添加和维护另一个新的日志记录框架。

(2) 占位符 {}

    占位符功能与String.format()方法中的%s非常近似,因为他在运行时才提取所提供的真正的字符串,而且这个占位符一般不需要指定参数的类型(相当方便,不过如果需要输出指定格式,还是需要自己拼接)。减少了字符串的拼接,进而减少String对象所需要的资源。

(3) isXXXEnabled()

    在使用log4j时,由于java没有那么方便的文档级别的预编译方法,如下的代码是不是很无聊?

if (logger.isDebugEnabled()) {
    logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
}

    而使用slf4j,可以更简洁的达到同样的效果,提高代码的可读性。

logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);

下面的SLF4J日志方法的代码,来自于slf4j-log4j12-1.6.1.jar包里的Log4j的适配器类Log4jLoggerAdapter.

public void debug(String format, Object arg1, Object arg2) { if (logger.isDebugEnabled()) {
      FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
      logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());
    }
}

3. slf4j + log4j来做日志

(1) 引用

    通过上面的介绍,除了引用slf4j之外,还需要作为适配器的slf4j-log4j12和具体实现的log4j的jar包。具体版本需要看使用的log4j版本和slf4j版本。     如果通过Maven来管理项目依赖,那就简单多了,只需要在pom文件中引用slf4j-log4j12的后,编译时会自动引入它所依赖的其他jar包。
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.6.1</version>
    </dependency> 

(2) log4j的配置

 log4j可以通过程序代码配置,也可以通过配置文件配置,显然配置文件的方式更加灵活。    log4j支持两种配置文件格式,一种是xml格式的文件,一种是java properties(key=value)。配置文件需要放置在classpath路径下,而且XML文件的优先级更高。     key/value形式的更有助于说明,这里使用properties文件作为例子介绍log4j的配置。     a. 配置根Logger:          
log4j.rootLogger = [ level ] , appenderName, appenderName, …        其中level是日志记录的优先级,appenderName是定义的Appenders,可以指定多个输出目的地。

    b. 配置Appender:
log4j.appender.appenderName = fully.qualified.name.of.appender.class
log4j.appender.appenderName.option1 = value1
…
log4j.appender.appenderName.option = valueN

        Appender常用属性:
        Threshold=WARN        - 指定日志级别
        ImmediateFlush=true   - 默认true,立即输出
        Target=System.err    -  默认情况下是:System.out,指定输出控制台
        File=mylog.txt              - 指定消息输出到mylog.txt文件
        Append=false              - 默认true,指定是否追加
        MaxFileSize=100KB    - 后缀可以是KB, MB 或者是 GB,指定日志文件的大小,到达时,将会自动滚动,原来的内容放置到mylog.log.1文件中
        MaxBackupIndex=2       - 指定滚动文件的最大值
        DatePattern='.yyyy-ww'  - 每周滚动一次。
            1)'.'yyyy-MM: 每月
            2)'.'yyyy-ww: 每周 
            3)'.'yyyy-MM-dd: 每天
            4)'.'yyyy-MM-dd-a: 每天两次
            5)'.'yyyy-MM-dd-HH: 每小时
            6)'.'yyyy-MM-dd-HH-mm: 每分钟
        
    c. 配置Layout:    Layout是和Appender绑定的,不同的Appender可以有不同的输出格式
     
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
log4j.appender.appenderName.layout.option1 = value1
…
log4j.appender.appenderName.layout.option = valueN
    当使用org.apache.log4j.PatternLayout来自定义信息格式时,可以使用 log4j.appender.A1.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p -%m%n 来格式化信息 
    %c 输出所属类的全名,可写为 %c{Num} ,Num类名输出的范围 如:"com.sun.aaa.classB", %C{2}将使日志输出输出范围为:aaa.classB 
    %d 输出日志时间其格式为 可指定格式 如 %d{HH:mm:ss}等 
    %l 输出日志事件发生位置,包括类目名、发生线程,在代码中的行数 
    %n 换行符 
    %m 输出代码指定信息,如info(“message”),输出message 
    %p 输出日志的优先级,即 FATAL ,ERROR 等 
    %r 输出从启动到显示该条日志信息所耗费的时间(毫秒数) 
    %t 输出产生该日志事件的线程名


[%-5p %d{yyyy-MM-dd HH:mm:ss.SSS}] [%t] %l [%m]%n是我们使用的日志格式。



    对于xml的配置,通过引用和参考的形式给出,
    http://www.cnblogs.com/kevin-yuan/archive/2012/11/23/2784610.html
    https://www.mkyong.com/logging/log4j-xml-example/





引用和参考
http://woshixy.blog.51cto.com/5637578/1371420
https://www.oschina.net/translate/why-use-sl4j-over-log4j-for-logging
http://blog.csdn.net/yycdaizi/article/details/8276265

//配置
https://my.oschina.net/exit/blog/182445
https://www.mkyong.com/logging/log4j-xml-example/
http://www.cnblogs.com/ITEagle/archive/2010/04/23/1718365.html