log4j源码阅读(一)之Logger
概述
log4j是一款非常方便而且強(qiáng)大的開源日志項(xiàng)目,在經(jīng)過簡單的配置后,可以達(dá)到相當(dāng)不錯(cuò)的效果。
頭腦一熱決定對log4j的源碼進(jìn)行一定的閱讀,其初衷是希望通過源碼的閱讀,提高寫代碼的能力。
?
log4j其核心概念可分為:
logger 日志接收器,即程序員在自己的代碼中使用如logger.error(...)的形式記錄日志。
append 日志寫出器,將logger接收到的日志信息寫入到各種設(shè)備,如文件,控制臺。
layout 日志格式化器,將輸入的日志先進(jìn)行格式化,再輸出。
?
log4j將日志分為了幾個(gè)級別,由低到高分別為:DEBUG < INFO < WARN < ERROR < FATAL。
若低級別的日志能輸出,則比之級別高的日志也能輸出。
?
UML
第一次畫UML,多少有點(diǎn)緊張...
主要是希望通過UML圖能夠反映出logger的組織結(jié)構(gòu),及l(fā)ogger與其他組件的關(guān)系,因此很多因素被忽
略了。之后的說明都將對照著這個(gè)圖來。
?
Logger
Logger繼承自Category,而在Category有這樣的說明
This class has been deprecated and replaced by the {@link Logger} <em>subclass</em></b></font>. It will be kept around to preserve backward compatibility until mid 2003.name:作為自己的標(biāo)識,在工廠方法中通過name來new出Logger實(shí)例。
level:每個(gè)Logger都有一個(gè)Level屬性,在輸出日志時(shí),將通過方法getEffectiveLevel()獲取到本身
有效的Level,以確定是否可以輸出該條日志。稍后將詳細(xì)說明級別判斷流程。
parent:每個(gè)Logger都有個(gè)父結(jié)點(diǎn),父子關(guān)系將在LoggerRepository中生成。正因?yàn)橛辛烁缸雨P(guān)系
所以在getEffectiveLevel方法中,實(shí)際上是向父結(jié)點(diǎn)方向遍歷,找到第一個(gè)不為空的Level。也就是
說,若不明確指定當(dāng)前結(jié)點(diǎn)的level,則使用父結(jié)點(diǎn)的level,在之后LoggerRepository的介紹時(shí),會
知道,有一個(gè)公共的父結(jié)點(diǎn)RootLogger。
1 /** 2 Starting from this category, search the category hierarchy for a 3 non-null level and return it. Otherwise, return the level of the 4 root category. 5 6 <p>The Category class is designed so that this method executes as 7 quickly as possible. 8 */ 9 public 10 Level getEffectiveLevel() { 11 for(Category c = this; c != null; c=c.parent) { 12 if(c.level != null) { 13 return c.level; 14 } 15 } 16 return null; // If reached will cause an NullPointerException. 17 } View Codeaai:每個(gè)Logger可關(guān)聯(lián)多個(gè)Appender,接收的日志被依次輸出到每一個(gè)Appender。Logger將對Appender
的管理代理到了AppenderAttachableImpl,例如addAppender操作實(shí)際上是交給aii處理的。
/**Add <code>newAppender</code> to the list of appenders of thisCategory instance.<p>If <code>newAppender</code> is already in the list ofappenders, then it won't be added again.*/synchronizedpublicvoid addAppender(Appender newAppender) {if(aai == null) {aai = new AppenderAttachableImpl();}aai.addAppender(newAppender);repository.fireAddAppenderEvent(this, newAppender);} View Code而在AppenderAttachableImpl中則僅將Appender添加到Vector
/**Attach an appender. If the appender is already in the list inwon't be added again.*/publicvoid addAppender(Appender newAppender) {// Null values for newAppender parameter are strictly forbidden.if(newAppender == null) {return;}if(appenderList == null) {appenderList = new Vector(1);}if(!appenderList.contains(newAppender)) {appenderList.addElement(newAppender);}} View Codedebug:類似的還有info,error等,都是用戶調(diào)用記錄日志方法。在判斷級別之后,將日志轉(zhuǎn)遞給Appender輸出
將日志轉(zhuǎn)化為LoggingEvent后,調(diào)用callAppenders向父結(jié)點(diǎn)方向遍歷,每個(gè)一結(jié)點(diǎn)都調(diào)用AppenderAttachableImpl
的方法輸出日志。實(shí)際上,在AppenderAttachableImpl中,將遍歷當(dāng)前結(jié)點(diǎn)所關(guān)聯(lián)的所有Appender,依次輸出。
publicvoid debug(Object message) {if(repository.isDisabled(Level.DEBUG_INT)) {return;}if(Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel())) {forcedLog(FQCN, Level.DEBUG, message, null);}} View Code /**This method creates a new logging event and logs the eventwithout further checks. */protectedvoid forcedLog(String fqcn, Priority level, Object message, Throwable t) {callAppenders(new LoggingEvent(fqcn, this, level, message, t));} View Code publicvoid callAppenders(LoggingEvent event) {int writes = 0;for(Category c = this; c != null; c=c.parent) {// Protected against simultaneous call to addAppender, removeAppender,...synchronized(c) {if(c.aai != null) {writes += c.aai.appendLoopOnAppenders(event);}if(!c.additive) {break;}}}if(writes == 0) {repository.emitNoAppenderWarning(this);}} View Code?
LoggerRepository
LoggerRepository是對logger進(jìn)行管理的倉庫,提供工廠方法getLogger(name)來創(chuàng)建新的logger實(shí)例。并將所有創(chuàng)建
的實(shí)例,在邏輯上組織成樹結(jié)構(gòu)(logger的parent字段)。每個(gè)實(shí)例都有一個(gè)父結(jié)點(diǎn)存在。而LoggerRepository本身包
含一個(gè)RootLogger成員,是所有l(wèi)ogger的共享祖先結(jié)點(diǎn)。也正是因?yàn)檫@個(gè)樹結(jié)構(gòu)的存在,在形如getEffectiveLevel等操
作中,都會向父結(jié)點(diǎn)方向遍歷。所有子結(jié)點(diǎn)在沒有特別配置過時(shí),都使用父結(jié)點(diǎn)的屬性(級別,輸出器)。
LoggerRepository本身也帶有l(wèi)evel屬性,在記錄日志時(shí),首先判斷的是級別是否超過倉庫的級別。該屬性默認(rèn)為ALL。
publicvoid debug(Object message) {if(repository.isDisabled(Level.DEBUG_INT)) {return;} ... View Code publicboolean isDisabled(int level) {return thresholdInt > level;} View Code?
Appender
logger將接收到的日志轉(zhuǎn)化成LoggingEvent,通過callAppender方法,交由代理AppenderAttachableImpl完成輸出。
AppenderAttachableImpl在遍歷所有關(guān)聯(lián)的Appender后,依次調(diào)用其doAppender方法進(jìn)行輸出。而每一個(gè)Appender
也是有優(yōu)先級概念的,其他和logger的level相同。因此在Appender執(zhí)行輸出的時(shí)候,也是要優(yōu)先判斷級別是否符合當(dāng)
前Appender的設(shè)置。另外,Appender提供一些過濾器,可對輸出進(jìn)行進(jìn)一步的控制(應(yīng)該是個(gè)簡單的職責(zé)鏈模式)。
publicsynchronized void doAppend(LoggingEvent event) {if(closed) {LogLog.error("Attempted to append to closed appender named ["+name+"].");return;}if(!isAsSevereAsThreshold(event.getLevel())) {return;}Filter f = this.headFilter;FILTER_LOOP:while(f != null) {switch(f.decide(event)) {case Filter.DENY: return;case Filter.ACCEPT: break FILTER_LOOP;case Filter.NEUTRAL: f = f.getNext();}}this.append(event); } View Code當(dāng)然輸出之前,還需要通過Layout.format(...)格式化后再輸出。
?
LogMagager
這是一個(gè)用戶接口,是上面UML圖中沒有體現(xiàn)出來的內(nèi)容。其主要是提供一些工廠,和一些默認(rèn)的配置。如默認(rèn)的倉庫。
同時(shí)還提供一些工廠方法,如getLogger,內(nèi)容只是將轉(zhuǎn)交給其他模塊實(shí)現(xiàn)。
static {// By default we use a DefaultRepositorySelector which always returns 'h'.Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG));repositorySelector = new DefaultRepositorySelector(h); } View Code因此,用戶可像通過LogManager使用logger,TestMain.class將最終轉(zhuǎn)化成name。
public static Logger LOG = LogManager.getLogger(TestMain.class); View Code?
小結(jié)
閱讀源碼時(shí),僅對大框架做了一些了解,如Appender,layout的實(shí)現(xiàn)細(xì)節(jié),都簡單略過了。log4j雖然強(qiáng)大,但終歸只是記錄
日志,源碼不復(fù)雜,有興趣的可以自己閱讀一次。對于我來說,這也僅是一個(gè)源碼閱讀的開始吧。希望以后能學(xué)習(xí)到更多
優(yōu)秀的設(shè)計(jì)。
?
轉(zhuǎn)載于:https://www.cnblogs.com/fullstack/p/3911187.html
總結(jié)
以上是生活随笔為你收集整理的log4j源码阅读(一)之Logger的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Entity Framework 与 L
- 下一篇: gVIM+zencoding快速开发HT