前言

Log4j2大家应该都不陌生,最近在研究日志框架的时候,发现其存在异步日志场景且性能很高,于是开始尝试,并记录踩坑点,注意方式为完全异步(AsyncLogger),混合异步这里不做展开。

环境

  • springboot 2.4.5
  • java 8

配置

1.引入其依赖并把 springboot 自带的日志框架 logback 排除掉:

<!--log4j2-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <!-- 排除默认日志框架 -->
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
        <!--异步,使用 log4j2 的 AsyncLogger 时需要包含 disruptor-->
        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <version>3.4.2</version>
        </dependency>

2.resourece 目录下创建logj2.xml(名称不要写错,spirng会默认加载读取):

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出 -->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数 -->
<configuration status="WARN" monitorInterval="30">

    <Properties>
    <!--你的日志目录,绝对路径 -->
        <Property name="LOG_PATH" value="/Users/zhangmingjia/logs/init-template"></Property>
    </Properties>

    <!--定义所有的appender -->
    <appenders>
        <!--输出控制台的配置 -->
        <console name="Console" target="SYSTEM_OUT" follow="false">
            <!--输出日志的格式 -->
            <PatternLayout charset="UTF-8"
                           pattern="[%date{yyyy-MM-dd HH:mm:ss.SSS}] %level [%thread][%file:%line] - %msg%n"/>
        </console>

        <!-- 打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档 -->
        <RollingFile name="RollingFileInfo" fileName="${LOG_PATH}/info.log"
                     filePattern="${LOG_PATH}/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <!-- 基于时间的触发策略。该策略主要是完成周期性的log文件封存工作 interval,integer型,指定两次封存动作之间的时间间隔;modulate,boolean型,说明是否对封存时间进行调制 -->
                <TimeBasedTriggeringPolicy interval="4" modulate="true"/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
        </RollingFile>

        <RollingFile name="RollingFileWarn" fileName="${LOG_PATH}/warn.log"
                     filePattern="${LOG_PATH}/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
            <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 -->
            <DefaultRolloverStrategy max="20"/>
        </RollingFile>

        <RollingFile name="RollingFileError" fileName="${LOG_PATH}/error.log"
                     filePattern="${LOG_PATH}/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
        </RollingFile>
    </appenders>
    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效 -->
    <loggers>
        <!--过滤掉spring和mybatis的一些无用的DEBUG信息 -->

     <logger name="org.springframework" level="info" ></logger>
     <logger name="org.mybatis" level="debug" ></logger>
            <root level="info" >
            <appender-ref ref="Console"/>
            <appender-ref ref="RollingFileInfo"/>
            <appender-ref ref="RollingFileWarn"/>
            <appender-ref ref="RollingFileError"/>
        </root>
    </loggers>
</configuration>

3.启动类设置

public class Application {
    public static void main(String[] args) {
    	//log4j2全异步日志配置设置
        System.setProperty("Log4jContextSelector", "org.apache.logging.log4j.core.async.AsyncLoggerContextSelector");
        SpringApplication.run(Application.class, args);
    }
}

4.启动如下,发现丢失类信息以及行信息

springboot异步时日志不会打印 springboot 异步记录日志_sed

原因分析:

这是由于默认情况下,异步日志记录器不会将 location 信息传递给 I/O 线程。

解决方案:

增加additivity配置和includeLocation配置即可,如下图做替换

<loggers>
        <!--过滤掉spring和mybatis的一些无用的DEBUG信息 -->
        <!--                <logger name="org.springframework" level="info" ></logger>-->
        <!--                <logger name="org.mybatis" level="debug" ></logger>-->

        <logger name="org.springframework" level="info" additivity="false"></logger>
        <logger name="org.mybatis" level="debug" additivity="false"></logger>


        <root level="info" includeLocation="true">
            <!--        <root level="info">-->
            <appender-ref ref="Console"/>
            <appender-ref ref="RollingFileInfo"/>
            <appender-ref ref="RollingFileWarn"/>
            <appender-ref ref="RollingFileError"/>
        </root>

    </loggers>

效果:

springboot异步时日志不会打印 springboot 异步记录日志_java_02