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

如何在Log4j2配置中避免日志重复写入?

解决Log4j2日志重复写入问题

问题根源分析

你的配置存在两个核心问题导致日志重复:

  1. Root Logger重复关联Appender:Root节点同时引用了routingAppender、allFileAppender、errorFileAppender和consoleAppender,同一日志会被多个Appender分别写入对应文件。
  2. Routing Appender结构违规:将allFileAppender、errorFileAppender、consoleAppender嵌套在routingAppender内部不符合Log4j2规范,Routing Appender仅允许Routes和Script作为子元素,这些嵌套的Appender不会被路由逻辑使用,反而会因为Root的直接引用重复输出。

修正后的配置方案

根据常见的日志分层需求(动态业务日志、全量日志、错误日志、控制台输出),调整配置如下:

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

<Configuration status="WARN">

    <Appenders>
        <!-- 默认业务日志Appender -->
        <RollingFile name="fileAppender" fileName="log/application.log" filePattern="log/application.log.%i">
            <PatternLayout>
                <pattern>%p{length=1} %d{yyMMdd HH:mm:ss.SSS} [%t] %c{1} %m%n</pattern>
            </PatternLayout>
            <Policies>
                <SizeBasedTriggeringPolicy size="${LOG_FILE_SIZE}"/>
            </Policies>
            <DefaultRolloverStrategy max="${BACKUP_INDEX}"/>
            <!-- 过滤ERROR级别,避免与错误日志文件重复 -->
            <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="ACCEPT"/>
        </RollingFile>

        <!-- 动态业务日志Appender(根据MDC路由) -->
        <Routing name="routingAppender">
            <Routes pattern="$${mdcContext}">
                <Route>
                    <RollingFile name="dynamicFileAppender" fileName="log/application.${mdcContext}.log" filePattern="log/application.${mdcContext}.log.%i">
                        <PatternLayout>
                            <pattern>%p{length=1} %d{yyMMdd HH:mm:ss.SSS} [%t] %c{1} %m%n</pattern>
                        </PatternLayout>
                        <Policies>
                            <SizeBasedTriggeringPolicy size="${LOG_FILE_SIZE}"/>
                        </Policies>
                        <DefaultRolloverStrategy max="${BACKUP_INDEX}"/>
                        <!-- 过滤ERROR级别 -->
                        <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="ACCEPT"/>
                    </RollingFile>
                </Route>
                <Route ref="fileAppender" key="$${mdcContext}"/>
            </Routes>
        </Routing>

        <!-- 全量日志Appender(包含所有级别) -->
        <RollingFile name="allFileAppender" fileName="log/application.all.log" filePattern="log/application.all.log.%i">
            <PatternLayout>
                <pattern>%p{length=1} %d{yyMMdd HH:mm:ss.SSS} [%t] %c{1} - %m%n</pattern>
            </PatternLayout>
            <Policies>
                <SizeBasedTriggeringPolicy size="${LOG_FILE_SIZE}"/>
            </Policies>
            <DefaultRolloverStrategy max="${BACKUP_INDEX}"/>
        </RollingFile>

        <!-- 错误日志Appender(仅ERROR级别) -->
        <RollingFile name="errorFileAppender" fileName="log/application.error.log" filePattern="log/application.error.log.%i">
            <PatternLayout>
                <pattern>%p{length=1} %d{yyMMdd HH:mm:ss.SSS} [%t] %c{1} - %m%n</pattern>
            </PatternLayout>
            <Policies>
                <SizeBasedTriggeringPolicy size="${LOG_FILE_SIZE}"/>
            </Policies>
            <DefaultRolloverStrategy max="${BACKUP_INDEX}"/>
            <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
        </RollingFile>

        <!-- 控制台输出(仅ERROR级别) -->
        <Console name="consoleAppender" target="SYSTEM_OUT">
            <PatternLayout pattern="%p{length=1} %d{yyMMdd HH:mm:ss.SSS} [%t] %c{1} %m%n"/>
            <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
        </Console>

    </Appenders>

    <Loggers>
        <Logger name="org.apache.commons" level="INFO"/>
        <Logger name="org.quartz.simpl.SimpleThreadPool" level="INFO"/>
        <Logger name="org.quartz.core.QuartzSchedulerThread" level="INFO"/>
        <Logger name="org.quartz.core.JobRunShell" level="ERROR"/>
        <Logger name="com.example" level="ERROR"/>
        <Logger name="org.apache.activemq.artemis.core.server" level="INFO"/>

        <!-- <Logger name="example.scheduler" level="debug">
            <AppenderRef ref="routingAppender"/>
        </Logger> -->

        <Root level="${LOGGING_LEVEL}">
            <AppenderRef ref="routingAppender"/>
            <AppenderRef ref="allFileAppender"/>
            <AppenderRef ref="errorFileAppender"/>
            <AppenderRef ref="consoleAppender"/>
        </Root>
    </Loggers>

</Configuration>

关键调整说明

  • 修正Appender结构:将allFileAppender、errorFileAppender、consoleAppender移至顶级<Appenders>标签下,符合Log4j2配置规范。
  • 添加级别过滤:
    • 给fileAppender和dynamicFileAppender添加ThresholdFilter,拒绝ERROR级别日志,避免业务日志文件混入错误日志。
    • 给errorFileAppender和consoleAppender添加ThresholdFilter,仅接受ERROR级别日志,确保只输出错误内容。
  • 保留全量日志:allFileAppender不添加过滤,收集所有级别的日志,满足归档需求。

如果你的需求是完全禁止同一日志出现在多个文件中(比如不需要全量日志),可以直接移除Root节点中的<AppenderRef ref="allFileAppender"/>,仅保留业务路由、错误日志和控制台输出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 18:17:02