`

Log日志框架的学习二.slf4j源代码分析

阅读更多

Commons-log + log4j 这黄金搭档一直以来都让我们很省心,很好的完成了日志的需求。但是随着技术的变更和对性能的追求,slf4j 和 logback 这对后起之秀的到来好像打破了原本很平静的日志系统,频繁的出现包冲突... 

       和平的日子不在了,让我们一起来看看究竟发生了什么...

 

 

首先看看这些个包,特别是slf4j引入后就引入了一大堆包之后就有点懵了。


为什么commons-logging和jcl-over-slf4j会有冲突呢?看一下它们的类结构


很清晰的可以看到jcl-over-slf4j 重写了 commons-logging...

 

还有slf4j-api的实现呢,同样看类:

其实就这么简单,往往看了代码之后才发现错误是这么显而易见。。。

 

 

顺着研究,继续看一下slf4j的源码及流程

1.测试类

 

Java代码  收藏代码
  1. package com.taobao.wuzhong.log;  
  2.   
  3. import org.apache.commons.logging.Log;  
  4. import org.apache.commons.logging.LogFactory;  
  5. import org.junit.Test;  
  6. import org.slf4j.Logger;  
  7. import org.slf4j.LoggerFactory;  
  8.   
  9. /** 
  10.  * DESC: 
  11.  *  
  12.  * Copyright: Copyright 2011 m.taobao.com 
  13.  *  
  14.  * @author wuzhong@taobao.com 
  15.  * @time 2011-4-6 下午03:42:11 
  16.  * @version 1.0 
  17.  **/  
  18. public class LogTest {  
  19.   
  20.     // Logback tries to find a file called logback.groovy in the classpath.  
  21.     // If no such file is found, logback tries to find a file called  
  22.     // logback-test.xml in the classpath.  
  23.     // If no such file is found, it checks for the file logback.xml in the  
  24.     // classpath..  
  25.     // If neither file is found, logback configures itself automatically using  
  26.     // the BasicConfigurator which will cause logging output to be directed to  
  27.     // the console.  
  28.     @Test  
  29.     public void test() {  
  30.                 //commons-logging的方式获取  
  31.         Log log = LogFactory.getLog(LogTest.class);  
  32.                 //slf4j直接的方式获取,推荐用这个  
  33.         Logger log2 = LoggerFactory.getLogger(LogTest.class);  
  34.         log.debug("eeeeee {} {} {}");  
  35.         log2.debug("{} {} {}"new String[] { "a""b""c" });  
  36.     }  
  37.   
  38. }  

 

 

logFactory.getLog 会调用内部静态变量 Slf4jLogFactory.getInstance方法,如下:

 

 public Log getInstance(String name) throws LogConfigurationException {

Java代码  收藏代码
  1. Log instance = null;  
  2. // protect against concurrent access of loggerMap  
  3. synchronized (this) {  
  4.   instance = (Log) loggerMap.get(name);  
  5.   if (instance == null) {  
  6.     Logger logger = LoggerFactory.getLogger(name);   //slf4j的方式,代理过去了  
  7.     if(logger instanceof LocationAwareLogger) {  
  8.       instance = new SLF4JLocationAwareLog((LocationAwareLogger) logger);  //包装了一层,做适配  
  9.     } else {  
  10.       instance = new SLF4JLog(logger);  
  11.     }  
  12.     loggerMap.put(name, instance);  
  13.   }  
  14. }  
  15. return (instance);  

 

loggerFactory 会调用getILoggerFactory().getlOgger()

 

Java代码  收藏代码
  1. LoggerFactory.java  
  2.  public static ILoggerFactory getILoggerFactory() {  
  3.     if (INITIALIZATION_STATE == UNINITIALIZED) {  
  4.       INITIALIZATION_STATE = ONGOING_INITILIZATION;  
  5.       performInitialization();  
  6.   
  7.     }  
  8.     switch (INITIALIZATION_STATE) {  
  9.     case SUCCESSFUL_INITILIZATION:  
  10.       return getSingleton().getLoggerFactory();  
  11.     case FAILED_INITILIZATION:  
  12.       throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);  
  13.     case ONGOING_INITILIZATION:  
  14.       // support re-entrant behavior.  
  15.       // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106  
  16.       return TEMP_FACTORY;  
  17.     }  
  18.     throw new IllegalStateException("Unreachable code");  
  19.   }  
  20.   
  21.  private final static void performInitialization() {  
  22.     bind();  
  23.     versionSanityCheck();  
  24.     singleImplementationSanityCheck();  
  25.   
  26.   }  

这里的bind很关键,这里动态的绑定了slf4j-api的实现机制

 

Java代码  收藏代码
  1. static {  
  2.     SINGLETON.init();  
  3.   }  
  4.   
  5.   /** 
  6.    * Package access for testing purposes. 
  7.    */  
  8.   void init() {  
  9.     try {  
  10.       try {  
  11.         new ContextInitializer(defaultLoggerContext).autoConfig();  
  12.       } catch (JoranException je) {  
  13.         Util.reportFailure("Failed to auto configure default logger context",  
  14.             je);  
  15.       }  
  16.       StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext);  
  17.       contextSelectorBinder.init(defaultLoggerContext, KEY);  
  18.       initialized = true;  
  19.     } catch (Throwable t) {  
  20.       // we should never get here  
  21.       Util.reportFailure("Failed to instantiate ["  
  22.           + LoggerContext.class.getName() + "]", t);  
  23.     }  
  24.   }  

 

获取配置信息初始化

 

Java代码  收藏代码
  1. autoConfig ….  
  2. public URL findURLOfDefaultConfigurationFile(boolean updateStatus) {  
  3.     ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);  
  4.     URL url = findConfigFileURLFromSystemProperties(myClassLoader, updateStatus);  
  5.     if (url != null) {  
  6.       return url;  
  7.     }  
  8.   
  9.     url = Loader.getResource(TEST_AUTOCONFIG_FILE, myClassLoader);  
  10.     if (updateStatus) {  
  11.       statusOnResourceSearch(TEST_AUTOCONFIG_FILE, myClassLoader, url);  
  12.     }  
  13.     if (url != null) {  
  14.       return url;  
  15.     }  
  16.   
  17.     url = Loader.getResource(AUTOCONFIG_FILE, myClassLoader);  
  18.     if (updateStatus) {  
  19.       statusOnResourceSearch(AUTOCONFIG_FILE, myClassLoader, url);  
  20.     }  
  21.     return url;  
  22.   }  
  23.   
  24.   public void autoConfig() throws JoranException {  
  25.     StatusListenerConfigHelper.installIfAsked(loggerContext);  
  26.     URL url = findURLOfDefaultConfigurationFile(true);  
  27.     if (url != null) {  
  28.       configureByResource(url);  
  29.     } else {  
  30.       BasicConfigurator.configure(loggerContext);  
  31.     }  
  32.   }  

 

最后画张流程图总结下,^_^


 

 

总结: log框架应该很好的诠释了 facade , adapter , 实现上还是比较简单的,很好的做到了接口和实现的分离,对今后的代码组织有一定的启发

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics