今天有时间看下TMBlog系统之前遗留的日志问题,就是采用了logback后之前的log4j等日志就无法输出了.
TMBlog 系统使用到了logback遇到了一个问题,明明已经使用了logback.xml但log4j还是提示警告
log4j:WARN No appenders could be found for logger (org.apache.commons.httpclient.HttpClient).
log4j:WARN Please initialize the log4j system properly.
我完美的想法是logback作为最牛逼的日志组件,应该把之前的log4j等实现接管过来吧,怎么没有呢?
我debug了一下,首先从出现警告的HttpClient开始,使用的是apache-commons-logging。其实只要搞懂这样代码做了什么事情,对日志系统就理解了。
private static final Log LOG = LogFactory.getLog(HttpClient.class);
首先这行代码在commons-logging内部实际执行的是:
getFactory().getInstance(clazz)
问题1,commong-logging如何查找LogFactory?
我就不翻译了,请看官方代码注释,4种情况写得很清楚明白:
在我的情况下会执行到最后一种情况,也就是使用org.apache.commons.logging.impl.LogFactoryImpl。
问题2,logFactory如何创建logger?
LogFactoryImpl的调用栈如下:
LogFactoryImpl.getInstance(Class clazz)
LogFactoryImpl.getInstance(String name)
LogFactoryImpl.newInstance(String name)
LogFactoryImpl.discoverLogImplementation(String logCategory)
最后实现的时候按照一下顺序尝试创建Class,如果成功则返回:
org.apache.commons.logging.impl.Log4JLogger
org.apache.commons.logging.impl.Jdk14Logger
org.apache.commons.logging.impl.Jdk13LumberjackLogger
org.apache.commons.logging.impl.SimpleLog
Log4JLogger是一个代理类,其构造函数如下:
public Log4JLogger(String name) {
this.name = name;
this.logger = getLogger();
}
public Logger getLogger() {
if (logger == null) {
logger = Logger.getLogger(name);
}
return (this.logger);
}
可以看到实际使用的org.apache.log4j.Logger.getLogger(name)来创建logger。由于我们的环境有log4j包,所以在我们的环境中第一个Log4j的Logger会创建成功。log4j logger创建之后会自动检测,如果没有发现log4j.properties等配置信息就会提示警告。
所以得出一个结论,出现问题是**++因为apache-common-logging不会自动发现logback++**。
问题3,logback和log4j如何桥接
既然apache-common-logging不能发现logback,那log4j和logback如何桥接呢?slf4j官方当然考虑到这个问题,参考官方文档:http://logback.qos.ch/bridge.html
使用log4j-over-slf4j,相当于ACL-->log4j-->slf4j-->logback打通。中间的log4j到slf4j,通过log4j-over-slf4j进行桥接。那么log4j-over-slf4j实际上如何桥接的呢?答案是直接在slf4j中定义同名的log4j的Logger包。这个问题参考:log4j-over-slf4的log4j Loger加载问题
问题4,slf4j存在的价值?
一个很实际的问题是为啥要搞掉JCL? 引入slf4j的成本还是很高的,这篇文章有回答:
http://articles.qos.ch/classloader.html
http://articles.qos.ch/thinkAgain.html
总结,混乱的各种日志组合情况
slf4j-log4j-
slf4j-jcl-
jcl-over-slf4j-
log4j-over-slf4j-
各种冲突情况总结:
slf4j-log4j,slf4j-simple不能同时出现,两个jar包都有sl4j的StaticLoggerBindder会冲突。
log4j-over-slf4j和log4j不能同时出现,两个jar包都有org.apache.log4j.Logger会冲突。
同理,jcl-over-slf4j和common-loggng不能同时出现。
slf4j-jcl和jcl-over-slf4j不能同时出现,逻辑上进入无限递归。
综上所总结情况,jar包的依赖配置如下组合
想用 log4j 和 logback 共存
- jcl-over-slf4j-xx.jar
- commons-logging (spring 和 struts2 框架要用到)
- logback-classic-x.xjar
- logback-core-xx.jar
- slf4j-api-xx.jar
- log4j-over-slf4j-xx.jar
如全新项目,则推荐只用 logback, 只需以下 jar :
- logback-classic-1.0.0.jar
- logback-core-1.0.0.jar
- slf4j-api-1.6.4.jar
- jcl-over-slf4j-1.6.4.jar
- commons-logging ( spring 和 struts2 框架要用到)
如果用 slf4j + log4j 则以下 jar:
- slf4j-api-1.6.4.jar
- slf4j-logj12.jar ( slf4j 与 log4j 之间的绑定)
- log4j.jar
[========]
附一个log4j.properties 文件可以用 PropertiesTranslator 转换成 logback.xml 文件内容。