1、log4j已成為大型系統必不可少的一部分,log4j可以很方便的幫助我們在程序的任何位置輸出所要打印的信息,便於我們對系統在調試階段和正式運行階段對問題分析和定位。由於日志級別的不同,對系統的性能影響也是有很大的差距,日志級別越高,性能越高。
2、log4j主要分為error,warn,info,debug四個級別,也是使用最多的四種,日志級別從左至右依次增加。
3、log4j對系統性能的影響程度主要體現在以下幾方面:
a、日志輸出的目的地,輸出到控制台的速度比輸出到文件系統的速度要慢。 b、日志輸出格式不一樣對性能也會有影響,如簡單輸出布局(SimpleLayout)比格式化輸出布局(PatternLayout)輸出速度要快。可以根據需要盡量采用簡單輸出布局格式輸出日志信息。 c、日志級別越低輸出的日志內容就越多,對系統系能影響很大。 d、日志輸出方式的不同,對系統系能也是有一定影響的,采用異步輸出方式比同步輸出方式性能要高。 e、每次接收到日志輸出事件就打印一條日志內容比當日志內容達到一定大小時打印系能要低。
4、針對以上幾點對系能的影響中的第4,5點,對日志配置文件做如下配置:
a、設置日志緩存,以及緩存大小
- log4j.appender.A3.BufferedIO=true
- #Buffer單位為字節,默認是8K,IO BLOCK大小默認也是8K
- log4j.appender.A3.BufferSize=8192
以上配置說明,當日志內容達到8k時,才會將日志輸出到日志輸出目的地。
b、設置日志輸出為異步方式
- <appender name="DRFOUT" class="org.apache.log4j.DailyRollingFileAppender">
- <param name="File" value="logs/brws.log" />
- <param name="Append" value="true" />
- <param name="DatePattern" value="yyyy_MM_dd'.'" />
- <layout class="org.apache.log4j.PatternLayout">
- <param name="ConversionPattern" value="%d [%t] %-5p %l %x - %m%n" />
- </layout>
- </appender>
- <appender name="ASYNCOUT" class="org.apache.log4j.AsyncAppender">
- <param name="BufferSize" value="512" />
- <appender-ref ref="DRFOUT" />
- </appender>
同步情況:各線程直接獲得輸出流進行輸出(線程間不需要同步)。
異步情況:1.各線程將日志寫到緩存,繼續執行下面的任務(這里是異步的)
2.日志線程發現需要記日志時獨占緩存(與此同時各線程等待,此時各線程是被阻塞住的),從緩存中取出日志信息,獲得輸出流進行輸出,將緩存解鎖(各線程收到提醒,可以接着寫日志了)
眾所周知,磁盤IO操作、網絡IO操作、JDBC操作等都是非常耗時的,日志輸出的主要性能瓶頸也就是在寫文件、寫網絡、寫JDBC的時候。日志是肯定要記的,而要采用異步方式記,也就只有將這些耗時操作從主線程當中分離出去才真正的實現性能提升,也只有在線程間同步開銷小於耗時操作時使用異步方式才真正有效 !
現在我們接着分別來看看這幾種記錄日志的方式:
a、將日志記錄到本地文件 同樣都是寫本地文件Log4j本身有一個buffer處理入庫,采用異步方式並不一定能提高性能(主要是如何配置好緩存大小);而線程間的同步開銷則是非常大的!因此在使用本地文件記錄日志時不建議使用異步方式。
b、將日志記錄到JMS JMS本身是支持異步消息的,如果不考慮JMS消息創建的開銷,也不建議使用異步方式。
c、將日子記錄到SOCKET 將日志通過Socket發送,純網絡IO操作不需要反饋,因此也不會耗時
d、將日志記錄到數據庫 眾所周知JDBC是幾種方式中最耗時的:網絡、磁盤、數據庫事務,都使JDBC操作異常的耗時,在這里采用異步方式入庫倒是一個不錯的選擇。
e、將日志記錄到SMTP 同JDBC
5、異步輸出日志工作原理
AsyncAppender采用的是生產者消費者的模型進行異步地將Logging Event送到對應的Appender中。
a、 生產者:外部應用了Log4j的系統的實時線程,實時將Logging Event傳送進AsyncAppender里
b、 中轉:Buffer和DiscardSummary
c、 消費者:Dispatcher線程和appenders
工作原理:
1) Logging Event進入AsyncAppender,AsyncAppender會調用append方法,在append方法中會去把logging Event填入Buffer中,當消費能力不如生產能力時,AsyncAppender會把超出Buffer容量的Logging Event放到DiscardSummary中,作為消費速度一旦跟不上生成速度,中轉buffer的溢出處理的一種方案。
2) AsyncAppender有個線程類Dispatcher,它是一個簡單的線程類,實現了Runnable接口。它是AsyncAppender的后台線程。
Dispatcher所要做的工作是:
① 鎖定Buffer,讓其他要對Buffer進行操作的線程阻塞。
② 看Buffer的容量是否滿了,如果滿了就將Buffer中的Logging Event全部取出,並清空Buffer和DiscardSummary;如果沒滿則等待Buffer填滿Logging Event,然后notify Disaptcher線程。
③ 將取出的所有Logging Event交給對應appender進行后面的日志信息推送。
以上是AsyncAppender類的兩個關鍵點:append方法和Dispatcher類,通過這兩個關鍵點實現了異步推送日志信息的功能,這樣如果大量的Logging Event進入AsyncAppender,就可以游刃有余地處理這些日志信息了。
************************************************************************************************************************************************************************
************************************************************************************************************************************************************************
不久前在系統中完成了監控的功能,監控系統的信息量很大,用戶對頁面的每一個點擊都會產生記錄,每天下來的日志量有2G多,我用log4j把這些監控記錄 放在日志里,然后進行異步處理,但即使是這樣,記錄日志會對磁盤IO產生頻繁的訪問,而IO通常就是系統的瓶頸所在。於是對log4j配置進行一些調優就 成了必要。下面是我系統中的log4j配置:
log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d (%F:%L) %-5p %c - %m%n
log4j.appender.fileout=org.apache.log4j.DailyRollingFileAppender log4j.appender.fileout.File=logs/server_log.txt log4j.appender.fileout.layout=org.apache.log4j.PatternLayout log4j.appender.fileout.layout.ConversionPattern=%d [%t] (%F:%L) %-5p %c - %m%n
log4j.appender.monitorAppender=org.apache.log4j.DailyRollingFileAppender log4j.appender.monitorAppender.File=mtlogs/mt_log.txt log4j.appender.monitorAppender.layout=org.apache.log4j.PatternLayout log4j.appender.monitorAppender.layout.ConversionPattern=%m%n log4j.appender.monitorAppender.DatePattern='.'yyyy-MM-dd-HH log4j.appender.monitorAppender.BufferedIO=true #Buffer單位為字節,默認是8K log4j.appender.monitorAppender.BufferSize=8192
1)log4j.additivity.monitorLogger=false 這個選項用於控制監控logger的日志不會輸出到rootlogger,否則無論會產生許多重復的數據,同時也會影響性能;
2)log4j.appender.monitorAppender.DatePattern='.'yyyy-MM-dd-HH 這個選項用於告訴DailyRollingFileAppender每小時輸出日志,而不是默認的一天輸出一次,因為監控日志的數據量很巨大,如果以天為單位輸出,日志文件會很大(G級),而且再處理會很耗時。
其他一些輸出選項還有:
1)'.'yyyy-MM: 每月
2)'.'yyyy-ww: 每周
3)'.'yyyy-MM-dd: 每天
4)'.'yyyy-MM-dd-a: 每天兩次
5)'.'yyyy-MM-dd-HH: 每小時
6)'.'yyyy-MM-dd-HH-mm: 每分鍾
3)log4j.appender.monitorAppender.BufferedIO=true log4j.appender.monitorAppender.BufferSize=8192 這 個選項用於告訴log4j輸出日志的時候采用緩沖的方式,而不是即時flush方式,並且設定了緩沖為8K,8K是默認值,可以根據日志輸出的情況來修 改。這個選項很重要,在測試中發現,當並發訪問很高,例如每一秒100個並發以上,使用緩存跟不使用緩沖差距很大。具體數字我這里就不列出來了。 另外我想說的是,log4j輸出緩沖日志是以8K為單位的,因為磁盤的一個block為8K,這樣可以減少碎片,也就是說假設你設置緩存為18K,log4j在16K(8K*2)的時候就會輸出),而不是18K。
4)組裝輸出內容之前可對logger的輸出級別先進行判斷而不要完全依賴log4j控制,因為組裝輸出日志內容也是要損耗效率的。 //若log4j並未開啟info級日志記錄,直接返回 if(!monitorLogger.isInfoEnabled()){ return; } StringBuilder log = new StringBuilder(); logSql.append(logPk+" "); ...
5)使用異步輸出 org.apache.log4j.AsyncAppender,異步輸出必須使用xml方式配置才能支持,我把上面properties形式的配置文件用xml表達一下:
<appender name="stdout" class="org.apache.log4j.ConsoleAppender"> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d (%F:%L) %-5p %c - %m%n" /> </layout> </appender>
<appender name="fileout" class="org.apache.log4j.DailyRollingFileAppender"> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d [%t] (%F:%L) %-5p %c - %m%n" /> </layout> <param name="File" value="logs/server_log.txt" /> </appender>
<appender name="monitorAppender" class="org.apache.log4j.DailyRollingFileAppender"> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%m%n" /> </layout> <param name="DatePattern" value="'.'yyyy-MM-dd-HH" /> <param name="File" value="mtlogs/mt_log.txt" /> <param name="BufferedIO" value="true" /> <!-- 8K為一個寫單元 --> <param name="BufferSize" value="8192" /> </appender>
<appender name="async" class="org.apache.log4j.AsyncAppender"> <appender-ref ref="monitorAppender"/> </appender> <root> <priority value="error" /> <appender-ref ref="stdout" /> <appender-ref ref="fileout" /> </root> <category name="com.danga.MemCached"> <priority value="error" /> <appender-ref ref="fileout" /> </category > <category name="com.opensymphony"> <priority value="error" /> <appender-ref ref="fileout" /> </category > <category name="monitorLogger" additivity="false"> <priority value="info" /> <appender-ref ref="async" /> </category > </log4j:configuration>
配置中紅色的部分就是用於支持異步輸出的,在用jmeter測試的過程中發覺使用異步方式,工作的不是很穩定。性能的提升也不顯著。所以最后並沒有采用。