三种日志框架与SLF4J的适配
之前已经介绍过了常用的JUL、Log4j、Logback等常用的日志框架,现将它们与SLF4J作一个整合。
先放一张SLF4J官网的图片,后面我们一点一点的来看。
Logback+SLF4J
对于Logback+SLF4J的使用,我们只需要引入logback-classic这个pom就行了,因为这个pom会自动帮我们引入logback-core、slf4j-api等pom。这种方式配置还是很简单的,Logback本身就支持SLF4J,所以新项目还是建议使用这个组合。
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
Log4j+SLF4J
使用这个组合时,slf4j-log4j12包已经有Log4j的依赖,因此不需要再单独引入log4j的api
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.8.0-alpha2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
<scope>compile</scope>
</dependency>
从这个图我们能够看出,Log4j想要适配SLF4J还需要一个适配器,因为前者出现的更早,那时候没有SLF4J,所以自身也就没有兼容SLF4J,需要一个适配器来进行适配,这个适配器就是slf4j-log4j12。接下来我们通过源码来看一下究竟是slf4j-log4j12是怎样将Log4j与SLF4J适配到一起的:
// 应用层日志记录还是只用到了SLF4J包,并不能看出具体是哪一种日志实现。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Logger logger = LoggerFactory.getLogger(Test.class.getName());
// Logger只是一个顶层接口,我们知道现在用的是Log4j的日志实现,所以这个Logger应该是一个Log4j实现类的对象,那么我们点进LoggerFactory.getLogger这个方法
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
// Logger对象是根据ILoggerFactory得到的,ILoggerFactory又是一个接口,通过getILoggerFactory()方法来返回的
public static ILoggerFactory getILoggerFactory() {
if (INITIALIZATION_STATE == UNINITIALIZED) {
synchronized (LoggerFactory.class) {
if (INITIALIZATION_STATE == UNINITIALIZED) {
INITIALIZATION_STATE = ONGOING_INITIALIZATION;
performInitialization();
}
}
}
switch (INITIALIZATION_STATE) {
case SUCCESSFUL_INITIALIZATION:
return StaticLoggerBinder.getSingleton().getLoggerFactory();
case NOP_FALLBACK_INITIALIZATION:
return NOP_FALLBACK_FACTORY;
case FAILED_INITIALIZATION:
throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
case ONGOING_INITIALIZATION:
// support re-entrant behavior.
// See also http://jira.qos.ch/browse/SLF4J-97
return SUBST_FACTORY;
}
throw new IllegalStateException("Unreachable code");
}
// 对于上述的方法,只需要关心StaticLoggerBinder.getSingleton().getLoggerFactory()这一行即可,
// 我搜了一下StaticLoggerBinder这个类,发现点到了slf4j-log4j12这个包中。
// StaticLoggerBinder类的getLoggerFactory()方法返回了一个Log4jLoggerFactory对象,我们具体看一下Log4jLoggerFactory这个类
public Logger getLogger(String name) {
Logger slf4jLogger = (Logger)this.loggerMap.get(name);
if (slf4jLogger != null) {
return slf4jLogger;
} else {
// 仍然是用Log4j那一套的Logger和LogManager生成一个Logger对象
org.apache.log4j.Logger log4jLogger;
if (name.equalsIgnoreCase("ROOT")) {
log4jLogger = LogManager.getRootLogger();
} else {
log4jLogger = LogManager.getLogger(name);
}
// 这里将Log4j的Logger转为SLF4J的Logger,完成适配
Logger newInstance = new Log4jLoggerAdapter(log4jLogger);
Logger oldInstance = (Logger)this.loggerMap.putIfAbsent(name, newInstance);
return (Logger)(oldInstance == null ? newInstance : oldInstance);
}
}
JUL+SLF4J
pom文件如下:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.25</version>
</dependency>
我们看到slf4j-jdk14有依赖slf4j-api,因此我们无需再单独引入slf4j-api。slf4j-jdk14适配JUL与SLF4J的原理和上面介绍slf4j-log4j12一样。同样是由slf4j-jdk14提供StaticLoggerBinder的实现,将JUL的Logger,封装成SLF4J的Logger类型,返回到应用层供程序调用。