log4j在日常開發中經常使用,但有時候對 配置文件應該放到什么位置有疑惑。現在我們通過從代碼的角度來看待這個問題,
看完后你也許會恍然大悟哦。
開始吧。
Log4j的組成及架構:
Log4j由三個重要的組成構成:日志記錄器(Loggers),輸出端(Appenders)和日志格式化器(Layout)。
1.日志記錄器(Loggers):控制要輸出哪些日志記錄語句,對日志信息進行級別限制。
2.輸出端(Appenders):指定了日志將打印到控制台還是文件中。
3.日志格式化器(Layout):控制日志信息的顯示格式。
類圖結構如下(不要眼花繚亂,我辛辛苦苦一個一個畫出來的):
log4j初始化
從架構上我們可以看出logger的實現是從logManager來具體完成的,因此初始化在logManager的static塊中,如下:
static { // By default we use a DefaultRepositorySelector which always returns 'h'. Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG)); repositorySelector = new DefaultRepositorySelector(h); /** Search for the properties file log4j.properties in the CLASSPATH. */ String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY, null); // if there is no default init override, then get the resource // specified by the user or the default config file. if(override == null || "false".equalsIgnoreCase(override)) { String configurationOptionStr = OptionConverter.getSystemProperty( DEFAULT_CONFIGURATION_KEY, null); String configuratorClassName = OptionConverter.getSystemProperty( CONFIGURATOR_CLASS_KEY, null); URL url = null; // if the user has not specified the log4j.configuration // property, we search first for the file "log4j.xml" and then // "log4j.properties" if(configurationOptionStr == null) { url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE); if(url == null) { url = Loader.getResource(DEFAULT_CONFIGURATION_FILE); } } else { try { url = new URL(configurationOptionStr); } catch (MalformedURLException ex) { // so, resource is not a URL: // attempt to get the resource from the class path url = Loader.getResource(configurationOptionStr); } } // If we have a non-null url, then delegate the rest of the // configuration to the OptionConverter.selectAndConfigure // method. if(url != null) { LogLog.debug("Using URL ["+url+"] for automatic log4j configuration."); try { OptionConverter.selectAndConfigure(url, configuratorClassName, LogManager.getLoggerRepository()); } catch (NoClassDefFoundError e) { LogLog.warn("Error during default initialization", e); } } else { LogLog.debug("Could not find resource: ["+configurationOptionStr+"]."); } } else { LogLog.debug("Default initialization of overridden by " + DEFAULT_INIT_OVERRIDE_KEY + "property."); } }
讓我們深入進去看看是怎么初始化的?
第一步:創建一個默認的RepositorySelector,RepositorySelector在logManager中使用,它的實現類對特定的應用上下文提供了一個LoggerRespository。LoggerRespository的實現類負責追蹤應用上下文。ResponsitorySelector提供了一個方法:
public LoggerRepository getLoggerRepository();
LoggerRepository從字面上理解,它是一個Logger的容器,它會創建並緩存Logger實例,從而具有相同名字的Logger實例不會多次創建,以提高性能。它的這種特性有點類似Spring的IOC概念。Log4J支持兩種配置文件:properties文件和xml文件。Configurator解析配置文件,並將解析后的信息添加到LoggerRepository中。LogManager最終將LoggerRepository和Configurator整合在一起。
LoggerRepository是一個Logger的容器,它負責創建、緩存Logger實例,同時它也維護了Logger之間的關系,因為在Log4J中,所有Logger都組裝成以RootLogger為根的一棵樹,樹的層次由Logger的Name來決定,其中以’.’分隔。
除了做為一個Logger容器,它還有一個Threshold屬性,用於過濾所有在Threshold級別以下的日志。以及其他和Logger操作相關的方法和屬性。
public interface LoggerRepository { public void addHierarchyEventListener(HierarchyEventListener listener); boolean isDisabled(int level); public void setThreshold(Level level); public void setThreshold(String val); public void emitNoAppenderWarning(Category cat); public Level getThreshold(); public Logger getLogger(String name); public Logger getLogger(String name, LoggerFactory factory); public Logger getRootLogger(); public abstract Logger exists(String name); public abstract void shutdown(); public Enumeration getCurrentLoggers(); public abstract void fireAddAppenderEvent(Category logger, Appender appender); public abstract void resetConfiguration(); }
Hierarchy類
Hierarchy是Log4J中默認對LoggerRepository的實現類,它用於表達其內部的Logger是以層次結構存儲的。在對LoggerRepository接口的實現中,getLogger()方法是其最核心的實現,因而首先從這個方法開始。Hierarchy中用一個Hashtable來存儲所有Logger實例,它以CategoryKey作為key,Logger作為value,其中CategoryKey是對Logger中Name字符串的封裝,之所以要引入這個類是出於性能考慮,因為它會緩存Name字符串的hash code,這樣在查找過程中計算hash code時就可以直接取得而不用每次都計算。
第二步:從classpath路徑查找log4j.properties屬性配置文件。
1. 判斷配置文件有沒有重寫?
/** Search for the properties file log4j.properties in the CLASSPATH. */ String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,null);
//public static final String DEFAULT_INIT_OVERRIDE_KEY = "log4j.defaultInitOverride";
publicstatic String getSystemProperty(String key, String def) { try { return System.getProperty(key, def); } catch(Throwable e) { // MS-Java throws com.ms.security.SecurityExceptionEx LogLog.debug("Was not allowed to read system property \""+key+"\"."); return def; } }
2. override為null或者為false時沒有重寫,然后從系統屬性中查找key:log4j.configuration和log4j.configuratorClass屬性。如果沒有設置log4j.configuration屬性,就查找log4j.xml,然后查找log4j.properties文件。使用方法:
if(configurationOptionStr == null) { url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE); if(url == null) { url = Loader.getResource(DEFAULT_CONFIGURATION_FILE); }
使用java2的線程上下文類加載器來查找資源,如果查找失敗,則使用加載該類(loader)的類加載器來加載資源。
3.如果設置了log4j.configuration屬性,則使用url形式讀取,如果資源不是url,則從classpath獲取資源:
try { url = new URL(configurationOptionStr); } catch (MalformedURLException ex) { // so, resource is not a URL: // attempt to get the resource from the class path url = Loader.getResource(configurationOptionStr); }
4. 如果log4j.configuration屬性為url形式:
// If we have a non-null url, then delegate the rest of the // configuration to the OptionConverter.selectAndConfigure // method. if(url != null) { LogLog.debug("Using URL ["+url+"] for automatic log4j configuration."); try { OptionConverter.selectAndConfigure(url, configuratorClassName, LogManager.getLoggerRepository()); } catch (NoClassDefFoundError e) { LogLog.warn("Error during default initialization", e); } } else { LogLog.debug("Could not find resource: ["+configurationOptionStr+"]."); }
小結:
The exact default initialization algorithm is defined as follows:
- Setting the log4j.defaultInitOverride system property to any other value then "false" will cause log4j to skip the default initialization procedure (this procedure).
- Set the resource string variable to the value of the log4j.configuration system property. The preferred way to specify the default initialization file is through the log4j.configuration system property. In case the system property log4j.configuration is not defined, then set the string variable resourceto its default value "log4j.properties".
- Attempt to convert the resource variable to a URL.
- If the resource variable cannot be converted to a URL, for example due to a MalformedURLException, then search for the resource from the classpath by calling org.apache.log4j.helpers.Loader.getResource(resource, Logger.class) which returns a URL. Note that the string "log4j.properties" constitutes a malformed URL.
See Loader.getResource(java.lang.String) for the list of searched locations.
- If no URL could not be found, abort default initialization. Otherwise, configure log4j from the URL.
The PropertyConfigurator will be used to parse the URL to configure log4j unless the URL ends with the ".xml" extension, in which case the DOMConfiguratorwill be used. You can optionaly specify a custom configurator. The value of the log4j.configuratorClass system property is taken as the fully qualified class name of your custom configurator. The custom configurator you specify must implement the Configurator interface.
參考文獻:
1. http://www.2cto.com/kf/201207/139798.html
2. http://logging.apache.org/log4j/1.2/manual.html