springboot log4j2 同步&异步使用 log4j2.xml配置

22 篇文章 0 订阅
本文详细介绍了如何在Spring Boot中排除默认的日志框架并引入Log4j2,通过XML配置文件设置了日志级别、全局变量、Appenders(包括控制台输出和文件归档)、过滤器、日志格式、归档策略和AsyncLogger异步日志。还通过并发测试展示了不同配置下日志丢失的问题。
摘要由CSDN通过智能技术生成

依赖

需要排除掉springboot自带的logback和logging的starter

 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
     <exclusions>
         <exclusion>
             <artifactId>spring-boot-starter-logging</artifactId>
             <groupId>org.springframework.boot</groupId>
         </exclusion>
         <exclusion>
             <!--log4j-slf4j-impl与 logback-classic包不兼容,删除这个包 -->
             <groupId>ch.qos.logback</groupId>
             <artifactId>logback-classic</artifactId>
         </exclusion>
     </exclusions>
 </dependency>
        
 <dependency> <!-- 引入log4j2依赖 -->
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-log4j2</artifactId>
 </dependency>

 <dependency> <!-- 异步依赖 -->
     <groupId>com.lmax</groupId>
     <artifactId>disruptor</artifactId>
     <version>3.4.4</version>
 </dependency>

XML配置

在resources下创建log4j2.xml

XML大致标签架构(表达的比较直白)

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"   monitorInterval="5">
    <Properties>
	全局变量		
    </Properties>

    <Appenders>
        <console >项目控制台设置</console>
        <RollingRandomAccessFile  >可归档的日志设置(推荐)</RollingRandomAccessFile >
        <RollingFile >可归档的日志设置</RollingFile>
    </Appenders>
    <Loggers>
      
        <Logger >部分日志设置</Logger>

        <AsyncLogger >异步日志</AsyncLogger>
        <Root >
            <AppenderRef />全局日志
        </Root>
    </Loggers>
</Configuration>

Configuration

level日志级别: ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF

 <Configuration status="WARN" status="info"  monitorInterval="5">

monitorInterval:xx秒 当xml变化时无需重启,热更新设置
status:Log4j2内部日志的输出级别

Properties 全局变量配置

全局变量配置 ${sys:catalina.home}为tomcat部署路径,例如:/data/tomcat。

<Properties>
        <!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符-->
        <!--
            %[format]conversion{param}
            % 必须,起始符号。
            [format] 可选,部分conversion需要,用于指定输出宽度,对齐方式等
            conversion 必须,转换符号
            param 可选,部分转换符号需要指定参数,如日期格式等
            例如:
            %d{yyyy-MM-dd HH:mm:ss} 打印当前时间
            %thread表示线程名
            %-5level 打印日记级别,并且占5个字符宽度
            %class{36} class路径
            %M 打印方法名称
            %logger{360} 表示 Logger 名字最长36个字符
        -->
        <property name="LOG_PATTERN" value="%date{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{360} - %msg%n" />
        <!-- 定义日志存储的路径,不要配置相对路径 -->
        <property name="FILE_PATH" value="/logs" />
        <property name="FILE_NAME" value="myApp" />
    </Properties>

Appenders 日志定义

定义日志输出目的地,内容和格式等

console 控制台输出

        <!--控制台输出日志的格式 target:SYSTEM_OUT 或 SYSTEM_ERR===============================-->
        <console name="Console" target="SYSTEM_OUT">
            <!--PatternLayout:输出格式,不设置默认为:%m%n.-->
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="OFF" onMatch="ACCEPT" onMismatch="DENY"/>
        </console>

File 自定义所有log信息

        <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用====-->
                <File name="Filelog" fileName="${FILE_PATH}/application.log" append="false">
                    <PatternLayout pattern="${LOG_PATTERN}"/>
                </File>

RollingRandomAccessFile

输出日志配置

  • name:Appender名称在标签内引用
  • immediateFlush:log4j2接收到日志事件时,是否立即将日志刷到磁盘。默认为true。
  • fileName:日志存储路径
  • filePattern:历史日志封存路径。其中%d{yyyyMMddHH}表示了日志的时间单位,log4j2自动识别zip等后缀,表示历史日志需要压缩。
  • immediateFlush:磁盘刷新方式 设置为false效率很高,但是日志存储会丢失亲测1w数据丢失3000+
<RollingRandomAccessFile  name="RollingFileInfo" fileName="${FILE_PATH}/info.log" immediateFlush="false"
                                  filePattern="${FILE_PATH}/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz">
日志过滤
  • level,表示最低接受的日志级别,博客配置的为INFO,即我们期望打印INFO级别以上的日志。
  • onMatch,表示匹配该级别及以上(>=)一般为ACCEPT=接受。
  • onMismatch,表示匹配该级别以下 (<)。一般为DENY=拒绝。NEUTRAL=中立。
ThresholdFilter 阈值过滤器(级别)
<ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
TimeFilter 时间过滤
<TimeFilter start="08:00:00" end="08:30:00" onMatch="ACCEPT" onMismatch="DENY" />
RegexFilter 匹配过滤
<RegexFilter regex=".* test .*" onMatch="NEUTRAL" onMismatch="DENY"/>
MarkerFilter 高亮过滤
<MarkerFilter marker="testError" onMatch="ACCEPT" onMismatch="DENY"/>

使用方法

private static Logger logger = LoggerFactory.getLogger(xxx.class);
private static final Marker MARKER = MarkerFactory.getMarker("testError");
    public static void main(String...strings) {
        logger.error(MARKER,  "Test message~");
        logger.error("没有指定marker,该条日志会被过滤掉");
    }

嵌套Filters 可实现只接收info级别(无warn 无error)

  <Filters>
      <ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
      <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
  </Filters>
PatternLayout 日志格式
        %[format]conversion{param}
        % 必须,起始符号。
        [format] 可选,部分conversion需要,用于指定输出宽度,对齐方式等
        conversion 必须,转换符号
        param 可选,部分转换符号需要指定参数,如日期格式等
        例如:
        %d{yyyy-MM-dd HH:mm:ss} 打印当前时间
        %thread表示线程名
        %-5level 打印日记级别,并且占5个字符宽度
        %class{36} class路径
        %M 打印方法名称
        %logger{360} 表示 Logger 名字最长36个字符

引用全局变量

<PatternLayout pattern="${LOG_PATTERN}"/>
Policies 归档设置
 <Policies> <!--归档设置-->
     <OnStartupTriggeringPolicy minSize="1"/>
     <TimeBasedTriggeringPolicy interval="10" modulate="false" />
     <SizeBasedTriggeringPolicy size="100MB"/>
     <CronTriggeringPolicy schedule="0 0 8 * * ?"/>
 </Policies>
OnStartupTriggeringPolicy 启动应用程序时触发归档动作

minSize:文件大小(字节数),minSize=0,强制创建一个空的归档文件。

<OnStartupTriggeringPolicy minSize="1"/>
TimeBasedTriggeringPolicy 按时间间隔归档
 interval=时间间隔 单位由filePattern的%d日期格式指定
          %d{yyyy-MM-dd-HH} 匹配单位是时
         %d{yyyy-MM-dd}匹配单位是日
         %d{yyyy-MM-dd HHmmss}匹配单位是秒
 modulate=boolean 是否对interval取模,决定了下一次触发的时间点
         以小时举例,当前系统时间是上午3点,interval是4,
         如果modulate=true,那么上午4.8.12...点触发归档,()
         如果modulate=false,那么上午7.11...点触发下一次归档,之后每间隔4小时触发一次归档
 maxRandomDelay=单位:秒,下一次触发时间会在interval基础上,增加一个随机的毫秒数Random.nextLong(0, 1+maxRandomDelay*1000)

<TimeBasedTriggeringPolicy interval="10" modulate="false" />
SizeBasedTriggeringPolicy 按照日志文件的大小

size:当前日志文件的最大size,支持单位:KB/MB/GB

<SizeBasedTriggeringPolicy size="100MB"/>

CronTriggeringPolicy 按照时间间隔来触发

schedule:cron语法 evaluateOnStartup=true,
在TriggeringPolicy初始时评估是否需要补充一次归档;false,不检测

<CronTriggeringPolicy schedule="0 0 8 * * ?"/>


DefaultRolloverStrategy 历史日志配置
 <!--保存24小时历史日志,但不想用文件索引
 1. age的单位:D天、H小时、M分、S秒
 2. basePath表示日志存储的基目录,maxDepth=“1”当前目录。其他maxDepth=2。
 -->
 <DefaultRolloverStrategy max="24">
     <Delete basePath="${FILE_NAME}/${FILE_PATH}" maxDepth="2">
         <IfFileName glob="*/msg.*.zip" />
         <IfLastModified age="24H" />
     </Delete>
 </DefaultRolloverStrategy>

Loggers

Logger 日志配置
  • name:用来指定该Logger所适用的类或者类所在的包全路径,继承自Root节点.
  • AppenderRef:关联的Appender
  • additivity:logEvent的传递性。true LogEvent处理后传递给父Logger打印。false LogEvent处理后不再向上传递给父Logger。

过滤掉mybatis的信息 显示spring信息

  <Logger name="org.springframework" level="info" additivity="true">
      <AppenderRef ref="Console"/>
  </Logger>
  <logger name="org.mybatis" level="info" additivity="false">
      <AppenderRef ref="Console"/>
  </logger>

AsyncLogger 异步日志

添加依赖

  <dependency>
      <groupId>com.lmax</groupId>
      <artifactId>disruptor</artifactId>
      <version>3.4.4</version>
  </dependency>

includelocation="false" 关闭日志行号信息
  <AsyncLogger name="asyncLoggerWarn" additivity="FALSE" level="warn" includelocation="false">
      <AppenderRef ref="msgAppender" />
  </AsyncLogger>

ROOT 日志默认打印到控制台
        <Root level="info" includeLocation="true">
            <AppenderRef ref="引用的日志配置名称"/>
        </Root>

整体XML

配置同步info,warn
配置异步info,warn
高亮error

<?xml version="1.0" encoding="UTF-8"?>

<Configuration status="WARN"   monitorInterval="5">
    <Properties>
        <property name="LOG_PATTERN" value="%date{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{360} - %msg%n" />
        <property name="FILE_PATH" value="/logs" />
        <property name="FILE_NAME" value="myApp" />
    </Properties>

    <Appenders>
        <!--控制台输出日志的格式 target:SYSTEM_OUT 或 SYSTEM_ERR===============================-->
        <console name="Console" target="SYSTEM_OUT">
            <!--PatternLayout:输出格式,不设置默认为:%m%n.-->
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="OFF" onMatch="ACCEPT" onMismatch="DENY"/>
        </console>
        <!-- ++++++++++++++++++ 配置Info ++++++++++++++++++++++++++++     -->
        <RollingRandomAccessFile name="RollingRAF_info" fileName="${FILE_PATH}/info.log" immediateFlush="true"
                                  filePattern="${FILE_PATH}/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz">
            <Filters>
                <ThresholdFilter level="WARN" onMatch="DENY" onMismatch="ACCEPT"/>
                <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="ACCEPT"/>
                <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="10" modulate="false" />
                <SizeBasedTriggeringPolicy size="100MB"/>
            </Policies>
            <DefaultRolloverStrategy max="24"/>
        </RollingRandomAccessFile>
        <!-- ++++++++++++++++++ 配置warn ++++++++++++++++++++++++++++     -->
        <RollingFile name="RollingRAF_warn" fileName="${FILE_PATH}/warn.log"
                     filePattern="${FILE_PATH}/${FILE_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.gz">
            <Filters>
                <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="100MB"/>
            </Policies>
            <DefaultRolloverStrategy max="15"/>
        </RollingFile>


        <!-- ++++++++++++++++++ 配置高亮的error ++++++++++++++++++++++++++++     -->
        <RollingFile name="RollingMarkerFilter" fileName="${FILE_PATH}/MarkerError.log" filePattern="${FILE_PATH}/${FILE_NAME}-MarkerError-%d{yyyy-MM-dd}_%i.log.gz">
            <MarkerFilter marker="MarkerError" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="100MB"/>
            </Policies>
            <DefaultRolloverStrategy max="15"/>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Logger name="org.springframework" level="info" additivity="false">
            <AppenderRef ref="Console"/>
        </Logger>
        <logger name="org.mybatis"
                level="info" additivity="false">
            <AppenderRef ref="Console"/>
        </logger>
        <!--AsyncLogger 异步日志 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-->
        <AsyncLogger name="asyncLoggerInfo" additivity="FALSE" level="info">
            <appender-ref ref="RollingRAF_info" />
        </AsyncLogger>
        <AsyncLogger name="asyncLoggerWarn" additivity="FALSE" level="warn">
            <appender-ref ref="RollingRAF_warn" />
        </AsyncLogger>
        <Root level="info" includeLocation="true">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="RollingRAF_info"/>
            <AppenderRef ref="RollingRAF_warn"/>
            <AppenderRef ref="RollingMarkerFilter"/>
        </Root>
    </Loggers>

</Configuration>

测试。。。。同步异步的坑

 * 测试 100Th》for10   丢失
 * 测试 10*10  OK
 * 测试 100*1  丢失

· 亲测 RollingRandomAccessFile immediateFlush=false 同步异步都会丢失数据
· AsyncLogger 无论使用 RollingRandomAccessFile || RollingFile 都会丢失数据
只有同步并发 immediateFlush=true || RollingFile 数据量才是完整的

    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.5.1</version>
    </dependency>

import cn.hutool.core.lang.Console;
import cn.hutool.core.thread.ConcurrencyTester;
import cn.hutool.core.thread.ThreadUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.*;
import org.junit.Test;
import org.slf4j.MarkerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.HashMap;



@SpringBootTest
@Slf4j
public class Log4j2DemoApplicationTests {

    private static org.apache.logging.log4j.Logger logger = LogManager.getLogger(Log4j2DemoApplicationTests.class);
    private static org.apache.logging.log4j.Logger asyncLoggerInfo = LogManager.getLogger("asyncLoggerInfo");
    private static org.apache.logging.log4j.Logger asyncLoggerWarn = LogManager.getLogger("asyncLoggerWarn");
    private static org.slf4j.Marker MARKER= MarkerFactory.getMarker("MarkerError");
    public static HashMap<String,Object> map=null;
    static{
        map=new HashMap<>();
        map.put("name","张三");
        map.put("age","18");
    }

    /**
     * 同步Info  RollingRandomAccessFile
     * 结果:数据量完整   用时:11200
     */
    @Test
    public void log4jSyncInfo() {
        ConcurrencyTester tester = ThreadUtil.concurrencyTest(100, () -> {
            for (int i = 0; i < 10000; i++) {
                logger.info(map);
            }
        });
        Console.log(tester.getInterval());
    }
    /**
     * 同步warn  RollingFile
     * 结果:数据   用时:12386
     */
    @Test
    public void log4jSyncWarn() {
        ConcurrencyTester tester = ThreadUtil.concurrencyTest(100, () -> {
            for (int i = 0; i < 10000; i++) {
                logger.warn(map);
            }
        });
        Console.log(tester.getInterval());
    }
    /**
     * 同异步info
     * 结果:丢失26万   用时:4125
     * 测试 100Th》for10   丢失
     * 测试 10*10  OK
     * 测试 100*1  丢失
     */
    @Test
    public void asyLog4j2Info() {
        ConcurrencyTester tester = ThreadUtil.concurrencyTest(100, () -> {
            for (int i = 0; i < 10000; i++) {
                asyncLoggerInfo.info(map);
            }
        });
        Console.log(tester.getInterval());
    }
    /**
     * 同异步warn
     * 结果:丢失26万   用时:3914
     */
    @Test
    public void asyLog4j2Warn() {
        ConcurrencyTester tester = ThreadUtil.concurrencyTest(100, () -> {
            for (int i = 0; i < 10000; i++) {
                asyncLoggerWarn.warn(map);
            }
        });
        Console.log(tester.getInterval());
    }
    @Test
    public void MARKER() {
        log.error(MARKER,  "Test message~");
        log.error("没有指定marker,该条日志会被过滤掉");
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值