log4j的日誌級別與使用

1、日誌的級別:

我們現在要調用logger的方法,不過在這個Logger對象中,有很多方法,所以要先了解log4j的日誌級別,log4j規定了默認的幾個級別:trace<debug<info<warn<error<fatal等。這裏要說明一下:

1)級別之間是包含的關係,意思是如果你設置日誌級別是trace,則大於等於這個級別的日誌都會輸出。

2)基本上默認的級別沒多大區別,就是一個默認的設定。你可以通過它的API自己定義級別。你也可以隨意調用這些方法,不過你要在配置文件裏面好好處理了,否則就起不到日誌的作用了,而且也不易讀,相當於一個規範,你要完全定義一套也可以,不用沒多大必要。

3)這不同的級別的含義大家都很容易理解,這裏就簡單介紹一下:

trace: 是追蹤,就是程序推進以下,你就可以寫個trace輸出,所以trace應該會特別多,不過沒關係,我們可以設置最低日誌級別不讓他輸出。

debug: 調試麼,我一般就只用這個作爲最低級別,trace壓根不用。是在沒辦法就用eclipse或者idea的debug功能就好了麼。

info: 輸出一下你感興趣的或者重要的信息,這個用的最多了。

warn: 有些信息不是錯誤信息,但是也要給程序員的一些提示,類似於eclipse中代碼的驗證不是有error 和warn(不算錯誤但是也請注意,比如以下depressed的方法)。

error: 錯誤信息。用的也比較多。

fatal: 級別比較高了。重大錯誤,這種級別你可以直接停止程序了,是不應該出現的錯誤麼!不用那麼緊張,其實就是一個程度的問題。

 2、日誌調用:

這裏隨便寫個類,調用就是這麼簡單,log4j的核心在配置文件上。

[java] view plain copy
  1. import org.apache.logging.log4j.Level;  
  2. import org.apache.logging.log4j.LogManager;  
  3. import org.apache.logging.log4j.Logger;  
  4. public class Hello {  
  5.     static Logger logger = LogManager.getLogger(Hello.class.getName());  
  6.     public boolean hello() {  
  7.         logger.entry();   //trace級別的信息,單獨列出來是希望你在某個方法或者程序邏輯開始的時候調用,和logger.trace("entry")基本一個意思  
  8.         logger.error("Did it again!");   //error級別的信息,參數就是你輸出的信息  
  9.         logger.info("我是info信息");    //info級別的信息  
  10.         logger.debug("我是debug信息");  
  11.         logger.warn("我是warn信息");  
  12.         logger.fatal("我是fatal信息");  
  13.         logger.log(Level.DEBUG, "我是debug信息");   //這個就是制定Level類型的調用:誰閒着沒事調用這個,也不一定哦!  
  14.         logger.exit();    //和entry()對應的結束方法,和logger.trace("exit");一個意思  
  15.         return false;  
  16.     }  
  17. }  
如果沒有自定義配置文件,上面這個類在寫一個main方法,控制檯會輸入下面的樣子:

[java] view plain copy
  1. 19:09:40.256 [main] ERROR cn.lsw.base.log4j2.Hello - Did it again!  
  2. 19:09:40.260 [main] FATAL cn.lsw.base.log4j2.Hello - 我是fatal信息  
看到沒,只有>=ERROR的日誌輸出來了(這是因爲Log4j有一個默認的配置,它的日誌級別是ERROR,輸出只有控制檯)。如果我已經定義好了日誌,我把日誌級別改成了TRACE,輸出會變成下面這樣:

[java] view plain copy
  1. 19:11:36.941 TRACE cn.lsw.base.log4j2.Hello 12 hello - entry  
  2. 19:11:36.951 ERROR cn.lsw.base.log4j2.Hello 13 hello - Did it again!  
  3. 19:11:36.951 INFO  cn.lsw.base.log4j2.Hello 14 hello - 我是info信息  
  4. 19:11:36.951 DEBUG cn.lsw.base.log4j2.Hello 15 hello - 我是debug信息  
  5. 19:11:36.951 WARN  cn.lsw.base.log4j2.Hello 16 hello - 我是warn信息  
  6. 19:11:36.952 FATAL cn.lsw.base.log4j2.Hello 17 hello - 我是fatal信息  
  7. 19:11:36.952 DEBUG cn.lsw.base.log4j2.Hello 18 hello - 我是debug信息  
  8. 19:11:36.952 TRACE cn.lsw.base.log4j2.Hello 19 hello - exit  

所有的日誌都打印出來了,大家可以對照上面的代碼看一看。

3. 配置文件:

現在開始正題了。

本來以爲Log4J 2應該有一個默認的配置文件的,不過好像沒有找到,下面這個配置文件等同於缺省配置:

[html] view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>    
  2. <configuration status="OFF">    
  3.   <appenders>    
  4.     <Console name="Console" target="SYSTEM_OUT">    
  5.       <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>    
  6.     </Console>    
  7.   </appenders>    
  8.   <loggers>    
  9.     <root level="error">    
  10.       <appender-ref ref="Console"/>    
  11.     </root>    
  12.   </loggers>    
  13. </configuration>  

而我們只要把configuration>loggers>root的level屬性改爲trace,就可以輸出剛纔寫的所有信息了。相信用過Log4j的人對這個配置文件也不算陌生,Log4J傳統的配置一直是.properties文件,鍵值對的形式,那種配置方式很不好看,但是基本上我們從這個配置文件也能看到Log4J 1的影子,無非是appender了,layout之類的,含義也基本一樣的。

這裏不準備仔細的講配置文件,沒什麼必要,大家只要知道一些基本的配置就可以了。我這裏寫幾個配置文件,並且給了一定的註釋和講解,基本上可以用了。

 第一個例子:

[html] view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <configuration status="OFF">  
  3.     <appenders>  
  4.         <Console name="Console" target="SYSTEM_OUT">  
  5.             <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>  
  6.         </Console>  
  7.     </appenders>  
  8.     <loggers>  
  9.         <!--我們只讓這個logger輸出trace信息,其他的都是error級別-->  
  10.         <!--  
  11.         additivity開啓的話,由於這個logger也是滿足root的,所以會被打印兩遍。  
  12.         不過root logger 的level是error,爲什麼Bar 裏面的trace信息也被打印兩遍呢  
  13.         -->  
  14.         <logger name="cn.lsw.base.log4j2.Hello" level="trace" additivity="false">  
  15.             <appender-ref ref="Console"/>  
  16.         </logger>  
  17.         <root level="error">  
  18.             <appender-ref ref="Console"/>  
  19.         </root>  
  20.     </loggers>  
  21. </configuration>  

先簡單介紹一下下面這個配置文件。

1)根節點configuration,然後有兩個子節點:appenders和loggers(都是複數,意思就是可以定義很多個appender和logger了)(如果想詳細的看一下這個xml的結構,可以去jar包下面去找xsd文件和dtd文件)

2)appenders:這個下面定義的是各個appender,就是輸出了,有好多類別,這裏也不多說(容易造成理解和解釋上的壓力,一開始也未必能聽懂,等於白講),先看這個例子,只有一個Console,這些節點可不是隨便命名的,Console就是輸出控制檯的意思。然後就針對這個輸出設置一些屬性,這裏設置了PatternLayout就是輸出格式了,基本上是前面時間,線程,級別,logger名稱,log信息等,差不多,可以自己去查他們的語法規則。

3)loggers下面會定義許多個logger,這些logger通過name進行區分,來對不同的logger配置不同的輸出,方法是通過引用上面定義的logger,注意,appender-ref引用的值是上面每個appender的name,而不是節點名稱。

這個例子爲了說明什麼呢?我們要說說這個logger的name(名稱)了(前面有提到)。

4、 name的機制:

 我們這裏看到了配置文件裏面是name很重要,沒錯,這個name可不能隨便起(其實可以隨便起)。這個機制意思很簡單。就是類似於java package一樣,比如我們的一個包:cn.lsw.base.log4j2。而且,可以發現我們前面生成Logger對象的時候,命名都是通過 Hello.class.getName(); 這樣的方法,爲什麼要這樣呢? 很簡單,因爲有所謂的Logger 繼承的問題。比如 如果你給cn.lsw.base定義了一個logger,那麼他也適用於cn.lsw.base.lgo4j2這個logger。名稱的繼承是通過點(.)分隔的。然後你可以猜測上面loggers裏面有一個子節點不是logger而是root,而且這個root沒有name屬性。這個root相當於根節點。你所有的logger都適用與這個logger,所以,即使你在很多類裏面通過  類名.class.getName()  得到很多的logger,而且沒有在配置文件的loggers下面做配置,他們也都能夠輸出,因爲他們都繼承了root的log配置。

我們上面的這個配置文件裏面還定義了一個logger,他的名稱是 cn.lsw.base.log4j2.Hello ,這個名稱其實就是通過前面的Hello.class.getName(); 得到的,我們爲了給他單獨做配置,這裏就生成對於這個類的logger,上面的配置基本的意思是隻有cn.lsw.base.log4j2.Hello 這個logger輸出trace信息,也就是他的日誌級別是trace,其他的logger則繼承root的日誌配置,日誌級別是error,只能打印出ERROR及以上級別的日誌。如果這裏logger 的name屬性改成cn.lsw.base,則這個包下面的所有logger都會繼承這個log配置(這裏的包是log4j的logger name的“包”的含義,不是java的包,你非要給Hello生成一個名稱爲“myhello”的logger,他也就沒法繼承cn.lsw.base這個配置了。

那有人就要問了,他不是也應該繼承了root的配置了麼,那麼會不會輸出兩遍呢?我們在配置文件中給瞭解釋,如果你設置了additivity="false",就不會輸出兩遍,否則,看下面的輸出:

這裏要在加入一個類做對比: 

[java] view plain copy
  1. import org.apache.logging.log4j.LogManager;  
  2. import org.apache.logging.log4j.Logger;  
  3. public class Test {  
  4.   
  5.     private static Logger logger = LogManager.getLogger(Test.class.getName());  
  6.     public static void main(String[] args) {  
  7.         logger.trace("開始程序.");  
  8.         Hello hello= new Hello();  
  9. //        for (int i = 0; i < 10000;i++){  
  10.             if (!hello.hello()) {  
  11.                 logger.error("hello");  
  12.             }  
  13. //        }  
  14.         logger.trace("退出程序.");  
  15.     }  
  16. }  

這裏先把配置文件改一下方便對照,一個是剛纔第一個logger的名稱還是cn.lsw.base.log4j2.Hello,additivity去掉或改爲true(因爲默認是true,所以可以去掉),第二是把root的level改爲info方便觀察。 

然後運行Test,看控制檯的日誌輸出:

[java] view plain copy
  1. 2013-12-20 19:59:42.538 [main] INFO  cn.lsw.base.log4j2.Test - test  
  2. 2013-12-20 19:59:42.541 [main] TRACE cn.lsw.base.log4j2.Hello - entry  
  3. 2013-12-20 19:59:42.541 [main] TRACE cn.lsw.base.log4j2.Hello - entry  
  4. 2013-12-20 19:59:42.542 [main] ERROR cn.lsw.base.log4j2.Hello - Did it again!  
  5. 2013-12-20 19:59:42.542 [main] ERROR cn.lsw.base.log4j2.Hello - Did it again!  
  6. 2013-12-20 19:59:42.542 [main] INFO  cn.lsw.base.log4j2.Hello - 我是info信息  
  7. 2013-12-20 19:59:42.542 [main] INFO  cn.lsw.base.log4j2.Hello - 我是info信息  
  8. 2013-12-20 19:59:42.542 [main] DEBUG cn.lsw.base.log4j2.Hello - 我是debug信息  
  9. 2013-12-20 19:59:42.542 [main] DEBUG cn.lsw.base.log4j2.Hello - 我是debug信息  
  10. 2013-12-20 19:59:42.542 [main] WARN  cn.lsw.base.log4j2.Hello - 我是warn信息  
  11. 2013-12-20 19:59:42.542 [main] WARN  cn.lsw.base.log4j2.Hello - 我是warn信息  
  12. 2013-12-20 19:59:42.542 [main] FATAL cn.lsw.base.log4j2.Hello - 我是fatal信息  
  13. 2013-12-20 19:59:42.542 [main] FATAL cn.lsw.base.log4j2.Hello - 我是fatal信息  
  14. 2013-12-20 19:59:42.542 [main] DEBUG cn.lsw.base.log4j2.Hello - 我是debug信息  
  15. 2013-12-20 19:59:42.542 [main] DEBUG cn.lsw.base.log4j2.Hello - 我是debug信息  
  16. 2013-12-20 19:59:42.543 [main] TRACE cn.lsw.base.log4j2.Hello - exit  
  17. 2013-12-20 19:59:42.543 [main] TRACE cn.lsw.base.log4j2.Hello - exit  
  18. 2013-12-20 19:59:42.543 [main] ERROR cn.lsw.base.log4j2.Test - hello  

可以看出,Test的trace日誌沒有輸出,因爲他繼承了root的日誌配置,只輸出info即以上級別的日誌。Hello 輸出了trace及以上級別的日誌,但是每個都輸出了兩遍。你可以試一下,把第一個logger的level該爲error,那麼error以上的級別也是輸出兩遍。這時候,只要加上additivity爲false就可以避免這個問題了。

當然,你可以爲每個logger 都在配置文件下面做不同的配置,也可以通過繼承機制,對不同包下面的日誌做不同的配置。因爲loggers下面可以寫很多歌logger。

下面在看一個稍微複雜的例子:

[html] view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <configuration status="error">  
  3.     <!--先定義所有的appender-->  
  4.     <appenders>  
  5.         <!--這個輸出控制檯的配置-->  
  6.         <Console name="Console" target="SYSTEM_OUT">  
  7.             <!--控制檯只輸出level及以上級別的信息(onMatch),其他的直接拒絕(onMismatch)-->  
  8.             <ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/>  
  9.             <!--這個都知道是輸出日誌的格式-->  
  10.             <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>  
  11.         </Console>  
  12.         <!--文件會打印出所有信息,這個log每次運行程序會自動清空,由append屬性決定,這個也挺有用的,適合臨時測試用-->  
  13.         <File name="log" fileName="log/test.log" append="false">  
  14.             <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>  
  15.         </File>  
  16.         <!--這個會打印出所有的信息,每次大小超過size,則這size大小的日誌會自動存入按年份-月份建立的文件夾下面並進行壓縮,作爲存檔-->  
  17.         <RollingFile name="RollingFile" fileName="logs/app.log"  
  18.                      filePattern="log/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">  
  19.             <PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>  
  20.             <SizeBasedTriggeringPolicy size="50MB"/>  
  21.         </RollingFile>  
  22.     </appenders>  
  23.     <!--然後定義logger,只有定義了logger並引入的appender,appender纔會生效-->  
  24.     <loggers>  
  25.         <!--建立一個默認的root的logger-->  
  26.         <root level="trace">  
  27.             <appender-ref ref="RollingFile"/>  
  28.             <appender-ref ref="Console"/>  
  29.         </root>  
  30.     </loggers>  
  31. </configuration>  

說複雜,其實也不復雜,這一個例子主要是爲了講一下appenders。

這裏定義了三個appender,Console,File,RollingFile,看意思基本也明白,第二個是寫入文件,第三個是“循環”的日誌文件,意思是日誌文件大於閥值的時候,就開始寫一個新的日誌文件。

這裏我們的配置文件裏面的註釋算比較詳細的了。所以就大家自己看了。有一個比較有意思的是ThresholdFilter ,一個過濾器,其實每個appender可以定義很多個filter,這個功能很有用。如果你要選擇控制檯只能輸出ERROR以上的類別,你就用ThresholdFilter,把level設置成ERROR,onMatch="ACCEPT" onMismatch="DENY" 的意思是匹配就接受,否則直接拒絕,當然有其他選擇了,比如交給其他的過濾器去處理了之類的,詳情大家自己去琢磨吧。






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章