正常情况下,三方库里涉及到如日志输出,该直接使用日志框架,如sef4j或jcl,不该于具体的实现关联,例如jul,log4j,logback.这种情况下,当应该包含此三方库时,由运行时决定,日志通过什么实现做输出。
比如jcl的策略是:
1.首先查找org.apache.commons.logging.Log属性,该属性可以在java代码中设置,更常见的是在classpath下的common-logging.properties文件中进行设置
2.在系统属性中查找org.apache.commons.logging.Log
3.如果类路径中有log4j的jar包,则采用log4j实现
4.如果jdk版本为1.4,采用Jdk14Logger
5.采用common-logging提供的SimpleLog
我当前遇到的case是这样的,应用使用的jcl + log4j,要接管的三方库使用的jul,如何实现对三方库的日志接管?
首先上一张这些日志之前的关系图
如图所示,
• 由于JCL-over-SLF4J和原来的JCL具有完全相同的API,因此两者是不能共存的。
• Logback和slf4j-log4j12也不能并存,否则SLF4J会迷惑并产生不确定的结果。
可以看到slf4j对jcl和jul均有不错的支持,所以我这里做的事情:
1.在应用中移除commons-log的依赖,引入JCL-over-slf4j,这样应该中原使用jcl的代码就不用修改了。
2.加入对三方库的jul的处理.
由于java.util包是不能被替换的,jul-to-slf4j.jar使用的方法是将JUL的root logger的handler替换成SLF4JBridgeHandler,这个handler用SLF4J的logger输出log。
首先我要将JUL的所有Log level打开。假如你沒这么做,JUL的Level為Info,你只有在Info Level以上的內容才传向SLF4J;
我们定义一个配置文件,jul.perperties
.level= ALL
handlers= java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level= ALL
接下来,加载配置:
private void readLog4jConfigFromClasspath(){
InputStream is = this.getClass().getResourceAsStream("/jul.properties");
try {
LogManager.getLogManager().readConfiguration(is);
} catch(Exception e) {
throw new RuntimeException(e);
} finally {
IOUtils.close(is);
}
}
然后就是通过Slf4j接管jul的handler了
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
接下来就OK了!
ref:https://www.tonylin.idv.tw/dokuwiki/doku.php/java:log4j:bridge
http://boundary.cc/2013/03/java-logging-system/
http://stackoverflow.com/questions/9117030/jul-to-slf4j-bridge
http://guyongpeng.iteye.com/blog/133422