(一)幾個基本概念java
Logger - 日誌寫出器,供程序員輸出日誌信息
Appender - 日誌目的地,把格式化好的日誌信息輸出到指定的地方去
ConsoleAppender - 目的地爲控制檯的Appender
FileAppender - 目的地爲文件的Appender
RollingFileAppender - 目的地爲大小受限的文件的Appender
Layout - 日誌格式化器,用來把程序員的logging request格式化成字符串
PatternLayout - 用指定的pattern格式化logging request的Layout程序員
日誌級別web
每一個Logger都被了一個日誌級別(log level),用來控制日誌信息的輸出。日誌級別從高到低分爲:
A:off 最高等級,用於關閉全部日誌記錄。
B:fatal 指出每一個嚴重的錯誤事件將會致使應用程序的退出。
C:error 指出雖然發生錯誤事件,但仍然不影響系統的繼續運行。
D:warm 代表會出現潛在的錯誤情形。
E:info 通常和在粗粒度級別上,強調應用程序的運行全程。
F:debug 通常用於細粒度級別上,對調試應用程序很是有幫助。
G:all 最低等級,用於打開全部日誌記錄。spring
上面這些級別是定義在org.apache.log4j.Level類中。Log4j只建議使用4個級別,優先級從高到低分別是 error,warn,info和debug。經過使用日誌級別,能夠控制應用程序中相應級別日誌信息的輸出。例如,若是使用了info級別,則應用程 序中全部低於info級別的日誌信息(如debug)將不會被打印出來。sql
(二)源碼解析apache
基本步驟:tomcat
一、LogManager類對log4j.xml或者log4j.properties文件進行解析(xml文件優先,若存在xml文件則不對properties文件進行解析)app
二、逐個解析配置文件中的logger對象,並將這些對象放入一個Hierarchy的實例中(Hierarchy類中定義了一個hashtable來存儲全部的logger)webapp
a、取得RootLoggeroop
b、取得RootLogger對應的Appender (必須在配置文件中指明rootLogger的Appender)
c、取得Appender的Layout,將Layout追加到Appender中
d、將Appender追加到RootLogger中
e、將RootLogger添加進Hierarchy中
f、依照上面abcde的步驟將其餘logger添加進Hierarchy中
三、經過LogManager.getLogger(String name)或LogManager.getLogger(Class clazz)獲取獲取到指定的logger對象(事實上,傳遞進去的clazz也會被轉換爲字符串,不過要加上它的包名,如Test.Class-->com.test.Test)
a、經過傳遞進來的參數,判斷Hierarchy中是否已經含有此logger;(經過hashtable.get(key)獲取再判斷)
b、若該logger已經存在於Hierarchy中則將Hierarchy的logger返回
c、經過new Logger(String name)或new Logger(Class clazz)建立一個新的logger,設置這個logger的parent屬性,將此對象放入Hierarchy中並返回
//爲使代碼更簡潔明瞭,此段代碼我作了部分的刪減 private final void updateParents(Logger cat) { String name = cat.name; int length = name.length(); boolean parentFound = false; //由此段代碼可看出,這裏是根據包名斷定父節點的 for(int i = name.lastIndexOf('.', length - 1); i >= 0; i = name.lastIndexOf('.', i - 1)) { String substr = name.substring(0, i); CategoryKey key = new CategoryKey(substr); Object o = ht.get(key); if(o instanceof Category) { parentFound = true; cat.parent = (Category)o; break; } } //若沒找到父節點,則默認其父節點爲root if(!parentFound) cat.parent = root; }
四、經過logger的error(Object message, Throwable t)等方法(包括error,warn,info,debug)輸出日誌
a、判斷你要輸入的日誌級別是否高於該logger的level,若高於,則調用該logger的各個appender輸出日誌,若低於,則不進行輸出
注:若是沒有設置日誌記錄器(Logger)的級別,那麼它將會繼承最近的祖先的級別。
//獲取有效的日誌級別 //若該logger沒有定義level則使用其最近的祖先定義的level // Loger類繼承於Category public Level getEffectiveLevel() { for(Category c = this; c != null; c = c.parent) if(c.level != null) return c.level; return null; }
b、逐個調用該logger的appender輸出日誌
注:appender中也有級別限制,只有知足這個限制的日誌才能被輸出
c、 若該logger的additive屬性爲true,則調用父類的appender輸出日誌(additive屬性默認爲true)
public void callAppenders(LoggingEvent event) { category; JVM INSTR monitorexit ; continue; exception; throw exception; int writes = 0; //由如下代碼能夠看出若是additive屬性爲true則會逐層調用祖先的appender,直到root或者遇到additive屬性爲false的祖先 label0: for(Category c = this; c != null; c = c.parent) { label1: { synchronized(c) { if(c.aai != null) writes += c.aai.appendLoopOnAppenders(event); if(c.additive) break label1; } break label0; } } if(writes == 0) repository.emitNoAppenderWarning(this); return; }
(三)關於日記文件的備份
RollFileAppender
當日志文件大小超出了指定值時,log4j會將咱們以前存放在指定文件(如:normal.log)的日誌備份(即轉移)到一個新的日記文件(如normal_1.log)
DailyRollFileAppender
按指定的時間頻道,將以前存放在指定文件(如:normal.log)的日誌備份(即轉移)到一個新的日誌文件(如 normal_20120101.log)
在DailyRollingFileAppender中能夠指定monthly(每個月)、 weekly(每週)、daily(天天)、half-daily(每半天)、hourly(每小時)和minutely(每分鐘)六個頻度,這是經過爲 DatePattern選項賦予不一樣的值來完成的。DatePattern選項的有效值爲:
'.'yyyy-MM,對應monthly(每個月)
'.'yyyy-ww,對應weekly(每週)
'.'yyyy-MM-dd,對應daily(天天)
'.'yyyy-MM-dd-a,對應half-daily(每半天)
'.'yyyy-MM-dd-HH,對應hourly(每小時)
'.'yyyy-MM-dd-HH-mm,對應minutely(每分鐘)
PS:以上種方式是分別經過大小和時間進行日誌的備份,若是在實際項目開發中須要根據實際條件指定新的備份方式,咱們能夠參照log4j.jar中的RollFileAppender和DailyRollFileAppender的源碼,本身進行編寫一個Appender類就好了
(四)SSH與Log4j整合的好處
1.配置文件(log4j.properties或者log4j.xml)能夠不用放在class-path中,而可讓咱們指定,通常咱們會把該配置文件與項目中的其餘配置文件放在一塊兒
在web.xml中的詳細設定以下:
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>WEB-INF/log4j.properties</param-value>
</context-param>
2. 日誌文件的存放路徑能夠用相對路徑指定,好比說放在當前項目下的WEB-INF/logs文件夾中,而不須要用絕對路徑定義日誌存放位置
<context-param> <param-name>webAppRootKey</param-name> <param-value>web.root</param-value> </context-param> <!-- 須要添加spring-web.jar包-->
<listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener>
這裏涉及到了另外一個知識點,Spring經過 org.springframework.web.util.WebAppRootListener 這個監聽器將咱們當前項目的所在路徑配置進了系統的環境變量中,
也就是至關於在系統環境變量中配置了一個叫「web.root」的變量,這個變量指向了咱們當前項目的物理路徑。這樣,咱們在項目中就能夠經過System.getProperty("web.root")來獲取當前項目的物理路徑,同時,咱們也能夠在log4j.properties 裏這樣定義logfile位置log4j.appender.logfile.File=${webapp.root}/WEB-INF/logs/mylog.log
若是在web.xml中已經配置了 org.springframework.web.util.Log4jConfigListener
這個監聽器,則不須要配置WebAppRootListener了。由於Log4jConfigListener已經包含了WebAppRootListener的功能
部署在同一容器中的Web項目,要配置不一樣的<param-value>,不能重複,由於這個值事實上就是至關於path環境變量中的一個key,key天然不能重複。
三、動態的改變記錄級別和策略,即修改log4j.properties,不須要重啓Web應用,這須要在web.xml中設置自動掃描log4j文件的間隔(每隔一段時間刷新一次配置)
在web.xml中的配置以下:
<param-name>log4jRefreshInterval</param-name>
<param-value>60000</param-value>
</context-param>
補充擴展
正式上線的項目中應儘可能少用system.out.println(),由於該語句會把內容打印到tomcat默認的日誌文件中,不便於日誌的管理,並且容易佔據系統資源;
hibernate自帶的showsql功能也是默認將對應的sql語句輸出到tomcat默認的日誌文件中,hibernate的日誌輸出也是依賴於log4j實現的,因此咱們也能夠經過配置將該日誌輸出控制起來。