log4j⽇志输出性能优化-缓存、异步
1、log4j已成为⼤型系统必不可少的⼀部分,log4j可以很⽅便的帮助我们在程序的任何位置输出所要打印的信息,便于我们对系统在调试阶段和正式运⾏阶段对问题分析和定位。由于⽇志级别的不同,对系统的性能影响也是有很⼤的差距,⽇志级别越⾼,性能越⾼。
2、log4j主要分为error,warn,info,debug四个级别,也是使⽤最多的四种,⽇志级别从左⾄右依次增加。
3、log4j对系统性能的影响程度主要体现在以下⼏⽅⾯:
a、⽇志输出的⽬的地,输出到控制台的速度⽐输出到⽂件系统的速度要慢。
b、⽇志输出格式不⼀样对性能也会有影响,如简单输出布局(SimpleLayout)⽐格式化输出布局(PatternLayout)输出速度要快。可以根据需要尽量采⽤简单输出布局格式输出⽇志信息。
c、⽇志级别越低输出的⽇志内容就越多,对系统系能影响很⼤。
d、⽇志输出⽅式的不同,对系统系能也是有⼀定影响的,采⽤异步输出⽅式⽐同步输出⽅式性能要⾼。
e、每次接收到⽇志输出事件就打印⼀条⽇志内容⽐当⽇志内容达到⼀定⼤⼩时打印系能要低。
log4j与log4j2
4、针对以上⼏点对系能的影响中的第4,5点,对⽇志配置⽂件做如下配置:
a、设置⽇志缓存,以及缓存⼤⼩
Java代码
1. log4j.appender.A3.BufferedIO=true
2. #Buffer单位为字节,默认是8K,IO BLOCK⼤⼩默认也是8K
3. log4j.appender.A3.BufferSize=8192
以上配置说明,当⽇志内容达到8k时,才会将⽇志输出到⽇志输出⽬的地。
b、设置⽇志输出为异步⽅式
Java代码
1. <appender name="DRFOUT" class="org.apache.log4j.DailyRollingFileAppender">
2. <param name="File" value="logs/brws.log" />
3. <param name="Append" value="true" />
4. <param name="DatePattern" value="yyyy_MM_dd'.'" />
5. <layout class="org.apache.log4j.PatternLayout">
6. <param name="ConversionPattern" value="%d [%t] %-5p %l %x - %m%n" />
7. </layout>
8. </appender>
9.
10. <appender name="ASYNCOUT" class="org.apache.log4j.AsyncAppender">
11. <param name="BufferSize" value="512" />
12. <appender-ref ref="DRFOUT" />
13. </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,就可以游刃有余地处理这些⽇志信息了。
************************************************************************************************************************************************************************
************************************************************************************************************************************************************************
itorLogger=INFO,monitorAppender
itorLogger=false
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/
log4j.appender.fileout.layout=org.apache.log4j.PatternLayout
log4j.appender.fileout.layout.ConversionPattern=%d [%t] (%F:%L) %-5p %c - %m%n
itorAppender=org.apache.log4j.DailyRollingFileAppender
itorAppender.File=mtlogs/
itorAppender.layout=org.apache.log4j.PatternLayout
itorAppender.layout.ConversionPattern=%m%n
itorAppender.DatePattern='.'yyyy-MM-dd-HH
itorAppender.BufferedIO=true
#Buffer单位为字节,默认是8K
itorAppender.BufferSize=8192
itorLogger=false
这个选项⽤于控制监控logger的⽇志不会输出到rootlogger,否则⽆论会产⽣许多重复的数据,同时也会影响性能;
2)itorAppender.DatePattern='.'yyyy-MM-dd-HH
这个选项⽤于告诉
3)itorAppender.BufferedIO=true
itorAppender.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表达⼀下:
<?xml version="1.0" encoding="UTF-8"?>
<log4j:configuration debug="true">
<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/"/>
</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/"/>
<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>
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论