You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Log4J2自定义级别日志重复输出问题求助

问题分析与解决方案

问题根源

你遇到的重复输出问题,核心是对Log4j2中AppenderRef的level属性功能理解有误:

  • 该属性并非"仅输出指定级别的日志",而是设置Appender接收的最低级别阈值——只要日志级别对应的数值大于等于该阈值,就会被该Appender输出。
  • 结合你自定义的级别数值:A(450) < B(460),在Log4j2中,数值越大代表级别越严重(比如标准级别INFO=20000、ERROR=40000),这意味着A是比B更低级别的日志。

当你输出A级别日志时:

  • ConsoleAppender:Root级别为INFO,若A的实际数值>=INFO(你能输出说明是这种情况),则符合Root的级别要求,会输出。
  • AppenderForB的level="B"阈值是460,若你代码或配置中搞反了A、B的数值(比如A=460、B=450),A级别日志会满足AppenderForB的阈值条件,导致输出到B.log;反之如果A=450<B=460,理论上不会输出,但你的现象说明数值逻辑存在混淆。

同理,B级别日志会满足AppenderForA的阈值条件,输出到A.log。

正确解决方案

如果你需要让每个Appender仅输出对应级别的日志,必须使用精确级别匹配过滤器,而非依赖AppenderRef的level属性。修改配置如下:

1. 配置带LevelMatchFilter的Appender

<Appenders>
    <File name="AppenderForA" fileName="A.log">
        <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        <!-- 仅匹配A级别日志 -->
        <LevelMatchFilter level="A" onMatch="ACCEPT" onMismatch="DENY"/>
    </File>
    <File name="AppenderForB" fileName="B.log">
        <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        <!-- 仅匹配B级别日志 -->
        <LevelMatchFilter level="B" onMatch="ACCEPT" onMismatch="DENY"/>
    </File>
    <Console name="ConsoleAppender" target="SYSTEM_OUT">
        <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
</Appenders>

<Loggers>
    <CustomLevels>
        <Level name="A" intLevel="45000"/> <!-- 调整数值到INFO以上,确保Root能处理 -->
        <Level name="B" intLevel="46000"/>
    </CustomLevels>
    <Root level="INFO">
        <AppenderRef ref="ConsoleAppender"/>
        <AppenderRef ref="AppenderForA"/>
        <AppenderRef ref="AppenderForB"/>
        <!-- 移除AppenderRef上的level属性,由Appender内部的Filter控制 -->
    </Root>
</Loggers>

2. 代码中确保自定义级别与配置一致

public class UserService1 {
    private static final Logger LOGGER = LogManager.getLogger(UserService1.class);
    // 复用配置中定义的级别,避免数值不一致
    private static final Level LEVEL_A = Level.getLevel("A");
    private static final Level LEVEL_B = Level.getLevel("B");

    public void doSomething() {
        LOGGER.log(LEVEL_A, "This is a log message with Custom Level A.");
        LOGGER.log(LEVEL_B, "This is a log message with Custom Level B.");
    }
}

关键说明

  • LevelMatchFilter的onMatch="ACCEPT"和onMismatch="DENY"确保只有匹配指定级别的日志才会被该Appender输出。
  • 调整自定义级别数值到INFO(20000)以上,确保Root的INFO级别会处理这些日志;如果需要保留低数值,需将Root级别调整为TRACE或ALL。
  • 代码中直接通过Level.getLevel("A")获取配置中定义的级别,避免代码与配置的数值不一致导致的问题。

内容的提问来源于stack exchange,提问作者Tarun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 18:15:31