詳談異步log4j2中的location信息打印問題
異步log4j2的location信息打印問題
背景:項目改造過程中將log4j2改成異步,發現行號沒有打印,於是扒瞭下官方文檔,大概陳述下:
先說一下這個問題是怎麼解決的,然後稍微擴展一下其他配置,有興趣的可以往下看或者溜一遍官方文檔
說下解決
在<AsyncLogger>標簽中配置includeLocation=”true”,
擴展:
1、additivity:這個屬性的意思是需不需要打印此logger繼承的父logger,如果是false則隻打印當前logger;如果是true則繼續打印上一層的logger,直到root。
2、實現error日志打印雙份:info.log中打印一份(即info中包括info和error日志),error.log中打印一份(隻包括error日志),這樣的好處是能根據error日志出現的上下文快速定位到程序bug出現的位置,這個功能需要使用過濾器實現,比如:
<RollingFile name="error" fileName="${log_home}/error.log" immediateFlush="false" append="true" filePattern="/history/error-%d{yyyy-MM-dd}.log.gz"> <Filters> <!--隻允許級別為error的日志通過--> <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> </Filters> <PatternLayout charset="UTF-8"> <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} #| %p #| %t #| %c{-1}:%L #| %m%n</Pattern> </PatternLayout> <Policies> <!--默認打印周期為一天--> <TimeBasedTriggeringPolicy interval="1" modulate="true"/> </Policies> <!--保存日志個數為15個--> <DefaultRolloverStrategy max="15"/> </RollingFile>
log4j2的異步形式大概分為兩種:全異步和同步異步混合。
1、全異步
實現方式:將系統屬性log4j2.contextSelector設置 為org.apache.logging.log4j.core.async.AsyncLoggerContextSelector,即
System.setProperty("log4j2.contextSelector, "org.apache.logging.log4j.core.async.AsyncLoggerContextSelector");
或者在啟動時設置:
-Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
官方給出瞭一個不需要location信息的配置文件示例:
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <!-- Async Loggers will auto-flush in batches, so switch off immediateFlush. --> <RandomAccessFile name="RandomAccessFile" fileName="async.log" immediateFlush="false" append="false"> <PatternLayout> <Pattern>%d %p %c{1.} [%t] %m %ex%n</Pattern> </PatternLayout> </RandomAccessFile> </Appenders> <Loggers> <!--不需要打印location信息--> <Root level="info" includeLocation="false"> <AppenderRef ref="RandomAccessFile"/> </Root> </Loggers> </Configuration>
註意:
如果用AsyncLoggerContextSelector實現全異步,那麼配置中就要使用<root>和<logger> 。如果配置瞭這個屬性,並且使用瞭<asyncRoot>和<asyncLogger>,那麼程序將會產生兩個線程:日志數據首先傳遞給線程A,然後線程A再傳遞給線程B,最後再輸出到磁盤,這樣是可行的,但是中間多瞭一步線程交互的過程,這是不必要的。
同步異步混合
相比起全異步,混合異步可能會花費更多的性能,
官方給出瞭一個混合異步的配置例子:
<?xml version="1.0" encoding="UTF-8"?> <!-- No need to set system property "log4j2.contextSelector" to any value when using <asyncLogger> or <asyncRoot>. --> <Configuration status="WARN"> <Appenders> <!-- Async Loggers will auto-flush in batches, so switch off immediateFlush. --> <RandomAccessFile name="RandomAccessFile" fileName="asyncWithLocation.log" immediateFlush="false" append="false"> <PatternLayout> <Pattern>%d %p %class{1.} [%t] %location %m %ex%n</Pattern> </PatternLayout> </RandomAccessFile> </Appenders> <Loggers> <!-- pattern layout actually uses location, so we need to include it --> <AsyncLogger name="com.foo.Bar" level="trace" includeLocation="true"> <AppenderRef ref="RandomAccessFile"/> </AsyncLogger> <Root level="info" includeLocation="true"> <AppenderRef ref="RandomAccessFile"/> </Root> </Loggers> </Configuration>
對於location信息:
默認情況下,異步日志記錄器不會將location信息傳遞給I/O線程,如果你的layouts或custom過濾器需要location信息,你需要在所有相關日志記錄器(包括根日志記錄器)的配置中設置“includeLocation=true”
如果其中一個layouts配置瞭關於位置的信息,比如HTML locationInfo,或者表達式%C或%class、%F或%file、%l或%location、%L或%line、%M或%method,log4j2將會獲取堆棧的快照(snapshot),並遍歷堆棧跟蹤以查找位置信息,因此會消耗較多的時間。
比同步logger慢1.3到5倍,同步日志記錄器在獲取堆棧快照之前會等待盡可能長的時間,如果不需要位置,那麼快照將永遠不會被捕獲。
瞭解更多請訪問官方文檔:Apache Log4j2 Async 官方文檔
log4j2支持異步打印提高打印輸出速度
pom依賴添加
<!--log4j2異步支持--> <dependency> <groupId>com.lmax</groupId> <artifactId>disruptor</artifactId> </dependency>
全局模式2兩種
第一種:resources 下創建屬性文件 log4j2.component.properties
# log4j2異步支持 Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
第二種:springboot啟動main方法添加代碼
System.setProperty("Log4jContextSelector","org.apache.logging.log4j.core.async.AsyncLoggerContextSelector");
或者采用啟動參數
-DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
局部模式 AsyncLogger
<Loggers> <AsyncLogger name="com.meituan.Main" level="trace" additivity="false"> <appender-ref ref="RollingFile"/> </AsyncLogger> <AsyncLogger name="RollingFile2" level="trace" additivity="false"> <appender-ref ref="RollingFile2"/> </AsyncLogger> <Root level="debug"> <AppenderRef ref="Console"/> <AppenderRef ref="RollingFile"/> </Root> </Loggers>
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- 關於log4j2的異步日志輸出方式
- log4j2的高並發死鎖問題配置優化方式
- log4j2的異步使用及添加自定義參數方式
- 全網最新Log4j 漏洞修復和臨時補救方法
- 使用log4j2自定義配置文件位置和文件名(附log4j2.xml配置實例)