介绍
SLF4J(Simple Logging Facade for Java)是一个简单门面日志;所谓门面日志,就是提供一个统一调用日志的接口,允许终端用户在部署的时候插入自己想要的日志框架。
SLF4J 接口的依赖如下
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
终端用户在部署的时候需要添加自己想要的日志框架
SLF4J 源码解读
SLF4J
-
org.slf4j.impl.**StaticLoggerBinder**
:绑定与发现 SLF4J 实现的类,每个 SLF4J 日志实现都需有一个 StaticLoggerBinder -
org.slf4j.**ILoggerFactory**
:获取日志类的日志工厂接口。 -
org.slf4j.**Logger**
: 调用日志的接口。
SLF4J 通过 LoggerFactory.getLogger()
获取 Logger
类。
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
public static Logger getLogger(Class<?> clazz) {
Logger logger = getLogger(clazz.getName());
/*...*/
return logger;
}
其中先获取 ILoggerFactory
的具体实现,通过 ILoggerFactory
获取 Logger
类。
INITIALIZATION_STATE 表示初始化状态,共有以下状态:
- UNINITIALIZED:未初始化
- SUCCESSFUL_INITIALIZATION:初始化成功(找到 StaticLoggerBinder)
- NOP_FALLBACK_INITIALIZATION:未发现日志实现(找不到 StaticLoggerBinder 类)
-
FAILED_INITIALIZATION:初始化失败(找到了 StaticLoggerBinder 类,但是加载实例失败,无
getSingleton
方法或其他异常) - ONGOING_INITIALIZATION:默认实现
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:
// StaticLoggerBinder 成功加载,则使用 StaticLoggerBinder 实例
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");
}
其中,如果没有初始化(或者被重置),会调用 performInitialization() 初始化
private final static void performInitialization() {
bind();
if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
versionSanityCheck();
}
}
performInitialization() 调用的 bind() 方法是加载具体日志实现框架的核心:
private final static void bind() {
try {
Set<URL> staticLoggerBinderPathSet = null;
// skip check under android, see also
// <http://jira.qos.ch/browse/SLF4J-328>
if (!isAndroid()) {
// 查找环境中的所有 org/slf4j/impl/StaticLoggerBinder.class 类 URL
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
// 如果有多个 StaticLoggerBinder 实现类,则未日志冲突,则通过 reportMultipleBindingAmbiguity 输出警告
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
}
// the next line does the binding
// 尝试获取 StaticLoggerBinder, 如果类不存在,则通过 catch 捕获异常,并输出警告
StaticLoggerBinder.getSingleton();
// 存在 StaticLoggerBinder, 给初始化状态赋值为 成功初始化。
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
// 存在多个 StaticLoggerBinder 时,输出最终使用的 StaticLoggerBinder
reportActualBinding(staticLoggerBinderPathSet);
// 处理以及加载过的 logger
fixSubstituteLoggers();
replayEvents();
// release all resources in SUBST_FACTORY
SUBST_FACTORY.clear();
} catch (NoClassDefFoundError ncde) {
String msg = ncde.getMessage();
if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
Util.report("Failed to load class \\"org.slf4j.impl.StaticLoggerBinder\\".");
Util.report("Defaulting to no-operation (NOP) logger implementation");
Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details.");
} else {
failedBinding(ncde);
throw ncde;
}
} catch (java.lang.NoSuchMethodError nsme) {
String msg = nsme.getMessage();
if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
INITIALIZATION_STATE = FAILED_INITIALIZATION;
Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
Util.report("Your binding is version 1.5.5 or earlier.");
Util.report("Upgrade your binding to version 1.6.x.");
}
throw nsme;
} catch (Exception e) {
failedBinding(e);
throw new IllegalStateException("Unexpected initialization failure", e);
}
}
SLF4J 实现
-
slf4j-simple
:slf4j 的简单实现 -
slf4j-log4j12
:SLF4J 的 log4j 实现 -
log4j-slf4j-impl
:SLF4J 的 log4j2 实现 - logback
slf4j+log4j
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
- slf4j-api
- log4j
slf4j+log4j2
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
</dependency>
- slf4j-api
- log4j-api
- log4j-core
日志冲突
如果环境中有 多个 SLF4J 的实现,则发生了日志冲突:SLF4J 会发出警告,并加载环境中第一个实现(StaticLoggerBinder)。
private static boolean isAmbiguousStaticLoggerBinderPathSet(Set<URL> binderPathSet) {
return binderPathSet.size() > 1;
}
/**
* Prints a warning message on the console if multiple bindings were found
* on the class path. No reporting is done otherwise.
*
*/
private static void reportMultipleBindingAmbiguity(Set<URL> binderPathSet) {
// 如果环境中查找到多个日志实现
if (isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
Util.report("Class path contains multiple SLF4J bindings.");
for (URL path : binderPathSet) {
Util.report("Found binding in [" + path + "]");
}
Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
}
}
解决冲突:
- 让环境中只剩下一个实现。
如果有三方依赖中直接使用了多个不同的日志实现,则:可以使用日志适配转换不同的日志实现。
比如,三方依赖中存在 log4j1 而系统使用 log4j2 时。可以:
-
排除 log4j1 的所有依赖
log4j:log4j
org.slf4j:slf4j-log4j12
-
添加
log4j-1.2-api
依赖适配 log4j1 到 log4j2。org.apache.logging.log4j:log4j-1.2-api
-
关于日志冲突