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

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") &amp;&amp; level &gt;= 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") &amp;&amp; level &gt;= 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,定期触发滚动检查:

  1. 调整Filter表达式,允许心跳日志通过:
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
  <evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator">
    <expression>
      (logger.startsWith("com.example") &amp;&amp; level &gt;= INFO) 
      || (logger.equals("com.example.LogHeartbeat") &amp;&amp; level == TRACE)
    </expression>
  </evaluator>
  <onMatch>ACCEPT</onMatch>
  <onMismatch>DENY</onMismatch>
</filter>
  1. 在代码中添加定时心跳逻辑(以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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 07:01:09