Logback RollingFileAppender使用EvaluatorFilter时日志不滚动问题问询
Logback RollingFileAppender + EvaluatorFilter 日志不滚动问题分析与解决
我来帮你拆解这个问题——RollingFileAppender搭配EvaluatorFilter时日志无法滚动,绝大多数情况是Filter的逻辑影响了滚动触发的时机,毕竟Logback的滚动检查是和日志事件处理绑定的。
核心原因
Logback的RollingFileAppender默认只会在处理日志事件时触发滚动检查(比如检查文件大小是否达标、是否到了滚动时间点)。如果你的EvaluatorFilter配置了
onMismatch="DENY",并且大部分/所有日志事件都被这个Filter拒绝了,那么Appender根本没有机会执行滚动检查,自然不会触发日志滚动。
举个典型的错误配置(就是你说的无法滚动的那组):
<!-- 有问题的配置:onMismatch设为DENY,无符合条件日志时无法触发滚动 --> <appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>app.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <fileNamePattern>app.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <maxFileSize>10MB</maxFileSize> </rollingPolicy> <filter class="ch.qos.logback.core.filter.EvaluatorFilter"> <evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator"> <!-- 只允许com.example包下的INFO及以上日志通过 --> <expression>logger.startsWith("com.example") && level >= INFO</expression> </evaluator> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> <!-- 这里是关键问题点! --> </filter> <encoder> <pattern>%d %-5level %logger - %msg%n</pattern> </encoder> </appender>
而你说的能正常滚动的配置,大概率是没有设置onMismatch="DENY",或者Filter逻辑允许部分日志事件通过,从而触发了滚动检查。
解决方案
方案1:调整Filter链,保留滚动触发机会
把onMismatch从DENY改为NEUTRAL,然后添加一个最终的Filter来拒绝所有未匹配的日志。这样即使没有符合条件的业务日志,日志事件仍会经过Appender的处理流程(只是最后被拒绝写入),从而触发滚动检查:
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 滚动策略保持不变 --> <file>app.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <fileNamePattern>app.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <maxFileSize>10MB</maxFileSize> </rollingPolicy> <!-- 第一个Filter:匹配目标日志 --> <filter class="ch.qos.logback.core.filter.EvaluatorFilter"> <evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator"> <expression>logger.startsWith("com.example") && level >= INFO</expression> </evaluator> <onMatch>ACCEPT</onMatch> <onMismatch>NEUTRAL</onMatch> <!-- 改为NEUTRAL,让事件进入下一个Filter --> </filter> <!-- 第二个Filter:拒绝所有未匹配的日志 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>OFF</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <encoder> <pattern>%d %-5level %logger - %msg%n</pattern> </encoder> </appender>
方案2:添加心跳日志触发滚动检查
如果你的业务场景确实需要严格过滤所有非目标日志,可以专门添加一个定时心跳日志,让它通过Filter,定期触发滚动检查:
- 调整Filter表达式,允许心跳日志通过:
<filter class="ch.qos.logback.core.filter.EvaluatorFilter"> <evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator"> <expression> (logger.startsWith("com.example") && level >= INFO) || (logger.equals("com.example.LogHeartbeat") && level == TRACE) </expression> </evaluator> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter>
- 在代码中添加定时心跳逻辑(以Spring为例):
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class LogHeartbeat { private static final Logger HEARTBEAT_LOGGER = LoggerFactory.getLogger("com.example.LogHeartbeat"); // 每小时输出一次心跳日志,触发滚动检查 @Scheduled(fixedRate = 3600000) public void triggerRolloverCheck() { HEARTBEAT_LOGGER.trace("Log heartbeat to trigger rollover"); } }
方案3:手动触发滚动(针对时间滚动场景)
如果你的滚动策略是基于时间的,可以通过代码定时调用RollingFileAppender的rollover()方法,强制触发滚动:
import ch.qos.logback.core.rolling.RollingFileAppender; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.LoggerContext; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class ManualLogRollover { // 每天凌晨强制触发滚动 @Scheduled(cron = "0 0 0 * * ?") public void triggerDailyRollover() { LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); RollingFileAppender<?> appender = (RollingFileAppender<?>) loggerContext.getLogger("ROOT").getAppender("ROLLING"); if (appender != null && appender.isStarted()) { appender.rollover(); } } }
内容的提问来源于stack exchange,提问作者AbNig
相关产品推荐
相关产品推荐

