slf4j 之logback日志之sl4j架構【二】


一、整體介紹

介紹:

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 }

先看下類圖:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM