引言
前不久log4j2因为一个漏洞闹的沸沸扬扬,一天N个应用要升级版本发布,有些系统不动它还好,一动它就是各种问题。反正日志就是不打印……最后费了好久一顿乱操作(各种尝试)最后终于可以打印日志了(其实也不知道是为啥)!暗自下决心得把这个搞懂。不知大家是否有过这样的经历
日志体系
这是目前java应用的常用日志体系结构也不是太全。
只聊一下slf4j和我们平时常用的一些日志框架 log4j1.x、log4j2.x、logback
slf4j-api:简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案。是为各种日志提供一个简单统一的接口,从而让用户在项目中引入各自的日志实现框架
这是官网上的一个设计图
源码分析
我们通常在引用日志的时候是使用了这个方法
Logger logger = LoggerFactory.getLogger(LogTest.class);
下面我们走进去看一下吧,还是比较简单的,首先slf4j-api包中并没有日志绑定类【org.slf4j.impl.StaticLoggerBinder】其实是在打包时候做排掉了。由其它各框架自己实现绑定类返回其工厂的实现
进入方法查看
//获取日志对象
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
//获取日志工厂
public static ILoggerFactory getILoggerFactory() {
if (INITIALIZATION_STATE == 0) {
Class var0 = LoggerFactory.class;
synchronized(LoggerFactory.class) {
if (INITIALIZATION_STATE == 0) {
INITIALIZATION_STATE = 1;
//初始化日志工厂,这里走进入会有日志的绑定操作
performInitialization();
}
}
}
switch(INITIALIZATION_STATE) {
case 1:
return SUBST_FACTORY;
case 2:
throw new IllegalStateException("org.slf4j.LoggerFactory in failed state. Original exception was thrown EARLIER. See also http://www.slf4j.org/codes.html#unsuccessfulInit");
case 3:
//已经绑定成功返回日志的工厂
return StaticLoggerBinder.getSingleton().getLoggerFactory();
case 4:
return NOP_FALLBACK_FACTORY;
default:
throw new IllegalStateException("Unreachable code");
}
}
org.slf4j.LoggerFactory#bind
//日志框架的绑定
private static final void bind() {
Set<URL> staticLoggerBinderPathSet = null;
if (!isAndroid()) {
//这里是去找org.slf4j.impl.StaticLoggerBinder的包
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
//下面是打印日志,发现的日志框架
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
}
//这里是拿到具体的实现框架,由于可能是加载多个,但是由于JVM的双亲委托加载机制
//这里只会加载一个,具体是哪种这个需要看jvm加载的顺序了
StaticLoggerBinder.getSingleton();
INITIALIZATION_STATE = 3;
//这里会打印出实际使用了哪个日志
reportActualBinding(staticLoggerBinderPathSet);
fixSubstituteLoggers();
replayEvents();
SUBST_FACTORY.clear();
}
这里在我的项目中引入了上面列出的3个日志框架
项目在启动时则会打印出
SLF4J: Class path contains multiple SLF4J bindings.
//log4j2
SLF4J: Found binding in [jar:file:/D:/Maven_Repositry/org/apache/logging/log4j/log4j-slf4j-impl/2.14.1/log4j-slf4j-impl-2.14.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
//logback
SLF4J: Found binding in [jar:file:/D:/Maven_Repositry/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
//log4j1
SLF4J: Found binding in [jar:file:/D:/Maven_Repositry/org/slf4j/slf4j-log4j12/1.6.1/slf4j-log4j12-1.6.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
//实际绑定的是log4j2
SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]
一句话说就是通过类StaticLoggerBinder的加载来确定到底使用哪个日志框架。如果项目中没有配置【log4j2.xml】的日志配置而配置的是【logback】的配置则会导致项目的日志出现不能打印的情况。
下面我们列一下每种框架所需的jar吧
log4j1.x:一般情况需要,slf4j-api、slf4j-log4j12(桥接包)、log4j
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
log4j2.x:一般情况需要,slf4j-api、log4j-slf4j-impl(桥接包)、log4j-core、log4j-api(版本要在2.15.0以上因为之前有bug)
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.14.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.15.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.15.0</version>
</dependency>
logback:只需要引入 logback-classic 就好了
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
现在弄清楚这个关系在来查问题就很容易定位了。