介绍:
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 }
先看下类图:
原文:http://www.cnblogs.com/xue-si-li/p/6270396.html