关于log4j2的异步日志输出方式
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
关于log4j2的异步⽇志输出⽅式
⽬录
log4j2的异步⽇志输出⽅式
第⼀种实现异步⽅式AsyncAppender
第⼆种实现异步⽅式AsyncLogger
log4j2异步注意事项
log4j2异步类型
⼩提⽰
log4j2的异步⽇志输出⽅式
使⽤log4j2的同步⽇志进⾏⽇志输出,⽇志输出语句与程序的业务逻辑语句将在同⼀个线程运⾏。
⽽使⽤异步⽇志进⾏输出时,⽇志输出语句与业务逻辑语句并不是在同⼀个线程中运⾏,⽽是有专门的线程⽤于进⾏⽇志输出操作,处理业务逻辑的主线程不⽤等待即可执⾏后续业务逻辑。
Log4j2中的异步⽇志实现⽅式有AsyncAppender和AsyncLogger两种。
其中:
AsyncAppender采⽤了ArrayBlockingQueue来保存需要异步输出的⽇志事件;
AsyncLogger则使⽤了Disruptor框架来实现⾼吞吐。
第⼀种实现异步⽅式AsyncAppender
AsyncAppender直接在log4j2的xml的配置⽂件中配置,注意下⾯代码的注释位置
<?xml version="1.0" encoding="UTF-8"?>
<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="MyFile"/>
</Async>
<!--异步AsyncAppender配置完毕,需要⼏个配置⼏个-->
</Appenders>
<Loggers>
<Root level="error">
<!--此处如果引⽤异步AsyncAppender的name就是异步输出⽇志-->
<!--此处如果引⽤Appenders标签中RollingFile的name就是同步输出⽇志-->
<AppenderRef ref="Async"/>
</Root>
</Loggers>
</Configuration>
重点内容全在上⾯代码的注释中,AsyncAppender的配置就在xml⽂件中实现,⽆需单独引⽤包来⽀持.配置AsyncAppender 后,⽇志事件写⼊⽂件的操作将在单独的线程中执⾏。
AsyncAppender的常⽤参数
参数名类型说明
name String Async Appender的名字
AppenderRef String异步调⽤的Appender的名字,可以配置多个
默认为true。
如果为true,appender将⼀直等待直到queue中有空闲;如果为false,当队列满的时候,
blocking boolean⽇志事件将被丢弃。
(如果配置了error appender,要丢弃的⽇志事件将由error appender处理)参数名类型说明
bufferSize integer队列中可存储的⽇志事件的最⼤数量,默认为128
第⼆种实现异步⽅式AsyncLogger
Log4j2中的AsyncLogger的内部使⽤了Disruptor框架。
Disruptor简介
Disruptor是英国外汇交易公司LMAX开发的⼀个⾼性能队列,基于Disruptor开发的系统单线程能⽀撑每秒600万订单。
⽬前,包括Apache Strom、Log4j2在内的很多知名项⽬都应⽤了Disruptor来获取⾼性能。
Disruptor框架内部核⼼数据结构为RingBuffer,其为⽆锁环形队列。
Disruptor为什么这么快?
lock-free-使⽤了CAS来实现线程安全
使⽤缓存⾏填充解决伪共享问题
⾸先在pom单中应⽤相关的包
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>
第⼆步在log4j2的xml⽂件中配置AsyncLogger
log4j2.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug" name="MyApp" packages="">
<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="logs/app.log"
filePattern="logs/app-%d{yyyy-MM-dd HH}.log">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="500MB"/>
</Policies>
</RollingFile>
<RollingFile name="RollingFile2" fileName="logs/app2.log"
filePattern="logs/app2-%d{yyyy-MM-dd HH}.log">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="500MB"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<!--上⾯的配置都和原配置⼀样,就是在下⽅这直接定义AsyncLogger,他的name在java类中被引⽤即可-->
<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>
</Configuration>
java代码如下:
public class Main {
public static void main(String args[]) {
//引⽤com.meituan.Main⽇志输出器
Logger logger = LogManager.getLogger(Main.class);
//引⽤的名为RollingFile2的异步AsyncLogger
Logger logger2 = LogManager.getLogger("RollingFile2");
Person person = new Person("Li", "lei");
("hello, {}", person);
("good bye, {}", person);
}
上述log4j2.xml中配置了两个AsyncLogger,名字分别为com.meituan.Main和RollingFile2。
并且,在main⽅法中分别使⽤两个logger来输出两条⽇志。
在加载log4j2.xml的启动阶段,如果检测到配置了AsyncRoot或AsyncLogger,将启动⼀个disruptor实例。
log4j2异步注意事项
log4j2异步类型
1) 使⽤<Async>标签
⽰例:
<Async name="asyncKafkaLog">
<AppenderRef ref="Failover" />
</Async>
注意事项:此类异步队列是BockingQueue,队列默认⼤⼩是128
2) 使⽤<AsyncLogger>标签
⽰例:
<AsyncLogger name="kafkaLogger" level="trace" includeLocation="false">
<AppenderRef ref="Failover"/>
</AsyncLogger>
注意事项:此类异步队列是Disruptor队列默认⼤⼩是4096
3) 使⽤ JVM参数
⽰例:
#启动参数⽅式
-DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
#代码⽅式
System.setProperty("Log4jContextSelector", "org.apache.logging.log4j.core.async.AsyncLoggerContextSelector");
注意事项:此类异步是全量异步,log4j配置⽂件⾥所有logger都⾃动异步,使⽤异步队列为Disruptor,队列默认⼤⼩4096⼩提⽰
① Disruptor队列性能远胜于BlockingQueue,这也是log4j2性能提升的重要原因之⼀
②如果启⽤了全量异步,⼜使⽤了<AsyncLogger>会如何?
log4j2会新建两个Disruptor队列,<AsyncLogger>之流使⽤⼀个,其他的使⽤另外⼀个,所以建议将可能发⽣阻塞的logger归类使⽤⼀个Disruptor,毕竟是队列,⼀个阻塞了其他的得乖乖等着
③如果默认队列长度不⾜咋办?
#第⼀步:加⼤两个Disruptor队列的长度
-DAsyncLogger.RingBufferSize=262144
-DAsyncLoggerConfig.RingBufferSize=262144
#第⼆步:设置队列满了时的处理策略:丢弃,否则默认blocking,异步就与同步⽆异了
-Dlog4j2.AsyncQueueFullPolicy=Discard
以上为个⼈经验,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。