當前位置:
首頁 > 最新 > log4j源碼粗讀

log4j源碼粗讀

幾乎每個大的應用都包括他自己的日誌或跟蹤API

日誌也有它自己的缺點,它會降低應用的運行速度;如果太過詳細,它會導致屏幕過閃;為了減輕這些擔心,log4j被設計成是可依賴的,快速的和可擴展的。log4j很少關注某個應用,因而會努力的做到簡單易懂,易使用。

三個主要組件Loggers,Appenders,Layouts

這三個組件相互合作以使開發者根據類型和級別記錄消息,並能夠在運行時控制這些消息的格式及在何處記錄。

Logger

logger的層級

根據開發者不同的選擇區域,logging的空間是被分類的,基於此,我們之前選擇category作為這個包的中心概念。然而在1.2的版本後,logger類替換了category類,那些早期的版本logger類可以近似看成category類的另一個名字。

Logger是命名的實體,是大小寫敏感的,並且遵循下面的層級規則,按點分割,點號後面的被認為是前面子孫。如com.foo是com.foo.Bar的父類

根logger處在整個logger層級的頂部,它有兩個例外

1. 它總是存在

2. 它不能通過名字獲取到,通過Logger.getRootLogger獲取

所有其他logger都通過Logger.getLogger方法獲取,這個方法獲取一個logger的名字作為參數

logger共有

TRACE,

DEBUG,

INFO,

WARN,

ERROR and

FATAL

這幾個可能的級別,當然也可以自己繼承Level類定義自己的log級別,雖然我們不鼓勵這麼做。

如果一個logger沒有分配級別,它會繼承它最近的祖先的級別。為保證所有logger都有級別,rootlogger總是有一個分配的級別(默認為debug)

用相同的name參數使用getLogger方法總是會返回同一個logger對象

與正常邏輯相反的是log4j可以以任何順序構造,比如,一個parent的logger可以被找到並鏈接到它的子孫即使它在它的子孫後面實例化

Appender

log4j允許logging列印到多個地方,每一個輸出的地方叫一個appender,如console,文件,gui組件,socket等等。

一個logger可以綁定多個appender

appender也會繼承,比如rootLogger綁定了console的appender,則所有子孫都會綁定console。可以通過設定additivity flag關掉這種繼承性

Layout

layout用於定製化輸出格式

PatternLayout類讓我們可以使用類似printf的方式輸出配置日誌格式

ObjectRender用於將對象渲染成字元串,對象的渲染遵循類的層級。例如,假定orange是水果,如果你註冊了一個FruitRender,則所有包含orange的fruit將會使用FruitRender渲染,除非你特殊註冊一個orange的render。Object Render必須實現ObjectRender介面。

配置文件可以寫成xml或kv格式的java properties

默認初始化過程

log4j不對環境做任何假定,尤其是,沒有默認的appender。

Logger的靜態初始器將會嘗試自動配置

1. 將log4j.defaultInitOverride這個system property設置成任何一個非false的值,將導致跳過默認的初始化過程

2. 設置resource變數的值為log4j.configuration system property,如果這個屬性未定義,則resource將會被設置成默認值「log4j.properties」

3. 嘗試將resource變數轉換為一個URL

4. 如果失敗,則嘗試通過getResource方法從classpath搜索,並返回一個URL。注意log4j.properties是一個合法的URL格式。getResource方法搜索的順序是,當前線程上下文對應classloader的resource,載入這個類的classloader。

5. 如果沒有URL可以找到,就放棄默認初始化過程。否則從這個url配置log4j

Tomcat下的默認初始化

默認的初始化過程在web-server環境下仍然是有用的。在Tomcat3.x/4.x,你應該將log4j.properties放在WEB-INF/classes目錄下,這樣log4j將會找到配置文件並進行初始化。

也可以設置system property log4j.configuration在Tomcat啟動前。Tomcat3.x用TOMCAT_OPTS 環境變數設置,4.x用CATALINA_OPTS

NDC

Nested diagnostic contexts, 嵌套的上下文診斷,線程信息保存是基於Stack

NDC是線程級別的,它的所有操作都只是針對一個線程,不會對其他線程的NDC產生影響。例如,一個servlet可以為每個客戶端請求創建一個NDC,包含客戶端的hostname和其他請求內的信息。

只要通過調用NDC.push就開業創建一個NDC,進入一個上下文。

當調用NDC.pop就離開這個上下文

當退出一個線程要確保調用了NDC.remove方法,以釋放內存,避免泄露

不需要每個push都對應一個pop,但不能真正應用中的上下文和NDC當前設置的上下文不匹配

有一個機制可以使你有時粗心的忘記在退出線程前調用remove方法,一個線程可以繼承另一個線程(可能是父線程)的NDC,通過調用inherit方法,是同一個NDC

一個線程可以通過cloneStack方法獲取一個NDC的一個拷貝(clone),不是同一個對象

NDC內沒有使用synchronized關鍵字同步,這在多線程的環境下看起來可能是危險的,所有線程共享一個hashtable,但hashtable是線程安全的。

REAP_THRESHOLD是在我們調用lazyRemove方法前調用push方法最多的次數,lazyRemove方法不會被很頻繁的調用,我們因此避免太頻繁的創造Enumeration

hashtable的key就是當前的線程對象。

lazyRemove方法:這個方法中會獲取hashtable的key的Enumeration(相當於iterator),將沒有主動調用remove的線程加入到一個vector中,然後相當於調用這些線程的,從hashtable中刪掉。但是我們最多處理4個線程,ht中死亡的線程比例越高,remove調用的可能性越高

MDC

MDC類似NDC,除了MDC是基於一個map,NDC是一個Stack

java2的版本是一個ThreadLocalMap,java1是不支持的。map的value是一個hashtable

Logger的log方法粗略過程

1. 檢查priority

2. 獲取appenderList

3. 對每個appender添加這個logEvent

4. 每個Appender檢查自己的filter

5. 通過layout格式化輸出

6. 執行這個log的輸出

了解上面這些再看源碼其實就很好理解了,具體代碼就不細列出了,文章對應版本是1.2,maven依賴如下

log4j

log4j

1.2.17

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 全球大搜羅 的精彩文章:

當上導演的,都是持續寫劇本的人——讀李安《十年一覺電影夢》之一
米開朗基羅·皮斯特萊托:一錘掄下的勇氣和責任

TAG:全球大搜羅 |