1. 程式人生 > 其它 >Log4j2 簡明教程(轉載)

Log4j2 簡明教程(轉載)


一、概述

log4j2官方文件內容非常多,要一次性瞭解全部是不可能的。正確的步驟應當是先了解最常見的配置,當發現原有知識無法解決問題,再重新檢視文件看有沒有合適的配置。
下面將從檔案結構入手,再到簡單的例項,從例項入手分析常見的配置的用途,其中涉及其中包括Appenders, Filters, Layout, Lookups的知識,最後根據學習。

可以搜尋到的關於log4j2的教程非常少,這篇文章更多的是讓大家對log4j2有個大體的瞭解,免得大家看到官方文件那麼多就暈了!

歡迎關注我的github:https://github.com/benson-lin

如果覺得排版不好,可以訪問:http://blog.bensonlin.me/post/log4j2-tutorial

log4j2.xml檔案結構

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?xml version="1.0"encoding="UTF-8"?>; <Configuration> <Properties> <Property name="name1">value</property> <Property name="name2"value="value2"/> </Properties> <Filter type=
"type"... /> <Appenders> <Appender type="type"name="name"> <Filter type="type"... /> </Appender> ... </Appenders> <Loggers> <Logger name="name1"> <Filter type="type"... /> </Logger> ... <Root level="level"> <AppenderRef ref="name"/> </Root>
</Loggers> </Configuration>

  

下面是一個比較完整的例子:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 <?xml version="1.0"encoding="UTF-8"?> <!-- 設定log4j2的自身log級別為warn --> <!-- OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> <configuration status="WARN"monitorInterval="30"> <appenders> <console name="Console"target="SYSTEM_OUT"> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> </console> <RollingFile name="RollingFileInfo"fileName="${sys:user.home}/logs/info.log" filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log"> <!--控制檯只輸出level及以上級別的資訊(onMatch),其他的直接拒絕(onMismatch)--> <Filters> <ThresholdFilter level="INFO"/> <ThresholdFilter level="WARN"onMatch="DENY"onMismatch="NEUTRAL"/> </Filters> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <Policies> <TimeBasedTriggeringPolicy/> <SizeBasedTriggeringPolicy size="100 MB"/> </Policies> </RollingFile> <RollingFile name="RollingFileWarn"fileName="${sys:user.home}/logs/warn.log" filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log"> <Filters> <ThresholdFilter level="WARN"/> <ThresholdFilter level="ERROR"onMatch="DENY"onMismatch="NEUTRAL"/> </Filters> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <Policies> <TimeBasedTriggeringPolicy/> <SizeBasedTriggeringPolicy size="100 MB"/> </Policies> </RollingFile> <RollingFile name="RollingFileError"fileName="${sys:user.home}/logs/error.log" filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log"> <ThresholdFilter level="ERROR"/> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <Policies> <TimeBasedTriggeringPolicy/> <SizeBasedTriggeringPolicy size="100 MB"/> </Policies> </RollingFile> </appenders> <loggers> <!--過濾掉spring和mybatis的一些無用的DEBUG資訊--> <logger name="org.springframework"level="INFO"></logger> <logger name="org.mybatis"level="INFO"></logger> <root level="all"> <appender-ref ref="Console"/> <appender-ref ref="RollingFileInfo"/> <appender-ref ref="RollingFileWarn"/> <appender-ref ref="RollingFileError"/> </root> </loggers> </configuration>

  

log4j2有預設的配置,如果要替換配置,只需要在classpath根目錄下放置log4j2.xml。
log4j 2.0與以往的1.x有一個明顯的不同,其配置檔案只能採用.xml, .json或者 .jsn。在預設情況下,系統選擇configuration檔案的優先順序如下:(classpath為src資料夾)

  • classpath下名為 log4j-test.json 或者log4j-test.jsn檔案
  • classpath下名為 log4j2-test.xml
  • classpath下名為 log4j.json 或者log4j.jsn檔案
  • classpath下名為 log4j2.xml

如果本地要測試,可以把log4j2-test.xml放到classpath,而正式環境使用log4j2.xml,則在打包部署的時候不要打包log4j2-test.xml即可。

下面是其預設配置:

1 2 3 4 5 6 7 8 9 10 11 12 13 <?xml version="1.0"encoding="UTF-8"?> <Configuration status="WARN"> <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"> <AppenderRef ref="Console"/> </Root> </Loggers> </Configuration>

  

下面將對上面的配置檔案進行一一講解。

二、示例Java程式碼

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 packagecom.foo; // Import log4j classes. importorg.apache.logging.log4j.Logger; importorg.apache.logging.log4j.LogManager; publicclassMyApp { // Define a static logger variable so that it references the // Logger instance named "MyApp". privatestaticfinalLogger logger = LogManager.getLogger(MyApp.class); publicstaticvoidmain(finalString... args) { // Set up a simple configuration that logs on the console. logger.trace("Entering application."); Bar bar =newBar(); if(!bar.doIt()) { logger.error("Didn't do it."); } logger.trace("Exiting application."); } } packagecom.foo; importorg.apache.logging.log4j.LogManager; importorg.apache.logging.log4j.Logger; publicclassBar { staticfinalLogger logger = LogManager.getLogger(Bar.class.getName()); publicbooleandoIt() { logger.entry(); logger.error("Did it again!"); returnlogger.exit(false); } }

  

如果使用如下配置,也就是預設配置:

1 2 3 4 5 6 7 8 9 10 11 12 13 <?xml version="1.0"encoding="UTF-8"?> <Configuration status="WARN"> <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"> <AppenderRef ref="Console"/> </Root> </Loggers> </Configuration>

  

輸出如下:只輸出error以上的日誌資訊

1 2 17:13:01.540[main] ERROR com.foo.Bar - Did it again! 17:13:01.540[main] ERROR MyApp - Didn'tdoit.

如果我們希望除了com.foo.Bar類下輸出TRACE以上到控制檯外,其他停止TRACE的輸出到控制檯,只輸出ERROR以上的日誌。可以如下配置:

1 2 3 4 5 6 <Loggers> <Logger name="com.foo.Bar"level="TRACE"/> <Root level="ERROR"> <AppenderRef ref="STDOUT"> </Root> </Loggers>

結果如下:

1 2 3 4 14:14:17.176[main] TRACE com.foo.Bar - Enter 14:14:17.182[main] ERROR com.foo.Bar - Did it again! 14:14:17.182[main] TRACE com.foo.Bar - Exit with(false) 14:14:17.182[main] ERROR com.foo.MyApp - Didn'tdoit.

因為com.foo.Bar沒有自己的Appender,所以會使用ROOT的Appender,如果自己也配置了在控制檯列印,就要注意可加性:如下配置,會ERROR以上的會列印兩次

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?xml version="1.0"encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <Console name="Console"target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> </Appenders> <Loggers> <Logger name="com.foo.Bar"level="trace"> <AppenderRef ref="Console"/> </Logger> <Root level="error"> <AppenderRef ref="Console"/> </Root> </Loggers> </Configuration>

結果如下

1 2 3 4 5 6 7 14:11:27.103[main] TRACE com.foo.Bar - Enter 14:11:27.103[main] TRACE com.foo.Bar - Enter 14:11:27.106[main] ERROR com.foo.Bar - Did it again! 14:11:27.106[main] ERROR com.foo.Bar - Did it again! 14:11:27.107[main] TRACE com.foo.Bar - Exit with(false) 14:11:27.107[main] TRACE com.foo.Bar - Exit with(false) 14:11:27.107[main] ERROR com.foo.MyApp - Didn'tdoit.

如果我們確實有這種需求(不想遵循父類的Appender),可以加上additivity="false"引數。如下配置,com.foo.Bar的trace以上日誌將儲存到檔案中,並且不會列印到控制檯。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <Configuration status="WARN"> <Appenders> <Console name="Console"target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> <RollingFile name="RollingFile"fileName="${sys:user.home}/logs/trace.log" filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log"> ... </RollingFile> </Appenders> <Loggers> <Logger name="com.foo.Bar"level="trace"additivity="false"> <AppenderRef ref="RollingFile"/> </Logger> <Root level="error"> <AppenderRef ref="Console"/> </Root> </Loggers> </Configuration>

log4j2支援自動重新配置,如果配置了monitorInterval,那麼log4j2每隔一段時間就會檢查一遍這個檔案是否修改。最小是5s

1 2 3 4 <?xml version="1.0"encoding="UTF-8"?> <Configuration monitorInterval="30"> ... </Configuration>

 

三、Appenders

ConsoleAppender

將使用 System.out 或 System.err輸出到控制檯。

可以有如下引數

  • name:Appender的名字
  • target:SYSTEM_OUT 或 SYSTEM_ERR,預設是SYSTEM_OUT
  • layout:如何格式化,如果沒有預設是%m%n

典型的ConsoleAppender如下

1 2 3 4 5 6 7 8 9 10 11 12 13 <?xml version="1.0"encoding="UTF-8"?> <Configuration status="warn"name="MyApp"packages=""> <Appenders> <Console name="STDOUT"target="SYSTEM_OUT"> <PatternLayout pattern="%m%n"/> </Console> </Appenders> <Loggers> <Root level="error"> <AppenderRef ref="STDOUT"/> </Root> </Loggers> </Configuration>

  

RollingFileAppender

顧名思義,日誌檔案回滾,也就是刪除最舊的日誌檔案,預設是3個檔案。可以通過DefaultRolloverStrategy設定max引數為多個

例子如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 <Appenders> <RollingFile name="RollingFile"fileName="logs/app.log" filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz"> <PatternLayout> <Pattern>%d %p %c{1.} [%t] %m%n</Pattern> </PatternLayout> <Policies> <TimeBasedTriggeringPolicy /> <SizeBasedTriggeringPolicy size="250 MB"/> </Policies> <DefaultRolloverStrategy max="20"/> </RollingFile> </Appenders>

  

現在說說TimeBasedTriggeringPolicy和SizeBasedTriggeringPolicy的作用。
第一個是基於時間的rollover,第二個是基於大小的rollover。第二個很容易理解,如果大小大於某個閾值,上面是50MB的時候就會滾動。

TimeBasedTriggeringPolicy中有其中一個引數是interval,表示多久滾動一次。預設是1 hour。modulate=true用來調整時間:比如現在是早上3am,interval是4,那麼第一次滾動是在4am,接著是8am,12am...而不是7am

四、Layouts

這裡只描述最常見的PatternLayout!更多看官方文件Layouts

1 2 3 4 5 6 7 8 9 10 <RollingFile name="RollingFileError"fileName="${sys:user.home}/logs/error.log" filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log"> <ThresholdFilter level="ERROR"/> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <Policies> <TimeBasedTriggeringPolicy/> <SizeBasedTriggeringPolicy size="50 MB"/> </Policies> <DefaultRolloverStrategy max="20"/> </RollingFile>

  

上面的%是什麼含義,還有哪些呢?其實最主要的引數還是%d, %p, %l, %m, %n, %X。下面的圖是摘取網上的。

%X用來獲取MDC記錄,這些記錄從哪來的?我們可以使用org.apache.logging.log4j.ThreadContext將需要記錄的值put進去。(我發現slf的MDC.java的put方法對log4j2不可用,因為底層依賴的是log4j1)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 packagecom.bensonlin.service.web.interceptor; importjavax.servlet.http.HttpServletRequest; importjavax.servlet.http.HttpServletResponse; importorg.apache.logging.log4j.ThreadContext; importorg.springframework.web.servlet.HandlerInterceptor; importorg.springframework.web.servlet.ModelAndView; publicclassMDCInterceptorimplementsHandlerInterceptor { publicfinalstaticString USER_KEY ="user_id"; publicfinalstaticString REQUEST_REQUEST_URI ="request_uri"; publicbooleanpreHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object arg2) throwsException { ThreadContext.put(REQUEST_REQUEST_URI, httpServletRequest.getRequestURI()); returntrue; } publicvoidpostHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object arg2, ModelAndView modelAndView)throwsException { } publicvoidafterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object arg2, Exception exception)throwsException { ThreadContext.remove(USER_KEY); ThreadContext.remove(REQUEST_REQUEST_URI); } publicstaticvoidsetUserKeyForMDC(String userId) { ThreadContext.put(USER_KEY, userId); } }

  

xml中使用%X{aaa}取出來:

1 2 3 <console name="Console"target="SYSTEM_OUT"> <PatternLayout pattern="%X{user_id} %X{request_uri} [%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> </console>

  

對應ThreadContext的文件在這裡

五、Filters

Filters決定日誌事件能否被輸出。過濾條件有三個值:ACCEPT(接受), DENY(拒絕) or NEUTRAL(中立).

ACCEP和DENY比較好理解就是接受和拒絕的意思,在使用單個過濾器的時候,一般就是使用這兩個值。但是在組合過濾器中,如果用接受ACCEPT的話,日誌資訊就會直接寫入日誌檔案,後續的過濾器不再進行過濾。所以,在組合過濾器中,接受使用NEUTRAL(中立),被第一個過濾器接受的日誌資訊,會繼續用後面的過濾器進行過濾,只有符合所有過濾器條件的日誌資訊,才會被最終寫入日誌檔案。

ThresholdFilter

有幾個引數:

  • level:將被過濾的級別。
  • onMatch:預設值是NEUTRAL
  • onMismatch:預設是DENY

如果LogEvent 中的 Log Level 大於 ThresholdFilter 中配置的 Log Level,那麼返回 onMatch 的值, 否則返回 onMismatch 的值,例如 : 如果ThresholdFilter 配置的 Log Level 是 ERROR , LogEvent 的Log Level 是 DEBUG。 那麼 onMismatch 的值將被返回, 因為 ERROR 小於DEBUG。如果是Accept,將自己被接受,而不經過下一個過濾器

下面的例子可以這樣理解:如果是INFO級別及其以上,將經過通過第一個過濾,進入第二個,否則是onMismatch:拒絕進入。對於第二個,如果是大於等於WARN(WARN/ERROR/ERROR),那麼將返回onMatch,也就是拒絕,如果是其他情況(也就是INFO),將是中立情況,因為後面沒有其他過濾器,則被接受。最後的結果就只剩下INFO級別的日誌。也就符合了RollingFileInfo只記錄Info級別的規則。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 <RollingFile name="RollingFileInfo"fileName="${sys:user.home}/logs/info.log" filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log"> <!--控制檯只輸出level及以上級別的資訊(onMatch),其他的直接拒絕(onMismatch)--> <Filters> <ThresholdFilter level="INFO"/> <ThresholdFilter level="WARN"onMatch="DENY"onMismatch="NEUTRAL"/> </Filters> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <Policies> <TimeBasedTriggeringPolicy interval="24"modulate="true"/> <SizeBasedTriggeringPolicy size="50 MB"/> </Policies> <DefaultRolloverStrategy max="20"/> </RollingFile>

  

六、Lookups

提供另外一種方式新增某些特殊的值到日誌中。

Date Lookup

與其他lookup不同,它不是通過key去查詢值,而是通過SimpleDateFormat驗證格式是否有效,然後記錄當前時間

1 2 3 4 5 6 <RollingFile name="Rolling-${map:type}"fileName="${filename}"filePattern="target/rolling1/test1-$${date:MM-dd-yyyy}.%i.log.gz"> <PatternLayout> <pattern>%d %p %c{1.} [%t] %m%n</pattern> </PatternLayout> <SizeBasedTriggeringPolicy size="500"/> </RollingFile>

Context Map Lookup: 如記錄loginId

1 2 3 4 5 <File name="Application"fileName="application.log"> <PatternLayout> <pattern>%d %p %c{1.} [%t] $${ctx:loginId} %m%n</pattern> </PatternLayout> </File>

這個的結果和前面的MDC是一樣的,即 %X{loginId}

Environment Lookup:記錄系統環境變數

比如可以獲取如/etc/profile中的變數值

1 2 3 4 5 <File name="Application"fileName="application.log"> <PatternLayout> <pattern>%d %p %c{1.} [%t] $${env:USER} %m%n</pattern> </PatternLayout> </File>

  

System Properties Lookup

可以獲取Java中的系統屬性值。

1 2 3 <Appenders> <File name="ApplicationLog"fileName="${sys:logPath}/app.log"/> </Appenders>

和系統屬性值有什麼區別呢?其實就是System.getProperties();和System.getenv();的區別。下面是一個小例子。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 packagecom.bensonlin.service.common; importjava.util.Iterator; importjava.util.Map; importjava.util.Map.Entry; importjava.util.Properties; publicclassMain { publicstaticvoidmain(String[] args) { Properties properties = System.getProperties(); Iterator i = properties.entrySet().iterator(); while(i.hasNext()) { Map.Entry entry = (Map.Entry) i.next(); Object key = entry.getKey(); Object value = entry.getValue(); System.out.println(key +"="+ value); } System.out.println("==================="); Map map = System.getenv(); Iterator it = map.entrySet().iterator(); while(it.hasNext()) { Entry entry = (Entry) it.next(); System.out.print(entry.getKey() +"="); System.out.println(entry.getValue()); } } }

輸出(摘取部分):

1 2 3 4 5 6 7 8 9 10 11 java.runtime.name=Java(TM) SE Runtime Environment sun.boot.library.path=C:\Program Files\Java\jdk1.8.0_25\jre\bin java.vm.version=25.25-b02 java.vm.vendor=Oracle Corporation java.vendor.url=http://java.oracle.com/ path.separator=; ... =================== JAVA_HOME=C:\Program Files\Java\jdk1.8.0_25 TEMP=D:\Temp ProgramFiles=C:\Program Files
...

可以看到其實Environment是獲取環境變數,而System Properties獲取的更多是與Java相關的值

轉載註明地址:http://blog.bensonlin.me/post/log4j2-tutorial