咱們在寫日誌的時候首先要獲取logger,在每個使用log4j的項目都有不少個地方要獲取logger,這些logger是真實的被實例化的Logger對象,他們有可能被分散在無數不一樣的類中,日誌體系結構講的是這些logger對象是如何組織的,他們之間又有什麼樣的關係。java
咱們舉個具體的實例來看看,假設個人項目包結構以下:app
項目結構分佈式
說明一下:com.flu.jdk包下面有兩個類分別是LogTest1和LogTest2,而後在包com.flu包下面有一個LogTest3類,很顯然,com.flu.jdk包是com.flu包的子包。假設咱們在這三個類中分別經過LogManager.getLogger(xxx.class)獲取三個logger實例,他們分別是logger一、logger2和logger3,咱們將要討論這三個logger的關係。性能
值得注意的是log4j的日誌體系中,有一個特殊的日誌對象叫作root(根),他是始終存在的,假設咱們首先獲取logger實例,log4j將構造下面這樣一個圖形(我不能把它叫作樹):this
當只有logger1的時候spa
當咱們獲取logger2實例的時候,這個圖將變成:debug
當加入logger2日誌實例時結構圖設計
當咱們獲取logger3實例的時候,這個圖又變了一個樣,以下:日誌
當加入logger3日誌實例以後code
僅僅才三個日誌實例,圖就搞的略複雜,可想log4j應用中,將會有無數的日誌實例按照這個規律組成紛繁複雜的有向圖結構,雖然看起來雜亂,可是又規律。那麼問題來了,這樣的結構有什麼用呢?下一節咱們將會看到這種結構對於日誌配置繼承的影響。
在往下面看以前咱們先來看看log4j對日誌級別的定義:
public final static int OFF_INT = Integer.MAX_VALUE; public final static int FATAL_INT = 50000; public final static int ERROR_INT = 40000; public final static int WARN_INT = 30000; public final static int INFO_INT = 20000; public final static int DEBUG_INT = 10000; //public final static int FINE_INT = DEBUG_INT; public final static int ALL_INT = Integer.MIN_VALUE;
很顯然,log4j的日誌級別有下面的關係:
OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL
log4j在寫日誌的時候只有噹噹前寫日誌的級別大於等於當前日誌實例的配置級別的時候,日誌寫操做才生效,好比當前日誌實例的配置級別爲INFO,那麼log.info會寫成功,而log.debug則不會寫。
咱們來看看一句簡單的log.info("this is log message")的背後,先來看看一段源代碼:
public void info(Object message) { if(repository.isDisabled(Level.INFO_INT)) return; if(Level.INFO.isGreaterOrEqual(this.getEffectiveLevel())) forcedLog(FQCN, Level.INFO, message, null); } public boolean isDisabled(int level) { return thresholdInt > level; }
首先看看當前寫的日誌級別是否被禁止的,默認的狀況下thresholdInt爲ALL,所以INFO的級別顯然比ALL大,所以下面會繼續看看INFO的級別是否大於等於當前日誌實例生效的級別,this.getEffectiveLevel()獲取的實例是什麼呢?咱們繼續看看代碼:
public Level getEffectiveLevel() { for(Category c = this; c != null; c=c.parent) { if(c.level != null) return c.level; } return null; // If reached will cause an NullPointerException. }
當前日誌生效的級別邏輯爲首先看看當前日誌實例是否有配置級別,若是沒有,那麼就繼續找當前日誌實例的parent節點,按照上一節中所表述的,若是當前日誌的日誌級別沒有配置,當找到root的日誌級別,並根據root的日誌級別來判定是否繼續進行日誌寫。這裏體現了日誌級別的繼承關係,其實不單單是日誌級別,日誌其餘相關的配置也會基於這種繼承的特性,好比appender組件等。
瞭解Log4j的日誌體系結構以及日誌級別配置的繼承特性以後,咱們如今應該比較清楚項目中應該如何配置了。以Log4j.xml配置文件爲例子,知足基本需求咱們只須要配置root這個日誌實例的日誌級別便可,以下:
<root> <level value="INFO" /> <appender-ref ref="CONSOLE" /> </root>
上面配置了root日誌實例的日誌級別爲INFO,若是獲取按照必定規範(當前類的權限定名做爲日誌實例名),那麼咱們能夠保證全部的日誌實例將繼承root所配置的日誌級別。
上面的配置略粗糙,假如咱們想爲不一樣的模塊給予不一樣的配置怎麼辦呢?最多見的是業務日誌與中間件日誌,好比咱們的業務業務包名爲com.dianping.biz,而咱們的rpc組件的包名字爲com.dianping.pigeon,則咱們可使用下面方法給予不一樣的模塊不一樣的配置:
<!--業務日誌配置--> <category name="com.dianping.biz"> <level value="INFO" /> <appender-ref ref="CONSOLE" /> </category> <!--pigeon組件日誌配置--> <category name="com.dianping.pigeon"> <level value="DEBUG" /> <appender-ref ref="CONSOLE" /> </category>
經過上面的配置,咱們能夠指定com.dianping.biz包下面全部類獲取的logger都繼承name爲com.dianping.biz的日誌配置,而com.dianping.pigeon包下面的全部類獲取的logger都繼承name爲com.dianping.pigeon的日誌篇日誌。不過一般設計良好的中間件都定製了日誌配置以確保中間件日誌與業務日誌隔離。
昨天有個同事對log4j進行了一些分享,會上聽的意猶未盡所以課下忍不住扒一扒log4j的內褲,日誌做爲java應用的一項重要內容,其不單單包括日誌如何寫、以什麼格式寫、以及日誌寫到哪裏的問題,還包括性能、擴展性、分佈式、日誌實時分析等方面問題,本文在介紹log4j日誌體系的基礎之上稍微聊一下項目應用於配置隔離相關內容,若是讀者有興趣能夠深刻研究,一定收貨滿滿。