一、整体介绍
介绍:
The Simple Logging Facade for Java (SLF4J) serves as a simple facade or abstraction for various logging frameworks (e.g. java.util.logging, logback, log4j) allowing the end user to plug in the desired logging framework at deployment time.
工作模式:
sl4j-api结构图
根据入口代码来分析。
Logger logger = LoggerFactory.getLogger(logbackTest.class);
我们可以知道初始化,是从这个方法开始的。
首先入口代码非常简单
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
public static ILoggerFactory getILoggerFactory() {
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://bugzilla.slf4j.org/show_bug.cgi?id=106
return TEMP_FACTORY;
}
throw new IllegalStateException("Unreachable code");
}
上面基本上就是整个调用流程的代码了。第5行通过factory模式得到了一个日志工厂,第6行根据名字获取日志logger。
第3个函数是主要的逻辑函数,根据INITIALIZATION_STATE 不同状态,做不同的处理。
当INITIALIZATION_STATE ==0时,初始化日志框架【9行】
当INITIALIZATION_STATE ==1时,返回的TEMP_FACTORY,我们尅看下定义【15行】
static SubstituteLoggerFactory TEMP_FACTORY = new SubstituteLoggerFactory();
SubstituteLoggerFactory是什么尼,很简单的结构,是由sl4j实现的一个。。,看一下文档描述:
SubstituteLoggerFactory 简单实现了ILoggerFactory接口,并总是返回一个单例的 NOPLogger实例。
作用:
* It used as a temporary substitute for the real ILoggerFactory during its
* auto-configuration which may re-enter LoggerFactory to obtain logger
* instances. See also http://bugzilla.slf4j.org/show_bug.cgi?id=106
当INITIALIZATION_STATE ==2时,返回的失败信息【17行】,其中我们可以看下各种状态枚举定义,可以知道2对应的状态是日志初始化失败
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;
当INITIALIZATION_STATE ==3时,返回的失败信息【19行】,显然是日志初始化成功,获取日志单例信息。
当INITIALIZATION_STATE ==4时,返回的失败信息【21行】,我们看见返回会的是NOP_FALLBACK_FACTORY(没有找到桥接jar)对象,看下定义
static NOPLoggerFactory NOP_FALLBACK_FACTORY = new NOPLoggerFactory();
/**
* NOPLoggerFactory is an trivial implementation of {@link
* ILoggerFactory} which always returns the unique instance of
* NOPLogger.
*
* @author Ceki Gülcü
*/
public class NOPLoggerFactory implements ILoggerFactory {
public NOPLoggerFactory() {
// nothing to do
}
public Logger getLogger(String name) {
return NOPLogger.NOP_LOGGER;
}
}
根据定义和和注释,我们我们可以大胆猜测,4状态就是什么都不做,NO Operator Logger Factory.仅仅是一个空实现。
官网描述:SINCE 1.6.0 If no binding is found on the class path, then SLF4J will default to a no-operation implementation.
二、核心代码分析
重点分析这个方法performInitialization();
1 private final static void performInitialization() {
2 bind();
3 if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
4 versionSanityCheck();
5 }
6 }
7 private final static void bind() {
8 try {
9 Set staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
10 reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
11 // the next line does the binding
12 StaticLoggerBinder.getSingleton();
13 INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
14 reportActualBinding(staticLoggerBinderPathSet);
15 emitSubstituteLoggerWarning();
16 } catch (NoClassDefFoundError ncde) {
17 String msg = ncde.getMessage();
18 if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
19 INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
20 Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
21 Util.report("Defaulting to no-operation (NOP) logger implementation");
22 Util.report("See " + NO_STATICLOGGERBINDER_URL
23 + " for further details.");
24 } else {
25 failedBinding(ncde);
26 throw ncde;
27 }
28 } catch (java.lang.NoSuchMethodError nsme) {
29 String msg = nsme.getMessage();
30 if (msg != null && msg.indexOf("org.slf4j.impl.StaticLoggerBinder.getSingleton()") != -1) {
31 INITIALIZATION_STATE = FAILED_INITIALIZATION;
32 Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
33 Util.report("Your binding is version 1.5.5 or earlier.");
34 Util.report("Upgrade your binding to version 1.6.x.");
35 }
36 throw nsme;
37 } catch (Exception e) {
38 failedBinding(e);
39 throw new IllegalStateException("Unexpected initialization failure", e);
40 }
41 }
首先进入bind方法,
bind方法第9行,findPossibleStaticLoggerBinderPathSet(),根据名字我们知道这个函数的功能是查找可能的StaticLoggerBinder路径集合。
1 private static Set findPossibleStaticLoggerBinderPathSet() {
2 // use Set instead of list in order to deal with bug #138
3 // LinkedHashSet appropriate here because it preserves insertion order during iteration
4 Set staticLoggerBinderPathSet = new LinkedHashSet();
5 try {
6 ClassLoader loggerFactoryClassLoader = LoggerFactory.class
7 .getClassLoader();
8 Enumeration paths;
9 if (loggerFactoryClassLoader == null) {
10 paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
11 } else {
12 paths = loggerFactoryClassLoader
13 .getResources(STATIC_LOGGER_BINDER_PATH);
14 }
15 while (paths.hasMoreElements()) {
16 URL path = (URL) paths.nextElement();
17 staticLoggerBinderPathSet.add(path);
18 }
19 } catch (IOException ioe) {
20 Util.report("Error getting resources from path", ioe);
21 }
22 return staticLoggerBinderPathSet;
23 }
我们可以看到都是在加载StaticLoggerBinder.class类
private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";
那么StaticLoggerBinder.class是干嘛的尼?传送门:http://skyao.github.io/2014/07/21/slfj4-binding/,后续会对StaticLoggerBinder仔细分析。
bind方法第10行,假设找到多个StaticLoggerBinder.class,就提示输出信息。
bind方法第12行,很关键的一行代码,和第10行关联起来了。获取一个StaticLoggerBinder单例.
1 private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
2 public static StaticLoggerBinder getSingleton() {
3 return SINGLETON;
4 }
5 static {
6 SINGLETON.init();
7 }
8 void init() {
9 try {
10 try {
11 new ContextInitializer(defaultLoggerContext).autoConfig();
12 } catch (JoranException je) {
13 Util.report("Failed to auto configure default logger context", je);
14 }
15 // logback-292
16 if(!StatusUtil.contextHasStatusListener(defaultLoggerContext)) {
17 StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext);
18 }
19 contextSelectorBinder.init(defaultLoggerContext, KEY);
20 initialized = true;
21 } catch (Throwable t) {
22 // we should never get here
23 Util.report("Failed to instantiate [" + LoggerContext.class.getName()
24 + "]", t);
25 }
26 }
可以看出其实就是一个单例,并初始化了。初始化的具体详情,我们在后续会分析。这里我们只需要知道得到了一个StaticLoggerBinder单例对象即可。
bind方法14,15,16行主要是改变状态,显示警告信息等。
由此我们知道bind方法核心功能是加载了StaticLoggerBinder.class,并初始化了单例对象。
我们回到上面getILoggerFactory方法。
getILoggerFactory方法19行代码:StaticLoggerBinder.getSingleton().getLoggerFactory();
即StaticLoggerBinder的getLoggerFactory方法,我们看一下定义。
1 public ILoggerFactory getLoggerFactory() {
2 if (!initialized) {
3 return defaultLoggerContext;
4 }
5
6 if (contextSelectorBinder.getContextSelector() == null) {
7 throw new IllegalStateException(
8 "contextSelector cannot be null. See also " + NULL_CS_URL);
9 }
10 return contextSelectorBinder.getContextSelector().getLoggerContext();
11 }
在第二行判断initialized状态在init()方法第20行知道,在初始化完成后,即为true。所以getLoggerFactory方法会直接到第6行。
private final ContextSelectorStaticBinder contextSelectorBinder = ContextSelectorStaticBinder.getSingleton();
public static ContextSelectorStaticBinder getSingleton() {
return singleton;
}
static ContextSelectorStaticBinder singleton = new ContextSelectorStaticBinder();
第6行获取到一个ContextSelectorStaticBinder的实例。并且获取getContextSelector方法
1 public ContextSelector getContextSelector() {
2 return contextSelector;
3 }
4 ContextSelector contextSelector;
5 public void init(LoggerContext defaultLoggerContext, Object key) throws ClassNotFoundException,
6 NoSuchMethodException, InstantiationException, IllegalAccessException,
7 InvocationTargetException {
8 if(this.key == null) {
9 this.key = key;
10 } else if (this.key != key) {
11 throw new IllegalAccessException("Only certain classes can access this method.");
12 }
13
14
15 String contextSelectorStr = OptionHelper
16 .getSystemProperty(ClassicConstants.LOGBACK_CONTEXT_SELECTOR);
17 if (contextSelectorStr == null) {
18 contextSelector = new DefaultContextSelector(defaultLoggerContext);
19 } else if (contextSelectorStr.equals("JNDI")) {
20 // if jndi is specified, let's use the appropriate class
21 contextSelector = new ContextJNDISelector(defaultLoggerContext);
22 } else {
23 contextSelector = dynamicalContextSelector(defaultLoggerContext,
24 contextSelectorStr);
25 }
26 }
我们查询代码知道getContextSelector获取的对象是在init()中方法赋值的。
在StaticLoggerBinder类中init()方法第19行代码,进行了ContextSelectorStaticBinder的实例化。
contextSelectorBinder.getContextSelector()根据代码知道,其实就是LoggerContext包装对象。
第10代码,contextSelectorBinder.getContextSelector().getLoggerContext();得到一个LoggerContext对象。
即,我们的getILoggerFactory的方法,得到了一个LoggerContext对象。
在getLogger方法第二行代码,传递name获取Logger对象。
1 public final Logger getLogger(final String name) {
2
3 if (name == null) {
4 throw new IllegalArgumentException("name argument cannot be null");
5 }
6
7 // if we are asking for the root logger, then let us return it without
8 // wasting time
9 if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {
10 return root;
11 }
12
13 int i = 0;
14 Logger logger = root;
15
16 // check if the desired logger exists, if it does, return it
17 // without further ado.
18 Logger childLogger = (Logger) loggerCache.get(name);
19 // if we have the child, then let us return it without wasting time
20 if (childLogger != null) {
21 return childLogger;
22 }
23
24 // if the desired logger does not exist, them create all the loggers
25 // in between as well (if they don't already exist)
26 String childName;
27 while (true) {
28 int h = LoggerNameUtil.getSeparatorIndexOf(name, i);
29 if (h == -1) {
30 childName = name;
31 } else {
32 childName = name.substring(0, h);
33 }
34 // move i left of the last point
35 i = h + 1;
36 synchronized (logger) {
37 childLogger = logger.getChildByName(childName);
38 if (childLogger == null) {
39 childLogger = logger.createChildByName(childName);
40 loggerCache.put(childName, childLogger);
41 incSize();
42 }
43 }
44 logger = childLogger;
45 if (h == -1) {
46 return childLogger;
47 }
48 }
49 }
至此,整个流程就走完了。
下一章,我们会对整个sl4j架构进行分析。分析代码的优缺点。
-------------------------------------------------------------------------华丽分割线,下面是StaticLoggerBinder代码分析-------------------------------
在我们的源码目录中,并没有这个类
那么他在哪里尼?这就是实现接口反转和适配很重要的一个环节了,还记得我们开始第一幅图么
,
sl4j-api会调用
看下类类定StaticLoggerBinder.class:binding一个实际的ILoggerFactory的实例。
* The binding of {@link LoggerFactory} class with an actual instance of
* {@link ILoggerFactory} is performed using information returned by this class.
来吧,一言不合就看源码:
1 public class StaticLoggerBinder implements LoggerFactoryBinder {
2
3 /**
4 * Declare the version of the SLF4J API this implementation is compiled
5 * against. The value of this field is usually modified with each release.
6 */
7 // to avoid constant folding by the compiler, this field must *not* be final
8 public static String REQUESTED_API_VERSION = "1.6"; // !final
9
10 final static String NULL_CS_URL = CoreConstants.CODES_URL + "#null_CS";
11
12 /**
13 * The unique instance of this class.
14 */
15 private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
16
17 private static Object KEY = new Object();
18
19 static {
20 SINGLETON.init();
21 }
22
23 private boolean initialized = false;
24 private LoggerContext defaultLoggerContext = new LoggerContext();
25 private final ContextSelectorStaticBinder contextSelectorBinder = ContextSelectorStaticBinder
26 .getSingleton();
27
28 private StaticLoggerBinder() {
29 defaultLoggerContext.setName(CoreConstants.DEFAULT_CONTEXT_NAME);
30 }
31
32 public static StaticLoggerBinder getSingleton() {
33 return SINGLETON;
34 }
35
36 /**
37 * Package access for testing purposes.
38 */
39 static void reset() {
40 SINGLETON = new StaticLoggerBinder();
41 SINGLETON.init();
42 }
43
44 /**
45 * Package access for testing purposes.
46 */
47 void init() {
48 try {
49 try {
50 new ContextInitializer(defaultLoggerContext).autoConfig();
51 } catch (JoranException je) {
52 Util.report("Failed to auto configure default logger context", je);
53 }
54 // logback-292
55 if(!StatusUtil.contextHasStatusListener(defaultLoggerContext)) {
56 StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext);
57 }
58 contextSelectorBinder.init(defaultLoggerContext, KEY);
59 initialized = true;
60 } catch (Throwable t) {
61 // we should never get here
62 Util.report("Failed to instantiate [" + LoggerContext.class.getName()
63 + "]", t);
64 }
65 }
66
67 public ILoggerFactory getLoggerFactory() {
68 if (!initialized) {
69 return defaultLoggerContext;
70 }
71
72 if (contextSelectorBinder.getContextSelector() == null) {
73 throw new IllegalStateException(
74 "contextSelector cannot be null. See also " + NULL_CS_URL);
75 }
76 return contextSelectorBinder.getContextSelector().getLoggerContext();
77 }
78
79 public String getLoggerFactoryClassStr() {
80 return contextSelectorBinder.getClass().getName();
81 }
82
83 }
先看下类图: