天天看點

Log4j三大元件運作機制

Log4j主要有三個元件:

    Logger:負責供用戶端代碼調用,執行debug(Object msg)、info(Object msg)、warn(Object msg)、error(Object msg)等方法。

    Appender:負責日志的輸出,Log4j已經實作了多種不同目标的輸出方式,可以向檔案輸出日志、向控制台輸出日志、向Socket輸出日志等。

    Layout:負責日志資訊的格式化。

執行順序及關系

      調用Log4j輸出日志時,調用各個元件的順序:

    1、日志資訊傳入 Logger。

    2、将日志資訊封裝成 LoggingEvent 對象并傳入 Appender。

    3、在 Appender 中調用 Filter 對日志資訊進行過濾,調用 Layout 對日志資訊進行格式化,然後輸出。

Log4j 中三種設定配置資訊的方式:

   1、利用系統提供的 BasicConfigurator

   2、利用 properties 格式的配置檔案,通過 PropertyConfigurator 類來從一個 perproties 檔案中加載配置資訊。

   3、利用 XML 檔案來配置資訊,通過 DOMConfigurator 類來從一個 XML 檔案中加載配置資訊。

下面的圖檔來自于網絡:

Log4j三大元件運作機制
源碼分析Log4j工作原理: (源自:http://flym.iteye.com/blog/417434)

Log4j如何加載配置檔案,并進行日志記錄?

    将Log4j從網上down下來,并建立工程,将源代碼導進去。從Logger入手,一般來說,取得一個Log都是通過

Java代碼  

Log4j三大元件運作機制
  1. Logger getLogger(Class clazz) {  
  2.     return LogManager.getLogger(clazz.getName());  
  3.  }  

轉入LogManager,首先應該注意的是這個類的static塊, 這個塊其實就是一個加載log4j配置檔案的過程。即在程式啟動之初,在JVM需要加載這個類時,這個初始化塊會自動運作,并且加載整個配置,以完成log4j的啟動。

Log4j三大元件運作機制
  1. Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG));  
  2. ......  
  3. OptionConverter.selectAndConfigure(url, configuratorClassName,  
  4.                        LogManager.getLoggerRepository());  

上面的所有代碼均完成兩件事,第一件事就是構造一個ROOT的Logger對象,此對象作為所有logger對象的最上層,其它logger的相應

屬性均從這個對象進行繼承或改寫,就好像java裡的繼承一樣,這個類是内定的,不能由配置檔案直接指定,且root都是在最頂層的,其他logger均

在此下,這樣形成一個完整的logger樹。下層可以引用上層,上層管理下層。   

第二件事則是去尋找配置檔案的位址資訊,通過各種方法都尋找log4j.xml或log4j.properties檔案,然後對檔案進行解析。(在此處,

通過properties檔案進行解析)

Log4j三大元件運作機制
  1. OptionConverter.void selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy)  

 這個方法會最終通過指定的檔案解析類(此處是PropertyConfigurator)進行解析,轉入

Log4j三大元件運作機制
  1. configurator.doConfigure(url, hierarchy)  

 這個方法将,url轉化成一個properties對象,進行解析。

Log4j三大元件運作機制
  1. doConfigure(props, hierarchy);  

 進入這個方法

Log4j三大元件運作機制
  1. String value = properties.getProperty(LogLog.DEBUG_KEY);//即log4j.debug  
  2.     if(value == null) {  
  3.       value = properties.getProperty("log4j.configDebug");  
  4.       if(value != null) {  
  5.       LogLog.setInternalDebugging(OptionConverter.toBoolean(value, true));  
  6.     }  

  上面方法讀取一個關于log4j自身的debug

Level資訊,主要用于log4j内部在解析時調用(因為log4j還不能使用logger對象進行寫資訊,它用到一個LogLog的類,來模拟

logger記錄,用于在自身解析的過程中輸出一些資訊),一般來說,這個都用不到。

    主要的資訊集中在以下三句話:

Log4j三大元件運作機制
  1. configureRootCategory(properties, hierarchy);  
  2. configureLoggerFactory(properties);  
  3. parseCatsAndRenderers(properties, hierarchy);  

第一句話用于解析root根對象上的相關配置。

第二句話用于解析loggerFactory(factory用于建立logger對象)

第三句話用于解析除rootLogger之外的其他logger以及render資訊。

第一句:

Log4j三大元件運作機制
  1.  void configureRootCategory(Properties props, LoggerRepository hierarchy) {  
  2.    String effectiveFrefix = ROOT_LOGGER_PREFIX;  
  3.    String value = OptionConverter.findAndSubst(ROOT_LOGGER_PREFIX, props);  
  4.    if(value == null) {  
  5.      value = OptionConverter.findAndSubst(ROOT_CATEGORY_PREFIX, props);  
  6.      effectiveFrefix = ROOT_CATEGORY_PREFIX;  
  7.    }  
  8. .....  
  9.      Logger root = hierarchy.getRootLogger();  
  10.      synchronized(root) {  
  11. parseCategory(props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value);  
  12.      }  

上面語句用于從屬性檔案中尋找logger.rootLogger或logger.rootCategory屬性的值然後,再進行配置rootLogger對象資訊(如Level,appender等)

Log4j三大元件運作機制
  1.   void parseCategory(Properties props, Logger logger, String optionKey,  
  2.              String loggerName, String value) {  
  3.   StringTokenizer st = new StringTokenizer(value, ",");  
  4.     if(!(value.startsWith(",") || value.equals(""))) {  
  5.       if(!st.hasMoreTokens())  
  6.     return;  
  7.       String levelStr = st.nextToken();  
  8.       if(INHERITED.equalsIgnoreCase(levelStr) ||   
  9.                                       NULL.equalsIgnoreCase(levelStr)) {  
  10.     if(loggerName.equals(INTERNAL_ROOT_NAME)) {  
  11.       LogLog.warn("The root logger cannot be set to null.");  
  12.     } else {  
  13.       logger.setLevel(null);  
  14.       } else {  
  15.     logger.setLevel(OptionConverter.toLevel(levelStr, (Level) Level.DEBUG));  
  16.       }  
  17.     logger.removeAllAppenders();  
  18.     Appender appender;  
  19.     String appenderName;  
  20.     while(st.hasMoreTokens()) {  
  21.       appenderName = st.nextToken().trim();  
  22.       appender = parseAppender(props, appenderName);  
  23.       if(appender != null) {  
  24.     logger.addAppender(appender);  
  25.   }  

上面這些内容,即是通過logger屬性的值(形如debug,A,R)等,然後将值以,分隔進行解析,将第一個字元串定義為logger的Level資訊,後面的值則定義為此logger的appender名稱。

當然上面這個方法,即不是盡為rootLogger服務,對于其他logger也調用這個方法,故上面在解析Level時,對root作了單獨判斷(因為

rootLogger的Level不能為空,至少均需要一個值)。處理Level,即将level值簡單設定在logger上即可以了。

   接下來即解析appender資訊,通過緊接着level後面的字元串,按清單方式進行解析,然後将appender加入logger的appenderList(即要進行資訊處理的監聽器表)中。進入parseAddpender(解析appender)

Log4j三大元件運作機制
  1.  String prefix = APPENDER_PREFIX + appenderName;  
  2.  String layoutPrefix = prefix + ".layout";  
  3.  appender = (Appender) OptionConverter.instantiateByKey(props, prefix,  
  4.               org.apache.log4j.Appender.class,  
  5.               null);  
  6. ...  
  7.  appender.setName(appenderName);  
  8.  if(appender instanceof OptionHandler) {  
  9.    if(appender.requiresLayout()) {  
  10. yout layout = (Layout) OptionConverter.instantiateByKey(props,  
  11.                       layoutPrefix,  
  12.                       Layout.class,  
  13.                       null);  
  14. appender.setLayout(layout);  
  15.        PropertySetter.setProperties(layout, props, layoutPrefix + ".");  
  16. LogLog.debug("End of parsing for \"" + appenderName +"\".");  
  17.    PropertySetter.setProperties(appender, props, prefix + ".");  
  18.  registryPut(appender);  

 上面這個方法,即是解析appender對象,通過log4j.appender.X的字首(X表示在rootLogger後的appender

名稱)來取得appender類名,并嘗試執行個體化,然後根據appender來判斷是否需要再解析appender的layout(即

log4j.appender.X.layout這個鍵),解析并設定相應屬性,最後分别解析appender本身的屬性資訊和layout的屬性資訊。

(通過ProperSetter這個類,根據javaBean屬性映射,将指定字尾後的資訊當作一個鍵,字尾在屬性檔案中的值作為指定鍵的值,并将這個鍵

值映射,通過javaBean設定到相應的對象上)

    至此,rootLogger即解析完畢。

   第二句:解析loggerFactory,略。

   第三句:解析其他logger資訊。

Log4j三大元件運作機制
  1.   void parseCatsAndRenderers(Properties props, LoggerRepository hierarchy) {  
  2.     Enumeration enumeration = props.propertyNames();  
  3.     while(enumeration.hasMoreElements()) {  
  4.       String key = (String) enumeration.nextElement();  
  5.       if(key.startsWith(CATEGORY_PREFIX) || key.startsWith(LOGGER_PREFIX)) {  
  6.     String loggerName = null;  
  7.     if(key.startsWith(CATEGORY_PREFIX)) {  
  8.       loggerName = key.substring(CATEGORY_PREFIX.length());  
  9.     } else if(key.startsWith(LOGGER_PREFIX)) {  
  10.       loggerName = key.substring(LOGGER_PREFIX.length());  
  11.     String value =  OptionConverter.findAndSubst(key, props);  
  12.     Logger logger = hierarchy.getLogger(loggerName, loggerFactory);  
  13.     synchronized(logger) {  
  14.       parseCategory(props, logger, key, loggerName, value);  
  15.       parseAdditivityForLogger(props, logger, loggerName);  
  16.       } else if(key.startsWith(RENDERER_PREFIX)) {  

這個方法,則主要根據logger.logger為字首的屬性資訊進行解析,并根據這個屬性資訊生成logger,并放在繼承樹中,設定相應樹資訊,最後設定其他資訊(如appender,level)等,與rootLogger解析基本一緻。

繼續閱讀