Log4j這個東東,大家一直都在用,可是真正有多少人知道,爲什麼要那樣配列?或者說有多少不抄襲可以自己配一份log4j列?讀者可以問問自己能否辦到。我們本章就一些重要的環節結合源碼進行分析。
一、Log4j是在那個地方讀取配置文件的?
在回答這個問題前,首先想想我們在程序中是怎麼用Log4j來打印日誌的?
1.獲取Logger,如
private static Logger logger = Logger.getLogger(App.class);
2.在方法中調用logger方法打印日誌
logger.info("this is the main Method");
於是我們跟進getLogger()方法查看實現,發現該方法如下定下:
static
public
Logger getLogger(Class clazz) {
return LogManager.getLogger(clazz.getName());
}
他是委託LogManager去實現的。於是我們再次跟進LogManager,通過看查該類的結構,發現有個靜態的static初始塊,我們一讀代碼,發現原來Log4j的配置文件就是在這裏讀取的,而且他是先查找log4j.xml,如果查找不到再去查找log4j.properteis。
其實還有一個解決這個問題的辦法,就是將log4j的源碼,以項目的形式導入eclipse/idea中,然後全局搜索log4j.properties或是log4j.xml。爲什麼想到搜這2個文件名?因爲log4j默認配置文件名就是這2個,那麼他在程序中,肯定會有常量來定義這個2個文件名,就算沒人常量,也會有直接根據這2個文件名加載配置的地方。這是一個程序開發者正常的邏輯,和處理辦法。
二、我們在log4j.properties中,經常都會配置一個log4j.rootLogger?它的作用是什麼?
我們在配置文件中,定主了rootLogger,Appender,Layout, 爲什麼我們程序中log就會按照這些規格來輸出列? 難道我們自己定義的Logger跟這這個rootLogger有什麼關係?
其實從命名,我們是不是猜測到rootLogger,是我們定義的其它Logger的超類列? 事實上也確實如此!我們一路踏實getLogger()方法,一直跟蹤到了Hierarchy.getLogger();
public
Logger getLogger(String name, LoggerFactory factory) {
//System.out.println("getInstance("+name+") called.");
CategoryKey key = new CategoryKey(name);
// Synchronize to prevent write conflicts. Read conflicts (in
// getChainedLevel method) are possible only if variable
// assignments are non-atomic.
Logger logger;
synchronized(ht) {
Object o = ht.get(key);
if(o == null) {
logger = factory.makeNewLoggerInstance(name);
logger.setHierarchy(this);
ht.put(key, logger);
updateParents(logger);
return logger;
} else if(o instanceof Logger) {
return (Logger) o;
} else if (o instanceof ProvisionNode) {
//System.out.println("("+name+") ht.get(this) returned ProvisionNode");
logger = factory.makeNewLoggerInstance(name);
logger.setHierarchy(this);
ht.put(key, logger);
updateChildren((ProvisionNode) o, logger);
updateParents(logger);
return logger;
}
else {
// It should be impossible to arrive here
return null; // but let's keep the compiler happy.
}
}
}
其實我們創建的Logger,都會存放到一個 命名爲ht 的HashTable中。最關鍵的是updateParents(logger);這個方法,於是我們進一步跟蹤這個方法,發現,如果一個Looger找不到父Logger,那麼
if(!parentFound)
cat.parent = root;
會將它的parent設爲rootLogger。 這個方法的具體代碼,我就不貼了,大家可以自己去看。那麼rootLogger的東西(Appender,Layout),是不是他可以直接繼承過來用列?
那麼關於rootLogger的繼承體系和依賴關係 ,我畫了一張比較簡單的uml草圖
(~~~~~~~~~~~~~~~~~~~圖片佔位,CSDN上傳圖片又掛掉了~~~~~~~~~~~~~~~~~~~~~~~)
上傳圖片試了N次都是掛掉了,只能簡單描述一下吧,或者大家進去看一下類結構吧
RootLogger繼承Logger,而Logger又繼承至Category。
Category裏面有個變量 AppenderAttachableImpl aai; 這個類就是封裝了一個 protected Vector appenderList;來存放Appender。
由此可以看出Logger和Appender是 1:N的關係
至於Appender的繼承體系太比較我,不描述了,大家自己去看。說了這麼多,大家再回頭對照看下配置文件,是不是一下子明白了很多?