关于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的名字,可以配置多个
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实例。

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

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 老生常谈Log4j和Log4j2的区别(推荐)

    相信很多程序猿朋友对log4j都很熟悉,log4j可以说是陪伴了绝大多数的朋友开启的编程.我不知道log4j之前是用什么,至少在我的生涯中,是log4j带我开启的日志时代. log4j是Apache的一个开源项目,我们不去考究它的起源时间,但是据我了解,log4j 1已经不再更新了. 回顾log4j,曾给我们留下了多少的回忆,我记得早些年,那时候mybatis还是叫ibatis的时候,我为了配置ibatis控制台打印日志,纠结了多少个夜晚,最后配置出来时的那种喜悦感.废话不多说,下面我就以列举的

  • Springboot2.x 使用 Log4j2 异步打印日志的实现

    介绍 目前常用的 Java 日志框架有 Log4j.Logback.Log4j2 ,性能方面推荐使用异步的 Log4j2,具体对比不多做分析,前人早已完成耕荒,我们就站在巨人的肩膀上来看看如何实操. SLF4J,简单日志门面(Simple Logging Facade for Java),是一个用于日志系统的简单 Facade,不是具体的日志解决方案,而是通过Facade Pattern提供一些Java logging API,我的理解是它和 Log4j.Logback.Log4j2 的关系类似

  • log4j2日志异步打印(实例讲解)

    log4j2支持日志的异步打印,日志异步输出的好处在于,使用单独的进程来执行日志打印的功能,可以提高日志执行效率,减少日志功能对正常业务的影响. 异步日志在程序的classpath需要加载disruptor-3.0.0.jar或者更高的版本. Asynchronous Loggers是一个新增特性在Log4j 2 ,可以实现完全异步也可以和同步混合使用,还可以只异步化Appender,以提升系统性能,官方数据显示混合没有完全异步化效果好. 1,完全异步模式: 这种异步日志方式,不需要修改原来的配

  • Spring Boot异步输出Logback日志方法详解

    一.介绍 1.1 Logback Logback是由log4j创始人设计的另一个开源日志组件,它分为下面下个模块: logback-core:其它两个模块的基础模块 logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API使你可以很方便地更换成其它日志系统如log4j或JDK14 Logging logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能 1.2 日志级别 包括:TRACE.DEBUG.INFO.WARN

  • 关于log4j2的异步日志输出方式

    目录 log4j2的异步日志输出方式 第一种实现异步方式AsyncAppender 第二种实现异步方式AsyncLogger log4j2异步注意事项 log4j2异步类型 小提示 log4j2的异步日志输出方式 使用log4j2的同步日志进行日志输出,日志输出语句与程序的业务逻辑语句将在同一个线程运行. 而使用异步日志进行输出时,日志输出语句与业务逻辑语句并不是在同一个线程中运行,而是有专门的线程用于进行日志输出操作,处理业务逻辑的主线程不用等待即可执行后续业务逻辑. Log4j2中的异步日志

  • Springboot异常日志输出方式

    目录 lombok插件使用 统一异常处理 统一日志输出 配置日志级别↓ Logback日志↓ 配置logback日志↓ 安装idea彩色日志插件:grep-console 复制粘贴即可 lombok插件使用 引入依赖,在项目中使用Lombok可以减少很多重复代码的书写.比如说getter/setter/toString等方法的编写 ↓ <!--lombok用来简化实体类--> <dependency> <groupId>org.projectlombok</gro

  • Slf4j+logback实现JSON格式日志输出方式

    目录 Slf4j+logback实现JSON格式日志输出 依赖 logback 记录JSON日志 Slf4j+logback实现JSON格式日志输出 依赖 <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.8</version> <scope>provided</s

  • gateway、webflux、reactor-netty请求日志输出方式

    目录 gateway.webflux.reactor-netty请求日志输出 场景 思路 解决方案 spring-webflux.gateway.springboot-start-web问题 Spring-webflux Spring-gateway gateway.webflux.reactor-netty请求日志输出 场景 在使用spring cloud gateway时想要输出请求日志,考虑到两种实现方案 方案一 官网中使用Reactor Netty Access Logs方案,配置“-D

  • springboot使用log4j2异步日志提升性能的实现方式

    目录 一.引入disruptor 二. 全局异步模式 三.异步/同步混合模式 同步日志的业务流程处理和日志打印是在同一个线程,日志打印的过程实际上是写文件IO的过程,这个过程是相对耗时的,并且会阻塞主线程的执行,只有日志打印完成后才会继续执行业务处理代码.如果日志量比较大,会影响主业务流程的处理效率.异步日志实现方式:将日志存入一个单独的队列中,有一个单独的线程从队列中获取日志并写入磁盘文件. 日志放入队列的耗时,肯定比磁盘写IO文件耗时要少的多得多,所以对主业务流程影响极小. 一个单独的线程进

  • log4j2的异步使用及添加自定义参数方式

    目录 log4j2异步使用及添加自定义参数 添加依赖(这里省略了版本号) 下面写一个生产可用的log4j2.xml的模板 补充知识 自定义日志格式 如何在日志中添加自己想传的参数? log4j 输入自定义参数 测试代码如下 log4j2异步使用及添加自定义参数 关于log4j2的性能和原理就不赘述了,这篇主要讲使用,配置文件解读,和添加自定义参数,偏应用的一篇文章. 相比与其他的日志系统,log4j2丢数据这种情况少:disruptor技术,在多线程环境下,性能高于logback等10倍以上:利

  • Python日志:自定义输出字段 json格式输出方式

    最近有一个需求:将日志以json格式输出, 并且有些字段是logging模块没有的.看了很多源码和资料, 终于搞定, 抽取精华分享出来, 一起成长. import json import logging class JsonFilter(logging.Filter): ip = 'IP' source = 'APP' def filter(self, record): record.ip = self.ip record.username = self.source return True i

  • Spring Boot mybatis-config 和 log4j 输出sql 日志的方式

    依赖 <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> 两种配置log4j的方式: 一定要新建一个log4j.properties文件 在yaml中直接配置 在mybatis-config中配置 mapper-locations 貌似不管用依旧需

  • log4j控制日志输出文件名称的两种方式小结

    目录 log4j控制日志输出文件名称 1. 第一种方式 2. 第二种方式(这种方式亲测正确) 如何随心所欲地自定义log4j输出格式 log4j控制日志输出文件名称 1. 第一种方式 在类对象中用如下方式定义logger变量 private static Logger logger = Logger.getLogger("lemmaXml"); 这样通过名称的方式获取logger,需要在log4j.properties文件中定义一个名称为lemmaXml的appender,配置如下:

  • 使用log4j2关闭debug日志

    目录 log4j2关闭debug日志 Log4J和slf4j的debug日志问题 log4j2关闭debug日志 最近项目引进入一个jar包,启动之之后debug日志就停不下来了,为了关闭这个日志花了半个下午.总结以下处理方式,以供大家参考: 1.如果引入了logback的jar包,排除引用(我的项目里没有引用) <exclusions> <exclusion> <groupId>ch.qos.logback</groupId> <artifactId

随机推荐