java日志:commons-loging,log4j,slf4j,LogBack的区别

Commons-logging只是一个API,定义了接口,不负责具体的实现,而具体的实现有以下几个:log4j,slf4j,logback

先简单说下Commons-logging的查找日志实现的顺序:

1.   先找org.apache.commons.logging.LogFactory 属性配置

2.  利用service 发现机制,扫描classpah 下的META-INF/services/org.apache.commons.logging.LogFactory 文件,若找到则使用里面的配置。

3.   否则,从Classpath 里寻找commons-logging.properties ,找到则根据里面的配置加载。

4.   否则,使用默认的配置:如果能找到Log4j 则默认使用log4j 实现,如果没有则使用JDK14Logger 实现,再没有则使用commons-logging 内部提供的SimpleLog 实现。

从上述加载流程来看,如果没有做任何配置,只要引入了log4j 并在classpath 配置了log4j.xml ,则commons-logging 就会使log4j ,而代码里不需要依赖任何log4j 的代码。

所以可以看出,使用Log4J主要是log4j.xml配置文件的编写

Log4J配置文件的基本格式如下:


 

其中 [level] 是日志输出级别,共有5级:


 

Appender 为日志输出目的地,Log4j提供的appender有以下几种:


 

Layout:日志输出格式,Log4j提供的layout有以下几种:


 

打印参数: Log4J采用类似C语言中的printf函数的打印格式格式化日志信息,如下:


Slf4j : 全称为Simple Logging Facade for JAVA, 是对不同日志框架提供的一个Facade封装,可以在部署的时候不修改任何配置即可接入一种日志实现方案。

slf4j的代理架构图

slf4j的代理架构图

slf4j的jar 包里存放了配置META-INF/services/org.apache.commons.logging.LogFactory =org.apache.commons.logging.impl.SLF4JLogFactory,而commons-logging 在初始化的时候会找到这个serviceId ,并把它作为LogFactory 。

完成桥接后,那么SLF4J 内部又是如何来装载合适的log 呢?

原理是SLF4J 会在编译时绑定import org.slf4j.impl.StaticLoggerBinder; 该类里面实现对具体日志方案的绑定接入。任何一种基于slf4j 的实现都要有一个这个类。如:

org.slf4j.slf4j-log4j12-1.5.6: 提供对 log4j 的一种适配实现。

Org.slf4j.slf4j-simple-1.5.6: 是一种 simple 实现,会将 log 直接打到控制台。

……

那么这个地方就要注意了:如果有任意两个实现slf4j 的包同时出现,那就有可能酿就悲剧,你可能会发现日志不见了、或都打到控制台了。原因是这两个jar 包里都有各自的org.slf4j.impl.StaticLoggerBinder ,编译时候绑定的是哪个是不确定的。这个地方要特别注意!!

Logback是由log4j创始人设计的又一个开源日志组件,性能进行了优化,log4j和logback的对比:

结果分析:

从测试结果可见:

如果不开启AsyncAppender,Log4j和Logback的性能不相上下;如查开启了AsyncAppender,Logback的性能将有非常大的提升。

关闭immediateFlush,在开启AsyncAppender的情况下,对Logback的影响很小,但对Log4J的影响却很大;但在不开启的AsyncAppender的情况下,对二都影响都很大。

原因是Logback对AsyncAppender做了很多优化,主要对并发处理方式的不同。二者都采用了生产者、消费者模型。

Log4j的实现原理如下:

Logging Event进入AsyncAppender,AsyncAppender会调用append方法,在append方法中会去把logging Event填入Buffer中,当消费能力不如生产能力时,AsyncAppender会把超出Buffer容量的Logging Event放到DiscardSummary中,作为消费速度一旦跟不上生成速度,中转buffer的溢出处理的一种方案。

AsyncAppender有个线程类Dispatcher,它是一个简单的线程类,实现了Runnable接口。它是AsyncAppender的后台线程。

Dispatcher所要做的工作是:

(1)锁定Buffer,让其他要对Buffer进行操作的线程阻塞。
(2)看Buffer的容量是否满了,如果满了就将Buffer中的Logging Event全部取出,并清空Buffer和DiscardSummary;如果没满则等待Buffer填满Logging Event,然后notify Disaptcher线程。

(3)将取出的所有Logging Event交给对应appender进行后面的日志信息推送。

于是Log4J用ArrayList实现了一个buffer队列(@see org.apache.log4j.AsyncAppender#append)来用户缓冲,并用一堆的synchronized来保证线程安全,对于AppenderAttachable则采用Vector来保证线程安全。

Logback的整体原理与Log4J基本相似,但直接使用了并发包的ArrayBlockingQueue来实现Buffer缓冲,对于AppenderAttachable并没采用加锁的机制来保证线程安全,而是通过读写分离来实现了线程安全,具体用的是CopyOnWriteArrayList。

综于以上原因,不难理解为什么异步下Logback比Log4J性能高出如此多。

结论:

考虑到Logback在多线程情况下的性能优秀表现,加上接口使用的方便性,强烈建议新应用接入Logback。老的应用如果日志没有成为系统的性能瓶颈,考虑到迁移成本较高,可以保留现状。

发表评论

电子邮件地址不会被公开。 必填项已用*标注


8 × 二 =

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">