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的名字,可以配置多个
blocking boolean 默认为true。如果为true,appender将一直等待直到queue中有空闲;如果为false,当队列满的时候,日志事件将被丢弃。(如果配置了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");
        logger.info("hello, {}", person);
        logger2.info("good bye, {}", person);
}

上述log4j2.xml中配置了两个AsyncLogger,名字分别为com.meituan.Main和RollingFile2。
并且,在main方法中分别使用两个logger来输出两条日志。
在加载log4j2.xml的启动阶段,如果检测到配置了AsyncRoot或AsyncLogger,将启动一个disruptor实例。

猜你喜欢

转载自blog.csdn.net/u011930054/article/details/103682392