log4j源码阅读(一)之Logger
概述
log4j是一款非常方便而且強大的開源日志項目,在經過簡單的配置后,可以達到相當不錯的效果。
頭腦一熱決定對log4j的源碼進行一定的閱讀,其初衷是希望通過源碼的閱讀,提高寫代碼的能力。
?
log4j其核心概念可分為:
logger 日志接收器,即程序員在自己的代碼中使用如logger.error(...)的形式記錄日志。
append 日志寫出器,將logger接收到的日志信息寫入到各種設備,如文件,控制臺。
layout 日志格式化器,將輸入的日志先進行格式化,再輸出。
?
log4j將日志分為了幾個級別,由低到高分別為:DEBUG < INFO < WARN < ERROR < FATAL。
若低級別的日志能輸出,則比之級別高的日志也能輸出。
?
UML
第一次畫UML,多少有點緊張...
主要是希望通過UML圖能夠反映出logger的組織結構,及logger與其他組件的關系,因此很多因素被忽
略了。之后的說明都將對照著這個圖來。
?
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:作為自己的標識,在工廠方法中通過name來new出Logger實例。
level:每個Logger都有一個Level屬性,在輸出日志時,將通過方法getEffectiveLevel()獲取到本身
有效的Level,以確定是否可以輸出該條日志。稍后將詳細說明級別判斷流程。
parent:每個Logger都有個父結點,父子關系將在LoggerRepository中生成。正因為有了父子關系
所以在getEffectiveLevel方法中,實際上是向父結點方向遍歷,找到第一個不為空的Level。也就是
說,若不明確指定當前結點的level,則使用父結點的level,在之后LoggerRepository的介紹時,會
知道,有一個公共的父結點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:每個Logger可關聯多個Appender,接收的日志被依次輸出到每一個Appender。Logger將對Appender
的管理代理到了AppenderAttachableImpl,例如addAppender操作實際上是交給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等,都是用戶調用記錄日志方法。在判斷級別之后,將日志轉遞給Appender輸出
將日志轉化為LoggingEvent后,調用callAppenders向父結點方向遍歷,每個一結點都調用AppenderAttachableImpl
的方法輸出日志。實際上,在AppenderAttachableImpl中,將遍歷當前結點所關聯的所有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進行管理的倉庫,提供工廠方法getLogger(name)來創建新的logger實例。并將所有創建
的實例,在邏輯上組織成樹結構(logger的parent字段)。每個實例都有一個父結點存在。而LoggerRepository本身包
含一個RootLogger成員,是所有logger的共享祖先結點。也正是因為這個樹結構的存在,在形如getEffectiveLevel等操
作中,都會向父結點方向遍歷。所有子結點在沒有特別配置過時,都使用父結點的屬性(級別,輸出器)。
LoggerRepository本身也帶有level屬性,在記錄日志時,首先判斷的是級別是否超過倉庫的級別。該屬性默認為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將接收到的日志轉化成LoggingEvent,通過callAppender方法,交由代理AppenderAttachableImpl完成輸出。
AppenderAttachableImpl在遍歷所有關聯的Appender后,依次調用其doAppender方法進行輸出。而每一個Appender
也是有優先級概念的,其他和logger的level相同。因此在Appender執行輸出的時候,也是要優先判斷級別是否符合當
前Appender的設置。另外,Appender提供一些過濾器,可對輸出進行進一步的控制(應該是個簡單的職責鏈模式)。
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當然輸出之前,還需要通過Layout.format(...)格式化后再輸出。
?
LogMagager
這是一個用戶接口,是上面UML圖中沒有體現出來的內容。其主要是提供一些工廠,和一些默認的配置。如默認的倉庫。
同時還提供一些工廠方法,如getLogger,內容只是將轉交給其他模塊實現。
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將最終轉化成name。
public static Logger LOG = LogManager.getLogger(TestMain.class); View Code?
小結
閱讀源碼時,僅對大框架做了一些了解,如Appender,layout的實現細節,都簡單略過了。log4j雖然強大,但終歸只是記錄
日志,源碼不復雜,有興趣的可以自己閱讀一次。對于我來說,這也僅是一個源碼閱讀的開始吧。希望以后能學習到更多
優秀的設計。
?
轉載于:https://www.cnblogs.com/fullstack/p/3911187.html
總結
以上是生活随笔為你收集整理的log4j源码阅读(一)之Logger的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Entity Framework 与 L
- 下一篇: js获取最近几天的日期(转载)