看了一本书,叫做《java工程师修炼之道》,第107页讲了各种日志框架及其配置。
平时我们开发,都是在控制台输出日志。可以使用日志框架进行日志输出,也可以使用System.out.println(xxx)进行日志输出。这里非常不推荐使用System.out.println(xxx),一是不容易控制,当只需要更高级别的日志输出的时候,就只有注释代码了。并且输出没有指定的格式和提供更多信息,只有开发本人知道在哪里输出的日志,干什么用的。还有一点就是System.out 这句话,其实会有上下文切换,由用户态切换到内核态,频繁的切换开销太大。所以, 啰嗦了这么多,日志框架是一定要用的。它不仅可以输出到控制台,也可以输出到文件,文件可以按指定方式生成,比如每天一个,超过某个大小生成等等。指定不同的日志级别输出到不同的文件,或者某个包的日志单独输出。个别日志框架为了提高性能,还会有日志缓冲,日志异步输出等方法。
下面呢,就是原书中的摘抄,或者我自己的理解了,有兴趣可以看原书,这比二手知识更加准确和成体系。
目前java提供的日志框架有:
JDK Logging
Apache Log4j
Logback
ApacheLog4j2
此外,还有两个用于实现日志统一的框架Apache Commons Logging、SLFJ。与上述框架不同,这两个框架只是一个门面,并没有日志框架的具体实现,可以认为是日志接口框架。
1 JDK Logging是JDK自带的日志框架,性能不高,所以开发用得少,略。
2 Log4j是使用的很广泛的日志框架,支持xml,properties配置。
它的properties的配置,例如:
log4j.rootLogger=INFO,appenderA
按照我的理解,着句话是说日志框架的根配置,默认的日志级别是INFO,也就是只有INFO日志级别及其以上的才会被输出,逗号后面跟着的是appender,这个我们在后续需要对它进行配置,也就是输出的目的地,格式等等信息。可以跟多个appender,逗号分隔。这样就是一份日志输出到不同的地方。最后我们可以指定其余的logger,它是类似于继承自rootLogger,除了自身的日志进行输出,rootLogger的日志也会输出到子logger的appender中。当然我们也可以不让它输出。
上面的appenderA的定义可以是下面这样的:
log4j.appender.appenderA = org.apache.log4j.ConsoleAppender
log4j.appender.appenderA.Target = System.out
log4j.appender.appenderA.layout = org.apache.log4j.PatternLayout
log4j.appender.appenderA.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{ 1 }:%L - %m%n
这个appenderA是输出到控制台的。那么INFO及其以上的日志信息就会输出到控制台。
我们再来配置一个子logger,比如:
log4j.logger.order=DEBUG,DRF
log4j.appender.DRF = org.apache.log4j.DailyRollingFileAppender
log4j.appender.DRF.layout=org.apache.log4j.PatternLayout
log4j.appender.DRF.layout.ConversionPattern=%p[%t]%d{yyy-MM-dd HH:mm:ss,SSS} %m%n
log4j.appender.DRF.File =./logs/log.log
log4j.appender.DRF.DatePattern = '.'yyyy-ww'.log'
log4j.appender.DRF.Append = true
log4j.appender.DRF.Encoding = UTF-8
这个是子logger的配置,它输出DEBUG及其以上的信息,输出到文件,这个文件每天生成一个。
这个子logger会输出哪些内容到文件呢:自身的DEBUG及其以上的信息会输出,然后rootLogger的INFO及其以上的信息会输出。也就是INFO及其以上的日志信息会重复输出,这不仅冗余,而且也对分析日志造成了困扰,我们不想要rootLogger的信息输入,怎么办呢,在子logger的配置中加入:
log4j.additivity.order=false
使用:
import org.apache.log4j.Logger;
public class TestA {
private static final Logger LOG = Logger.getLogger(this.getClass());
public void methodA(){
if(LOG.isDebugEnabled()){// 当不启用debug级别的时候,就不走日志打印的逻辑,减少不必要的运算。
LOG.debug("xxx");
}
}
}
为了应对某一个时间内,大量日志信息进入appender中,可以配置缓冲,让日志在appender中累积,等足够量了,再一次性写入,减少I/O操作:
log4j.appender.DRF.BufferedIO=true
#Buffer单位为字节,默认是8K,IO BLOCK大小默认也是8K
log4j.appender.DRF.BufferSize=8192
除了缓冲,还可以使用AsyncAppender来做异步日志:
<appender name="ASYNC" class="org.apache.log4j.AsyncAppender">
<param name="BufferSize" value="512"/>
<appender-ref ref="DRF"/>
</appender>
3 Logback
是Log4j的创始人设计的又一个开源日志组件,改进了Log4j。
同样提供异步缓冲等功能。日志可以使用占位符。
4 Log4j2
2015年8月,官方正式宣布Log4j 1.x系列声明终结,推荐用户升级到Log4j2,并号称在修正了Logback固有的架构问题的同时,改进了Logback的功能。Log4j2 不兼容Log4j,Log4j2 不仅提供了日志的实现,也提供了门面,目的是为了统一日志框架。
Log4j2支持xml,json,yaml,不再支持properties。
配置方法可以到网上随便一找都有,跟Log4j配置思路大同小异。同样也有缓冲和异步日志。
相比之前需要先判断日志级别再输出,Lo4j2提供了占位符功能,所以可以直接写:
LOG.debug("错误:{}",msg);
上面提到的4个框架是实际的日至框架,每种都有不同的写法,对于开发者而言,跟代码绑定死了,实际上限制了日志框架的自由切换。如果日志框架按照统一的日志接口编程那么,我们就可以做到无缝切换日志框架了,这就是Apache Commons Logging 与SLFJ的初衷。
Apache Commons Logging简称为JCL,是Apache开源的日志门面框架,Sppring中使用的日志框架就是JCL。使用如下:
import org.apache.commons.LogFactory;
private static final Log LOG = LogFactory.get(this.getClass());
LOG.info("xxx");
maven引入JCL的依赖:commons-logging
JCL检测到类路径下的日志实现框架,然后使用该日志框架作为实现。在maven中最好排除其余的日志包,以免造成日志框架的循环依赖。