一、日誌的級別:java
咱們如今要調用logger的方法,不過在這個Logger對象中,有不少方法,因此要先了解log4j的日誌級別,log4j規定了默認的幾個級別:trace<debug<info<warn<error<fatal等。這裏要說明一下:程序員
1)級別之間是包含的關係,意思是若是你設置日誌級別是trace,則大於等於這個級別的日誌都會輸出。apache
2)基本上默認的級別沒多大區別,就是一個默認的設定。你能夠經過它的API本身定義級別。app
3)簡單介紹一下不一樣的級別的含義:測試
trace: 追蹤,就是程序推動一下,你就能夠寫個trace輸出,因此trace應該會特別多,通常設置最低日誌級別不讓他輸出。線程
debug: 調試,通常就只用這個做爲最低級別。debug
info: 輸出感興趣的或者重要的信息。調試
warn: 有些信息不是錯誤信息,可是也要給程序員的一些提示。日誌
error: 錯誤信息。code
fatal: 級別比較高。重大錯誤,這種級別你能夠直接中止程序了,是不該該出現的錯誤!
二、日誌調用:
log4j的核心在配置文件上。
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class Hello { static Logger logger = LogManager.getLogger(Hello.class.getName()); public boolean hello() { logger.entry(); //trace級別的信息,單獨列出來是但願你在某個方法或者程序邏輯開始的時候調用,和logger.trace("entry")基本一個意思 logger.error("Did it again!"); //error級別的信息,參數就是你輸出的信息 logger.info("我是info信息"); //info級別的信息 logger.debug("我是debug信息"); logger.warn("我是warn信息"); logger.fatal("我是fatal信息"); logger.log(Level.DEBUG, "我是debug信息"); //這個就是制定Level類型的調用:誰閒着沒事調用這個,也不必定哦! logger.exit(); //和entry()對應的結束方法,和logger.trace("exit");一個意思 return false; } }
若是沒有自定義配置文件,上面這個類在寫一個main方法,控制檯會輸出:
Did it again! 我是fatal信息
由於Log4j有一個默認的配置,它的日誌級別是ERROR。若是日誌級別改爲了TRACE,控制檯會輸出:
entry Did it again! 我是info信息 我是debug信息 我是warn信息 我是fatal信息 我是debug信息 exit
3. 配置文件:
原本覺得Log4J 2應該有一個默認的配置文件的,不過好像沒有找到,下面這個配置文件等同於缺省配置:
<?xml version="1.0" encoding="UTF-8"?> <configuration status="OFF"> <appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> </appenders> <loggers> <root level="error"> <appender-ref ref="Console"/> </root> </loggers> </configuration>
咱們只要把configuration>loggers>root的level屬性改成trace,就能夠輸出剛纔寫的全部信息了。Log4J傳統的配置一直是.properties文件,鍵值對的形式,那種配置方式很很差看,可是基本上咱們從這個配置文件也能看到Log4J 1的影子,無非是appender了,layout之類的,含義也基本同樣的。
第一個例子:
<?xml version="1.0" encoding="UTF-8"?> <configuration status="OFF"> <appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> </appenders> <loggers> <!--咱們只讓這個logger輸出trace信息,其餘的都是error級別--> <!-- additivity開啓的話,因爲這個logger也是知足root的,因此會被打印兩遍。 不過root logger 的level是error,爲何Bar 裏面的trace信息也被打印兩遍呢 --> <logger name="cn.lsw.base.log4j2.Hello" level="trace" additivity="false"> <appender-ref ref="Console"/> </logger> <root level="error"> <appender-ref ref="Console"/> </root> </loggers> </configuration>
先簡單介紹一下下面這個配置文件。
1)根節點configuration,而後有兩個子節點:appenders和loggers(都是複數,意思就是能夠定義不少個appender和logger了)
2)appenders:這個下面定義的是各個appender,就是輸出了,有好多類別,這個例子只有一個Console,這些節點可不是隨便命名的,Console就是輸出控制檯的意思。而後就針對這個輸出設置一些屬性,這裏設置了PatternLayout就是輸出格式了,基本上是前面時間,線程,級別,logger名稱,log信息等,差很少,能夠本身去查他們的語法規則。
3)loggers下面會定義許多個logger,這些logger經過name進行區分,來對不一樣的logger配置不一樣的輸出,方法是經過引用上面定義的logger,注意,appender-ref引用的值是上面每一個appender的name,而不是節點名稱。
四、 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",就不會輸出兩遍,不然,看下面的輸出:
這裏要在加入一個類作對比:
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class Test { private static Logger logger = LogManager.getLogger(Test.class.getName()); public static void main(String[] args) { logger.trace("開始程序."); Hello hello= new Hello(); // for (int i = 0; i < 10000;i++){ if (!hello.hello()) { logger.error("hello"); } // } logger.trace("退出程序."); } }
這裏先把配置文件改一下方便對照,一個是剛纔第一個logger的名稱仍是cn.lsw.base.log4j2.Hello,additivity去掉或改成true(由於默認是true,因此能夠去掉),第二是把root的level改成info方便觀察。
而後運行Test,看控制檯的日誌輸出:
test entry entry Did it again! Did it again! 我是info信息 我是info信息 我是debug信息 我是debug信息 我是warn信息 我是warn信息 我是fatal信息 我是fatal信息 我是debug信息 我是debug信息 exit exit hello
能夠看出,Test的trace日誌沒有輸出,由於他繼承了root的日誌配置,只輸出info即以上級別的日誌。Hello 輸出了trace及以上級別的日誌,可是每一個都輸出了兩遍。你能夠試一下,把第一個logger的level該爲error,那麼error以上的級別也是輸出兩遍。這時候,只要加上additivity爲false就能夠避免這個問題了。
固然,你能夠爲每一個logger 都在配置文件下面作不一樣的配置,也能夠經過繼承機制,對不一樣包下面的日誌作不一樣的配置。由於loggers下面能夠寫不少歌logger。
下面在看一個稍微複雜的例子:
<?xml version="1.0" encoding="UTF-8"?> <configuration status="error"> <!--先定義全部的appender--> <appenders> <!--這個輸出控制檯的配置--> <Console name="Console" target="SYSTEM_OUT"> <!--控制檯只輸出level及以上級別的信息(onMatch),其餘的直接拒絕(onMismatch)--> <ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/> <!--這個都知道是輸出日誌的格式--> <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> </Console> <!--文件會打印出全部信息,這個log每次運行程序會自動清空,由append屬性決定,這個也挺有用的,適合臨時測試用--> <File name="log" fileName="log/test.log" append="false"> <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> </File> <!--這個會打印出全部的信息,每次大小超過size,則這size大小的日誌會自動存入按年份-月份創建的文件夾下面並進行壓縮,做爲存檔--> <RollingFile name="RollingFile" fileName="logs/app.log" filePattern="log/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz"> <PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/> <SizeBasedTriggeringPolicy size="50MB"/> </RollingFile> </appenders> <!--而後定義logger,只有定義了logger並引入的appender,appender纔會生效--> <loggers> <!--創建一個默認的root的logger--> <root level="trace"> <appender-ref ref="RollingFile"/> <appender-ref ref="Console"/> </root> </loggers> </configuration>
這一個例子主要是爲了講一下appenders。
這裏定義了三個appender,Console,File,RollingFile,看意思基本也明白,第二個是寫入文件,第三個是「循環」的日誌文件,意思是日誌文件大於閥值的時候,就開始寫一個新的日誌文件。
有一個比較有意思的是ThresholdFilter ,一個過濾器,其實每一個appender能夠定義不少個filter,這個功能頗有用。若是你要選擇控制檯只能輸出ERROR以上的類別,你就用ThresholdFilter,把level設置成ERROR,onMatch="ACCEPT" onMismatch="DENY" 的意思是匹配就接受,不然直接拒絕。