需求
从这段描述可以看出,SLF4J是一个“日志门面”(Logging Facade)它允许用户自由的切换所需的日志框架。
其实SLF4J是典型的面向接口的编程,只是一个日志标准,只提供了一套记录日志的api,并没有日志系统的具体实现,而是通过对接如log4j、java.util.logging等日志框架,来实现日志的输出,各个应用程序在使用日志的的地方全部使用slf4j-api 提供的接口进行编程,系统中具体使用哪个日志框架觉得于引入哪个具体的实现。
设计
上图中所有的和日志框架的集成都是由SLF4J团队开发的,但是和log4j集成的是log4j1,如果使用log4j2需要使用 log4j-slf4j-impl ,此包为log4j团队实现,
实现
SLF4J 1.8之后实现方式,1.8之后采用Java SPI机制来加载不同的日志实现,由于采用了SPI机制,所以SLF4J要求JDK版本要在1.6以上
- 在使用SLF4J开发时我们使用以下代码,可以看出主要涉及两个类:org.slf4j.Logger 和 org.slf4j.LoggerFactory ,通过源码可以看出Logger是一个接口,定义来标准的日志操作,由 LoggerFactory 来具体确定Logger实现,所以此处主要分析 LoggerFactory代码。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
}
- getLogger 方法主要有两个步骤
- 获取 ILoggerFactory 的具体实现
- 通过 ILoggerFactory 的 getLogger 方法获取真实绑定的日志框架,查看 ILoggerFactory 源码发现它是一个接口并且只有 getLogger 一个方法,所以可以判断在正式的日志框架集成的包中需要实现此接口,并返回适配好的 Logger 的实现,接下来分析 getILoggerFactory() 方法,分析SLF4J具体是怎么查找并绑定到具体的日志框架的
public static Logger getLogger(String name) {
// 获取真实的 ILoggerFactory 的具体实现
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
- 可以看到最终是调用方法 performInitialization(); 执行初始化,而 performInitialization() 方法主要是调用 bind() 方法执行最终的绑定的,如果绑定成功后直接调用 getSingleton() 获取单例,然后调用 getLoggerFactory(); 方法获取具体的实现工厂
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:
// 绑定成功后直接调用 getSingleton() 获取单例,然后调用 getLoggerFactory(); 方法获取具体的实现工厂
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");
}
// 执行初始化
private final static void performInitialization() {
bind();
if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
// 绑定成功后,执行版本检查,检查SLF4J版本和具体实现的版本是否一致
versionSanityCheck();
}
}
- 通过源码可以看出,1.8之后定义了接口 org.slf4j.spi.SLF4JServiceProvider 用于实现SPI的绑定
private final static void bind() {
try {
// 使用JavaSPI机制加载具体实现
List<SLF4JServiceProvider> providersList = findServiceProviders();
// 如果发现多个绑定 报告发现多个日志实现
reportMultipleBindingAmbiguity(providersList);
// 判断具体实现
if (providersList != null && !providersList.isEmpty()) {
// 获取第一个绑定
PROVIDER = providersList.get(0);
// SLF4JServiceProvider.initialize() is intended to be called here and nowhere else.
// 初始化
PROVIDER.initialize();
// 变更状态
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
// 报告具体绑定的日志实现
reportActualBinding(providersList);
} else {
// 如果没有具体实现 修改状态为没有找到具体实现
INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
Util.report("No SLF4J providers were found.");
Util.report("Defaulting to no-operation (NOP) logger implementation");
Util.report("See " + NO_PROVIDERS_URL + " for further details.");
// 调用1.7 之前的方法,但是不会进行绑定,只是输出提示
Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
reportIgnoredStaticLoggerBinders(staticLoggerBinderPathSet);
}
postBindCleanUp();
} catch (Exception e) {
failedBinding(e);
throw new IllegalStateException("Unexpected initialization failure", e);
}
}
// 标准Java SPI查找实现
private static List<SLF4JServiceProvider> findServiceProviders() {
ServiceLoader<SLF4JServiceProvider> serviceLoader = ServiceLoader.load(SLF4JServiceProvider.class);
List<SLF4JServiceProvider> providerList = new ArrayList<SLF4JServiceProvider>();
for (SLF4JServiceProvider provider : serviceLoader) {
providerList.add(provider);
}
return providerList;
}
- 以下为log4j1 集成源码:
org.slf4j.spi.SLF4JServiceProvider
org.slf4j.log4j12.Log4j12ServiceProvider