Java的log系统比较繁杂。在这里梳理一下。本文只涉及log系统介绍和处理log系统之间的切换。不涉及如何配置和使用。
具体的log系统
Log4j:准确的说是log4j 1.x版。是之前使用最广泛的log系统。
Logback:Log4j的作者另立炉灶写的新版log,比起log4j性能更好。具体的对比可以参考http://www.oschina.net/translate/reasons-to-prefer-logbak-over-log4j
JUL:Java Util Logging,是java 1.4以来自带的一个logging系统。相信用的人应该不多吧……
以上三个log系统是实打实的做事情的log系统,需要相应的配置来制定如何处理log等。
Log Facade系统
实际情况下,一个项目可能不想具体依赖于一种具体的log系统(比如log4j)。尤其是对于开源的中间件,类库等。如果一个开源的类库在代码里写了用log4j,那所有用到这个类库的代码都必须跟log4j扯上关系了(注意,没说必须要用log4j哦,后文会涉及y)。
更可能的是,应用依赖的类库有的用log4j,有的用logback,局面就会比较混乱。这会让应用无法方便的统一管理log系统。
应运而生的就是下面要介绍的这种log facade组件。或者说是一个bridge,一个包装,一个adapter……总之是做于log落地无关的事情的。它的作用就是将代码和log系统隔离,提供一个统一的接口,然后把log请求adapter到具体的log实现系统(上文中提到的)。
JCL:又叫做Common Logging,apache common logging,Jakarta Commons Logging。都是一个东西。
SLF4j:Simple Log Facade 4 Java。这个系统是log4j,logback的作者实现的。
SLF4j+logback
在具体的项目中,slf4j+logback是个不错的搭配。简单介绍一下这个组合。slf4j是个门面,在具体的代码中,它会尝试加载org.slf4j.impl.StaticLoggerBinder这个类。然后通过这个类的方法创建LogFactroy,以及各种log。所以StaticLoggerBinder就是连接slf4j和具体实现的桥梁。每种具体的实现,都要提供一个StaticLoggerBinder,来让slf4j找到具体的实现。
动态绑定/加载实现
logback和log4j都提供了StaticLoggerBinder(毕竟是一个人做的嘛……)。如果使用logback,就需要在cp里加入logback-classic(外围+扩展,StaticLoggerBinder就在这里面),logback-core(核心)。如果使用log4j,就需要slf4j-log4j12(版本即log4j的版本)。当然还少不了slf4j的jar包slf4j-api。
这样的话,应用程序中就只会引入slf4j的类。而具体实现则是slf4j负责打理的(加载Binder,Binder会负责触发log系统初始化等)。应用和具体的log系统就解绑了。类库使用了slf4j之后,就不必担心自己的选择会影响应用了。应用如果把log4j的相关jar包放在cp里,那就会是使用log4j。同样也可以使用logback。
如果cp里有多个Binder,就可能会看到如下的错误提示:
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/D:/Users/mzang/.m2/repository/org/slf4j/slf4j-log4j12/1.7.5/slf4j-log4j12-1.7.5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/D:/Users/mzang/.m2/repository/ch/qos/logback/logback-classic/1.0.13/logback-classic-1.0.13.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]
p.s. Common Logging和slf4j异曲同工,只是具体实现方式不同。
更麻烦的事——切换Log系统
如果上面的东西还好理解,那么下面的事情就有点搞了。
在实际情况下,可能我们会有这样的需求。一个组件使用了log4j/jul/common log。但是我们想统一到使用slf4j + logback。举例来说,如果我们的应用一直用slf4j,结果引入的一个新类库使用了common logging。比较好的处理方式是让slf4j接替 common logging。或者说,让请求都落到slf4j上。
jcl-over-slf4j这个包可以完成这个任务。具体的步骤是,把common logging的jar包从cp里删掉,然后把jcl-over-slf4j放入cp。这个jar包中的类和common logging中的类名,方法名等完全一样,只是在具体的方法中,把所有的请求都暗渡陈仓的转移到了slf4j上。
同样还有log4j-over-slf4j,可以解决在代码中写了使用log4j的情况。只要用这个jar包替代log4j的jar包就可以了(还有一些细节,比如,如果程序中显示的调用了log4j中的一些类,appender什么的,那就没办法了,如果是规规矩矩的使用log4j.properties,那就没啥大问题)。
对于jul就复杂一点,因为不能把java中自带的类删了。所以jul-to-slf4j的做法是用自己的Hander(JUL处理日志的接口)作为root,同时删除所有的其它logger。这样就相当于用个二传手把所有的log通过这个硬塞进来的Handler,委托给了slf4j,然后slf4j再寻找实现,bulabula就跟前面一样了。
slf4j官网上有一个关于这方面的系统阐释。主要就是这个图。我尝试加了一下中文注释:
原地址:http://www.slf4j.org/legacy.html
那一大段话:
这个图展示了出于方便和权宜之下,所有可能的重定向和绑定。重定向只有在必须的时候才做。比如说,把jul重定向到slf4j,但是系统中又根本没用到jul,这样的重定向是没意义的。
总结一下:
负责提供slf4j绑定的jar包:
slf4j-log4j12
slf4j-jdk14
logback-classic
他们需要和具体的、对应的、版本一致的实现同时出现在cp中。
负责重定向到slf4j的:
jcl-over-slf4j
log4j-over-slf4j
jul-to-slf4j
他们需要替换掉原来的log系统的jar包(jcl和log4j),或者需要初始化一下(jul-to-slf4j)
http://deepnighttwo.iteye.com/blog/2039553
http://blog.csdn.net/longyulu/article/details/38685503