目录
- 本文debug slf4j 的源码执行流程
- 依次执行
- 不添加 日志实现框架(本篇主讲)
- 添加日志实现框架
- 通晓Slf4j 内部实现根据 classpath 实现 facade 的原理
涉及知识点
- 类加载
- 设计模式
- factory 工厂模式
- spi service provider interface 模式
- facade 外观模式
- singleton 单例模式
前沿
- commons-logging和slf4j都是日志的接口,供用户使用,而没有提供实现
- 当年Apache说服log4j以及其他的日志来按照commons-logging的标准编写,但是由于commons-logging的类加载有点问题,实现起来也不友好,因此log4j的作者就创作了slf4j,也因此而与commons-logging两分天下
项目背景
- IJ Maven 项目
- 准备 pom.xml
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
- step1:入口
public class LogDemo {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(LogDemo.class); //断点处
logger.info("log");
System.out.println("log test");
}
}
- step2:进入 LoggerFactory.java 之 getLogger(Class clazz)
- 首先注意 LoggerFactory 有一个 静态变量 INITIALIZATION_STATE,标记ILoggerFactory 实例初始化的结果
- 该变量的可能值已经用 static final 变量分别定义
//INITIALIZATION_STATE 的可能结果:
static final int UNINITIALIZED = 0;
static final int ONGOING_INITIALIZATION = 1;
static final int FAILED_INITIALIZATION = 2;
static final int SUCCESSFUL_INITIALIZATION = 3;
static final int NOP_FALLBACK_INITIALIZATION = 4;
1)类加载时初始化该变量
//类加载 初始化为 UNINITIALIZED 0
static volatile int INITIALIZATION_STATE = UNINITIALIZED;
2)现在进入 getLogger(Class clazz)
/**
* Return a logger named corresponding to the class passed as parameter,
* using the statically bound ILoggerFactory instance.
* 根据静态绑定的 ILoggerFactory 实例 获取 logger
**/
public static Logger getLogger(Class<?> clazz) {
//核心代码 class=log.learn.LogDemo
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;
}
- step3: 进入同类 getLogger(String name)
- 该方法是一个收口,以String 为参 获取和以 Class 类型为参都转至此
/**
* Return a logger named according to the name parameter using the
* statically bound ILoggerFactory instance.
* 说明最终获取 logger 的实现是由 静态绑定的 ILoggerFactory 实例决定的
**/
public static Logger getLogger(String name) {
//进入 getILoggerFactory 方法
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
- step4:进入同类 getILoggerFactory()
/**
* Return the ILoggerFactory instance in use.
*
* ILoggerFactory instance is bound with this class at compile time.
* 在编译时刻绑定 ILoggerFactory 的具体实例
**/
public static ILoggerFactory getILoggerFactory() {
//类加载时, INITIALIZATION_STATE == UNINITIALIZED,标志为未初始化
//且对 初始化状态的更新使用了 双重校验锁(DCL,即 double-checked locking),参考单例模式初始化
if (INITIALIZATION_STATE == UNINITIALIZED) {
synchronized (LoggerFactory.class) {
if (INITIALIZATION_STATE == UNINITIALIZED) {
//将初始化状态标记为 ONGING
INITIALIZATION_STATE = ONGOING_INITIALIZATION;ONGING
//执行 ILoggerFactory 实例初始化,实际是开始在 classpath 寻找看看是否存在 。。。
//我们先跳到初始化的位置,这也是Slf4j 实现 facade 的核心所在
//了解了核心,再转回来看下面的
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");
}
- step5: 进入performInitialization 方法
//可以看到该方法用 private 修饰,即在类内部使用
private final static void performInitialization() {
//核心代码 bind, 开始绑定 ILoggerFactory 具体实现
bind();
if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
versionSanityCheck();
}
}
- step6: 进入 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()) {
//绑定的过程,该方法去寻找 StaticLoggerBinder.class 文件
//Step7: 折返回来向下执行
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
}
// the next line does the binding
//寻找 StaticLoggerBinder 类若找不到则抛异常
//pom 没有添加 日志实现依赖,抛异常
StaticLoggerBinder.getSingleton();
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
reportActualBinding(staticLoggerBinderPathSet);
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-simple.jar 时打出的日志
Connected to the target VM, address: '127.0.0.1:64503', transport: 'socket'
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
- step7: 进入 findPossibleStaticLoggerBinderPathSet 方法
// We need to use the name of the StaticLoggerBinder class, but we can't
// reference the class itself.
// 根据classpath 下是否存在 StaticLoggerBinder 来作为判断是否存在 具体日志实现框架的标准
// 此处也暗示出 所有日志实现框架的包路径及 所必需包含的 StaticLoggerBinder 类路径
private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";
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;
//而用户在运行期,是获取不到引导类加载器bootstrapclassloader的,因此当一个类获取它的类加载器,得到的对象时null,就说明它是由引导类加载器加载的。
//引导类加载器是负责加载系统目录下的文件,因此源码中使用getSystemresource来获取资源文件。
if (loggerFactoryClassLoader == null) {
paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
} else {
//判断能否找到 StaticLoggerBinder 的 class 文件
//pom 未添加 日志实现 依赖包的话 是找不到该 class 文件的
//因为可能存在若干个该 class 文件,故此处用 Enumeration 来迭代存储URL,Enumeration 现在被 Iteration 替代
//两者功能一致
paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
}
while (paths.hasMoreElements()) {
URL path = paths.nextElement();
//将所有class url 存到有序set 中
staticLoggerBinderPathSet.add(path);
}
} catch (IOException ioe) {
Util.report("Error getting resources from path", ioe);
}
//返回保存了class URL 的有序集合,转到 step6,继续向下执行
return staticLoggerBinderPathSet;
}
- 重新研究 step6 后半部分
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()) {
//绑定的过程,该方法去寻找 StaticLoggerBinder.class 文件
//Step7: 折返回来向下执行
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
}
// the next line does the binding
//寻找 StaticLoggerBinder 类若找不到则抛异常
//pom 没有添加 日志实现依赖,抛异常
//此处在pom未添加 日志实现类时,classpath 是不存在 StaticLoggerBinder class 的,故抛出NoClassDefFoundError 异常
//进入catch 代码块
StaticLoggerBinder.getSingleton();
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
reportActualBinding(staticLoggerBinderPathSet);
fixSubstituteLoggers();
replayEvents();
// release all resources in SUBST_FACTORY
SUBST_FACTORY.clear();
} catch (NoClassDefFoundError ncde) {
String msg = ncde.getMessage();
if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
//初始化状态为 NOP_FALLBACK_INITIALIZATION,
//bind 方法结束,step5 中 performInitialization 方法结束
//INITIALIZATION_STATE 决定 后续使用哪个日志实现框架
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);
}
}
- step8: 进入 getILoggerFactory() 的 switch
//即最终选择的日志实现框架是通过 INITIALIZATION_STATE 来区分的
switch (INITIALIZATION_STATE) {
//如果是 SUCCESSFUL_INITIALIZATION,则说明成功在 classpath 找到了 实现框架
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;
}
- step9:重新进入 getLogger(String name)
public static Logger getLogger(String name) {
//classpath 不存在日志实现框架的,此刻得到的是 NOPLoggerFactory 的实例
//
ILoggerFactory iLoggerFactory = getILoggerFactory();
//进入NOPLoggerFactory 的 getLogger
return iLoggerFactory.getLogger(name);
}
- step10:进入NOPLoggerFactory 的 getLogger 方法
public class NOPLoggerFactory implements ILoggerFactory {
public NOPLoggerFactory() {
// nothing to do
}
//获取具体日志对象
public Logger getLogger(String name) {
return NOPLogger.NOP_LOGGER;
}
}
- Step11:进入 NOPLogger.NOP_LOGGER
public class NOPLogger extends MarkerIgnoringBase {
private static final long serialVersionUID = -517220405410904473L;
/**
* The unique instance of NOPLogger.
* 获得 NOPLogger 实例
*/
public static final NOPLogger NOP_LOGGER = new NOPLogger();
......
//实现的Logger 的所有方法,方法体全为空
/** A NOP implementation. */
public final void debug(String format, Object arg1, Object arg2) {
// NOP
}
}
- Step12:重新进入main 方法
public class LogDemo {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(LogDemo.class);
//进入NOPLogger 的 info 方法,方法体为空
logger.info("log");
}
}