1.1 問題描述
1.2 分析思路
1.3 初步結論
1.4 迴歸驗證
1.5 結論
-
log4j2使用異步日誌將大幅提升性能,減少對應用本身的影響。 -
從根本上減少不必要日誌的輸出 。
2.1 log4j2的優勢
-
異常處理,在logback中,Appender中的異常不會被應用感知到,但是在log4j2中,提供了一些異常處理機制。 -
性能提升, log4j2相較於log4j 1和logback都具有很明顯的性能提升,後面會有官方測試的數據。
-
自動重載配置,參考了logback的設計,當然會提供自動刷新參數配置,最實用的就是我們在生產上可以動態的修改日誌的級別而不需要重啓應用——那對監控來說,是非常敏感的。 -
無垃圾機制 ,log4j2在大部分情況下,都可以使用其設計的一套無垃圾機制,避免頻繁的日誌收集導致的jvm gc。
2.2 Log4J2日誌分類
2.3 同步日誌
2.4 異步日誌
其中:
-
AsyncAppender採用了ArrayBlockingQueue來保存需要異步輸出的日誌事件; -
AsyncLogg er則使用了Disruptor框架來實現高吞吐。
2.4.1 AsyncAppender
<Configuration status="warn">
<Appenders>
<!--正常的Appender配置,此處配置的RollingFile會在下面AsyncAppender被通過name引用-->
<RollingFile name="RollingFileError" fileName="${Log_Home}/error.${date:yyyy-MM-dd}.log" immediateFlush="true"
filePattern="${Log_Home}/$${date:yyyy-MM}/error-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %logger{36} : %msg%xEx%n"/>
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
<Policies>
<TimeBasedTriggeringPolicy modulate="true" interval="1"/>
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
</RollingFile>
<!--一個Appender配置完畢-->
<!--異步AsyncAppender進行配置直接引用上面的RollingFile的name-->
<Async name="Async">
<AppenderRef ref="RollingFileError"/>
</Async>
<!--異步AsyncAppender配置完畢,需要幾個配置幾個-->
</Appenders>
<Loggers>
<Root level="error">
<!--此處如果引用異步AsyncAppender的name就是異步輸出日誌-->
<!--此處如果引用Appenders標籤中RollingFile的name就是同步輸出日誌-->
<AppenderRef ref="Async"/>
</Root>
</Loggers>
</Configuration>
目前,包括Apache Strom、Log4j2在內的很多知名項目都應用了Disruptor來獲取高性能。
Disruptor框架內部核心數據結構爲RingBuffer,其爲無鎖環形隊列。
-
lock-free-使用了CAS來實現線程安全 -
使用緩存行填 充解決僞共享問題
然後在src/java/resources目錄添加log4j2.component.properties配置文件
設置異步日誌系統屬性
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>
<!--日誌級別以及優先級排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--status="WARN" :用於設置log4j2自身內部日誌的信息輸出級別,默認是OFF-->
<!--monitorInterval="30" :間隔秒數,自動檢測配置文件的變更和重新配置本身-->
<configuration status="WARN" monitorInterval="30">
<Properties>
<!--1、自定義一些常量,之後使用${變量名}引用-->
<Property name="logFilePath">log</Property>
<Property name="logFileName">test.log</Property>
</Properties>
<!--2、appenders:定義輸出內容,輸出格式,輸出方式,日誌保存策略等,常用其下三種標籤[console,File,RollingFile]-->
<!--Appenders中配置日誌輸出的目的地
console只的是控制檯 system.out.println
rollingFile 只的是文件大小達到指定尺寸的時候產生一個新的文件-->
<appenders>
<!--console :控制檯輸出的配置-->
<console name="Console" target="SYSTEM_OUT">
<!--PatternLayout :輸出日誌的格式,LOG4J2定義了輸出代碼,詳見第二部分 %p 輸出優先級,即DEBUG,INFO,WARN,ERROR,FATAL-->
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
</console>
<!--File :同步輸出日誌到本地文件-->
<!--append="false" :根據其下日誌策略,每次清空文件重新輸入日誌,可用於測試-->
<File name="log" fileName="${logFilePath}/${logFileName}" append="false">
<!-- 格式化輸出:
%d表示日期,%thread表示線程名,
%-5level:級別從左顯示5個字符寬度
%thred: 輸出產生該日誌事件的線程名
%class:是輸出的類
%L: 輸出代碼中的行號
%M:方法名
%msg:日誌消息,
%n是換行符
%c: 輸出日誌信息所屬的類目,通常就是所在類的全名
%t: 輸出產生該日誌事件的線程名
%l: 輸出日誌事件的發生位置,相當於%C.%M(%F:%L)的組合,包括類目名、發生的線程,以及在代碼中的行數。舉例:Testlog4.main(TestLog4.Java:10)
%p: 輸出日誌信息優先級,即DEBUG,INFO,WARN,ERROR,FATAL,
2020.02.06 at 11:19:54 CST INFOcom.example.redistest.controller.PersonController 40 setPerson - 添加成功1條數據
-->
<!-- %class{36} 表示 class 名字最長36個字符 -->
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</File>
<!--
關鍵點在於 filePattern後的日期格式,以及TimeBasedTriggeringPolicy的interval,日期格式精確到哪一位,interval也精確到哪一個單位.
1) TimeBasedTriggeringPolicy需要和filePattern配套使用,由於filePattern配置的時間最小粒度如果設置是dd天,所以表示每一天新建一個文件保存日誌。
2) SizeBasedTriggeringPolicy表示當文件大小大於指定size時,生成新的文件保存日誌。與%i配合使用-->
<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">
<!--ThresholdFilter :日誌輸出過濾-->
<!--level="info" :日誌級別,onMatch="ACCEPT" :級別在info之上則接受,onMismatch="DENY" :級別在info之下則拒絕-->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<!-- Policies :日誌滾動策略-->
<Policies>
<!-- TimeBasedTriggeringPolicy :時間滾動策略,默認0點小時產生新的文件,interval="6" : 自定義文件滾動時間間隔,每隔6小時產生新文件, modulate="true" : 產生文件是否以0點偏移時間,即6點,12點,18點,0點-->
<TimeBasedTriggeringPolicyinterval="6" modulate="true"/>
<!-- SizeBasedTriggeringPolicy :文件大小滾動策略-->
<SizeBasedTriggeringPolicysize="100 MB"/>
</Policies>
<!-- DefaultRolloverStrategy屬性如不設置,則默認爲最多同一文件夾下7個文件,這裏設置了20 -->
<DefaultRolloverStrategy max="20"/>
</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">
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
<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" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
</appenders>
<!--3、然後定義logger,只有定義了logger並引入的appender,appender纔會生效-->
<loggers>
<!--過濾掉spring和mybatis的一些無用的DEBUG信息-->
<!--Logger節點用來單獨指定日誌的形式,name爲包路徑,比如要爲org.springframework包下所有日誌指定爲INFO級別等。 -->
<logger name="org.springframework" level="INFO"></logger>
<logger name="org.mybatis" level="INFO"></logger>
<!-- Root節點用來指定項目的根日誌,如果沒有單獨指定Logger,那麼就會默認使用該Root日誌輸出 -->
<root level="all">
<appender-ref ref="Console"/>
<appender-ref ref="RollingFileInfo"/>
<appender-ref ref="RollingFileWarn"/>
<appender-ref ref="RollingFileError"/>
</root>
<!--AsyncLogger :異步日誌,LOG4J有三種日誌模式,全異步日誌,混合模式,同步日誌,性能從高到底,線程越多效率越高,也可以避免日誌卡死線程情況發生-->
<!--additivity="false" : additivity設置事件是否在root logger輸出,爲了避免重複輸出,可以在Logger 標籤下設置additivity爲”false”只在自定義的Appender中進行輸出
-->
<AsyncLogger name="AsyncLogger" level="trace" includeLocation="true" additivity="false">
<appender-ref ref="RollingFileError"/>
</AsyncLogger>
</loggers>
</configuration>
-
不要同時使用AsyncAppender和AsyncLogger,也就是在配置中不要在配置 Appender的時候,使用Async標識的同時,又配置AsyncLogger,這不會報錯,但是對於性能提升沒有任何好處。 -
不要在開啓了全局同步的情況下,仍然使用AsyncAppender和AsyncLogger。這和上一條是同一個意思,也就是說,如果使用異步日誌,AsyncAppender、AsyncLogger和全局日誌,不要同時出現。 -
如果不是十分必須,不管是同步異步,都設置immediateFlush爲false,這會對性能提升有很大幫助。 -
如果不是 確 實需要,不要打印location信息,比如HTML的location,或者pattern模式裏的%C or $class, %F or %file, %l or %location, %L or %line, %M or %method, 等,因爲Log4j需要在打印日誌的時候做一次棧的快照才能獲取這些信息,這對於性能來說是個極大的損耗。
參考資料
[1] https://www.yisu.com/zixun/623058.html
[2] https://www.jianshu.com/p/9f0c67facbe2
[3] https://blog.csdn.net/thinkwon/article/details/101625124
[4] https://zhuanlan.zhihu.com/p/386990511
本文分享自微信公衆號 - 京東雲開發者(JDT_Developers)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。