You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何配置日志策略:仅触发warn及以上级别时记录全级别日志

实现动态日志级别调整:仅在出现警告/错误时记录全量日志

刚好之前帮同事处理过类似的日志优化需求,你的场景核心是动态触发日志级别切换——正常情况下只输出info级别来节省存储和性能,一旦系统产生warn及以上级别的日志,立刻切换为输出所有级别(包括debug、trace)的日志,方便后续排查问题。下面给你主流日志框架的具体实现方案:

方案1:Logback 实现方式

Logback可以通过自定义TurboFilter来实现这个逻辑,它能在日志事件被处理前进行拦截判断,动态调整日志输出策略。

步骤1:自定义TurboFilter

创建一个Java类继承TurboFilter,维护一个标志位来跟踪是否出现过warn+级别的日志:

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.turbo.TurboFilter;
import ch.qos.logback.core.spi.FilterReply;
import org.slf4j.Marker;

public class DynamicLogLevelFilter extends TurboFilter {
    // 标志位:是否触发了warn及以上级别日志
    private volatile boolean highLevelEventDetected = false;
    // 默认只输出info及以上
    private Level defaultLevel = Level.INFO;

    @Override
    public FilterReply decide(Marker marker, ch.qos.logback.classic.Logger logger, Level level, String format, Object[] params, Throwable t) {
        // 如果已经检测到高级别事件,允许所有日志通过
        if (highLevelEventDetected) {
            return FilterReply.NEUTRAL;
        }
        // 检查当前日志级别是否是warn/error/fatal
        if (level.isGreaterOrEqual(Level.WARN)) {
            highLevelEventDetected = true;
            return FilterReply.NEUTRAL;
        }
        // 未触发高级别事件时,只允许默认级别及以上通过
        return level.isGreaterOrEqual(defaultLevel) ? FilterReply.NEUTRAL : FilterReply.DENY;
    }
}

步骤2:配置Logback.xml

在配置文件中引入这个自定义过滤器,并设置根日志级别为TRACE(因为过滤器会控制实际输出):

<configuration>
    <!-- 控制台输出示例 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 引入自定义动态过滤器 -->
    <turboFilter class="com.yourpackage.DynamicLogLevelFilter" />

    <!-- 根日志级别设为最低(TRACE),交给过滤器控制实际输出 -->
    <root level="TRACE">
        <appender-ref ref="CONSOLE" />
    </root>
</configuration>

方案2:Log4j2 实现方式

Log4j2可以通过自定义Filter来实现这个动态切换逻辑,灵活性很高:

步骤1:自定义Filter

创建一个实现Filter接口的类,核心逻辑和Logback版本一致:

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.Logger;
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.PluginFactory;
import org.apache.logging.log4j.core.filter.AbstractFilter;

@Plugin(name = "DynamicLogLevelFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE)
public class DynamicLogLevelFilter extends AbstractFilter {
    private volatile boolean highLevelEventDetected = false;
    private final Level defaultLevel = Level.INFO;

    @Override
    public Result filter(Logger logger, Level level, Marker marker, String msg, Object... params) {
        return decideLevel(level);
    }

    @Override
    public Result filter(LogEvent event) {
        return decideLevel(event.getLevel());
    }

    private Result decideLevel(Level level) {
        if (highLevelEventDetected) {
            return Result.NEUTRAL;
        }
        if (level.isMoreSpecificThanOrEqualTo(Level.WARN)) {
            highLevelEventDetected = true;
            return Result.NEUTRAL;
        }
        return level.isMoreSpecificThanOrEqualTo(defaultLevel) ? Result.NEUTRAL : Result.DENY;
    }

    @PluginFactory
    public static DynamicLogLevelFilter createFilter() {
        return new DynamicLogLevelFilter();
    }
}

步骤2:配置Log4j2.xml

在配置中添加这个过滤器,并将根日志级别设为TRACE

<?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"/>
            <!-- 应用自定义过滤器 -->
            <DynamicLogLevelFilter/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="TRACE">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

关键逻辑说明

  • 标志位设计:用volatile修饰highLevelEventDetected,确保多线程环境下的可见性,一旦触发高级别日志就永久切换到全量输出(如果需要重置,可以添加定时任务或接口来重置标志位)。
  • 根日志级别设置:必须把根级别设为TRACE(最低级别),否则低于根级别的日志会被直接过滤,过滤器就没机会处理了。
  • 性能开销:这个逻辑只是简单的级别判断和标志位检查,性能开销极低,不会对系统造成明显影响。

内容的提问来源于stack exchange,提问作者Akshay G Rao

火山引擎 最新活动