如何在Java应用中用Log4j和SLF4J过滤重复日志消息?
解决Log4j过滤重复日志消息的方案
Log4j本身没有内置的重复日志过滤组件,但可以通过自定义过滤器实现高效去重,同时满足你简化配置、兼容多Appender的需求。以下是具体实现方案:
一、自定义重复日志过滤器
1. 实现Filter类
编写一个Log4j插件式过滤器,通过记录每条日志的最后输出时间,过滤超时范围内的重复消息(可配置超时时间,避免误过滤不同场景的相同日志):
import org.apache.logging.log4j.core.Filter; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.config.Node; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.config.plugins.PluginAttribute; import org.apache.logging.log4j.core.config.plugins.PluginFactory; import org.apache.logging.log4j.core.filter.AbstractFilter; import java.util.concurrent.ConcurrentHashMap; @Plugin(name = "DuplicateMessageFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true) public class DuplicateMessageFilter extends AbstractFilter { private final ConcurrentHashMap<String, Long> lastLoggedRecords = new ConcurrentHashMap<>(); private final long timeoutMillis; private DuplicateMessageFilter(long timeoutMillis, Result onMatch, Result onMismatch) { super(onMatch, onMismatch); this.timeoutMillis = timeoutMillis; } @Override public Result filter(LogEvent event) { String formattedMsg = event.getMessage().getFormattedMessage(); long now = System.currentTimeMillis(); Long lastTime = lastLoggedRecords.get(formattedMsg); if (lastTime != null && (now - lastTime) < timeoutMillis) { return onMatch; // 重复消息,执行过滤 } lastLoggedRecords.put(formattedMsg, now); return onMismatch; // 非重复消息,允许输出 } @PluginFactory public static DuplicateMessageFilter createFilter( @PluginAttribute(value = "timeoutMillis", defaultLong = 300000) long timeoutMillis, @PluginAttribute(value = "onMatch", defaultString = "DENY") Result onMatch, @PluginAttribute(value = "onMismatch", defaultString = "ACCEPT") Result onMismatch) { return new DuplicateMessageFilter(timeoutMillis, onMatch, onMismatch); } }
2. 配置log4j.xml
将自定义过滤器添加到需要去重的Appender中(比如ConsoleAppender),配置简单且可复用至其他Appender:
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> <!-- 自定义重复消息过滤器:超时5分钟,重复则拒绝输出 --> <DuplicateMessageFilter timeoutMillis="300000" onMatch="DENY" onMismatch="ACCEPT"/> </Console> <!-- 后续扩展其他Appender时,直接添加该过滤器即可 --> <!-- <File name="File" fileName="app.log"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> <DuplicateMessageFilter timeoutMillis="300000"/> </File> --> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="Console"/> </Root> </Loggers> </Configuration>
二、关键配置说明
timeoutMillis:设置重复消息的判断超时时间,默认5分钟。如果需要严格过滤连续重复的日志(比如循环中重复输出的同一条日志),可将值设为0,但可能会误过滤不同时间点的相同业务日志,建议根据实际场景调整。onMatch:重复消息的处理策略,设为DENY即过滤重复日志;onMismatch设为ACCEPT即允许非重复日志输出。- 多Appender兼容:后续新增的Appender只需添加
<DuplicateMessageFilter>标签即可实现去重,无需额外修改逻辑。
内容的提问来源于stack exchange,提问作者aj.savvi
相关产品推荐
相关产品推荐

