背景就不介绍了,总之是那些over,to,slf4j+log4j,slf4j+log4j2,commons-logging,一言以蔽之,何等乱七八糟!
看资料的时候觉得两张图不错,扒过来的,链接在最后参考文章中
slf4j
那先来看slf4j,slf4j就是个日志的接口,没有提供任何实现
Logger
Logger类是入口方法,是个interface,具体的日志实现框架会实现这些接口
ILoggerFactory
获取Logger的接口类,具体的日志实现框架需实现getLogger()方法
LoggerFactory
静态绑定具体日志实现框架,提过Logger对象
NOPLogger
slf4j自带的Logger的实现类
流程
流程上都是先通过LoggerFactory获取Logger,然后通过Logger记录日志
获取Logger
通过静态方法getLogger(Class clazz)获取
public static Logger getLogger(Class<?> clazz) {
Logger logger = getLogger(clazz.getName());
if (DETECT_LOGGER_NAME_MISMATCH) {
Class<?> autoComputedCallingClass = Util.getCallingClass();
if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(),
autoComputedCallingClass.getName()));
Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
}
}
return logger;
}
getLogger(Class clazz)再通过getLogger(String name)使用ILoggerFactory.getILoggerFactory()方法获取ILoggerFactory实例,就如上面说的,ILoggerFactory是用来获取Logger对象的接口类
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");
}
这里的初始化有5个状态
UNINITIALIZED 未初始化
ONGOING_INITIALIZATION 正在被初始化
FAILED_INITIALIZATION 初始化失败
SUCCESSFUL_INITIALIZATION 初始化成功
NOP_FALLBACK_INITIALIZATION 空状态,表示无底层日志实现框架时的结果
-
注:注意到了一个细节,这里用了一个DCL,理论上DCL会有JVM指令重排的问题(参考文章一),再看了一眼INITIALIZATION_STATE的定义,hh,多虑了。
static Set<URL> findPossibleStaticLoggerBinderPathSet() { // use Set instead of list in order to deal with bug #138 // LinkedHashSet appropriate here because it preserves insertion order // during iteration Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>(); try { ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader(); Enumeration<URL> paths; if (loggerFactoryClassLoader == null) { paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH); } else { paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH); } while (paths.hasMoreElements()) { URL path = paths.nextElement(); staticLoggerBinderPathSet.add(path); } } catch (IOException ioe) { Util.report("Error getting resources from path", ioe); } return staticLoggerBinderPathSet;
}
INITIALIZATION_STATE初始化值是UNINITIALIZED,这里标记为ONGOING_INITIALIZATION后,进入bind流程。findPossibleStaticLoggerBinderPathSet()方法寻找StaticLoggerBinder.class 文件,StaticLoggerBinder类实现了LoggerFactoryBinder接口的getLoggerFactory(),pom未添加日志实现依赖包的话是找不到StaticLoggerBinder.class 文件的
当找不到StaticLoggerBinder.class 文件时,则走到NoClassDefFoundError异常,INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION,则会用LoggerFactory下的NOPLoggerFactory对象,而NOPLoggerFactory基本是个空实现(所以最后输出也是空)
- 注:StaticLoggerBinder.class 在 slf-api.jar 中是不存在的,并没有打包进去,但是在slf-api-source.jar 是存在 org.slf4j.impl.StaticLoggerBinder这个类的,因为需要保证 slf4j 本身编译不报错
真正实现在log4j-api-2.x上,通过log4j-slf4j-impl-2.x把slf4j桥接到log4j2上(我自己的项目),第三方日志实现框架提供logger对象和log.trace()等等方法
参考文章:
1、https://www.infoq.cn/article/double-checked-locking-with-delay-initialization
2、https://www.itcodemonkey.com/article/1725.html
3、http://techblog.ppdai.com/2018/07/04/20180704/