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

Spring Boot下SLF4J指定类分Appender输出不同日志级别

解决方案:Spring Boot JavaConfig 实现指定类日志分Appender级别输出(无需自定义Filter)

刚好之前折腾过类似的需求,用Spring Boot的JavaConfig完全能搞定,而且不用自己写Filter,靠Logback(Spring Boot默认日志框架)的内置组件就可以实现。下面是具体的实现步骤:

1. 先配置两个基础Appender

首先创建ConsoleAppender和RollingFileAppender的Bean,把编码器、滚动策略这些基础属性配置好:

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;
import ch.qos.logback.core.util.FileSize;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ResourceUtils;

import java.io.File;

@Configuration
public class LoggingAppenderConfig {

    // 配置控制台输出Appender
    @Bean
    public ConsoleAppender<ILoggingEvent> consoleAppender(LoggerContext loggerContext) {
        ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<>();
        appender.setContext(loggerContext);
        appender.setName("CONSOLE_APPENDER");

        // 设置日志格式编码器
        PatternLayoutEncoder encoder = new PatternLayoutEncoder();
        encoder.setContext(loggerContext);
        encoder.setPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n");
        encoder.start();

        appender.setEncoder(encoder);
        appender.start();
        return appender;
    }

    // 配置滚动文件输出Appender
    @Bean
    public RollingFileAppender<ILoggingEvent> rollingFileAppender(LoggerContext loggerContext) throws Exception {
        RollingFileAppender<ILoggingEvent> appender = new RollingFileAppender<>();
        appender.setContext(loggerContext);
        appender.setName("ROLLING_FILE_APPENDER");

        // 日志文件存储路径(项目根目录下的logs文件夹)
        File logDir = new File(ResourceUtils.getFile("classpath:.").getParentFile(), "logs");
        if (!logDir.exists()) logDir.mkdirs();
        appender.setFile(logDir.getAbsolutePath() + "/app.log");

        // 设置日志格式编码器
        PatternLayoutEncoder encoder = new PatternLayoutEncoder();
        encoder.setContext(loggerContext);
        encoder.setPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n");
        encoder.start();

        // 设置滚动策略:按时间+文件大小分割,保留30天日志,总大小不超过1GB
        SizeAndTimeBasedRollingPolicy<ILoggingEvent> rollingPolicy = new SizeAndTimeBasedRollingPolicy<>();
        rollingPolicy.setContext(loggerContext);
        rollingPolicy.setParent(appender);
        rollingPolicy.setFileNamePattern(logDir.getAbsolutePath() + "/app.%d{yyyy-MM-dd}.%i.log");
        rollingPolicy.setMaxFileSize(FileSize.valueOf("10MB"));
        rollingPolicy.setMaxHistory(30);
        rollingPolicy.setTotalSizeCap(FileSize.valueOf("1GB"));
        rollingPolicy.start();

        appender.setEncoder(encoder);
        appender.setRollingPolicy(rollingPolicy);
        appender.start();
        return appender;
    }
}

2. 给目标Logger配置分级别输出逻辑

这是核心步骤:我们要获取com.google.api.client.http.HttpTransport对应的Logger,然后给它关联两个Appender,同时用Logback内置的EvaluatorFilter给每个Appender设置级别过滤规则,不用自己写Filter类:

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.EvaluatorFilter;
import ch.qos.logback.core.spi.FilterReply;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

@Configuration
public class TargetLoggerLevelConfig {

    // 目标类的Logger名称
    private static final String TARGET_LOGGER = "com.google.api.client.http.HttpTransport";

    @Autowired
    private LoggerContext loggerContext;

    @Autowired
    private ConsoleAppender<ILoggingEvent> consoleAppender;

    @Autowired
    private RollingFileAppender<ILoggingEvent> rollingFileAppender;

    @PostConstruct
    public void configureTargetLogger() {
        // 获取目标Logger实例
        Logger targetLogger = loggerContext.getLogger(TARGET_LOGGER);
        // 关闭日志传递:避免该Logger的日志再输出到根Logger的Appender,防止重复打印
        targetLogger.setAdditive(false);

        // --- 给Console Appender添加过滤器:只接收该Logger的DEBUG及以上级别日志 ---
        EvaluatorFilter<ILoggingEvent> consoleFilter = new EvaluatorFilter<>();
        consoleFilter.setContext(loggerContext);
        // 用Janino表达式实现过滤逻辑:日志属于目标Logger,且级别>=DEBUG
        consoleFilter.setEvaluator(new ch.qos.logback.classic.boolex.JaninoEventEvaluator() {{
            setContext(loggerContext);
            setExpression("logger.equals(\"" + TARGET_LOGGER + "\") && level >= DEBUG");
        }});
        consoleFilter.setOnMatch(FilterReply.ACCEPT); // 符合条件就输出
        consoleFilter.setOnMismatch(FilterReply.DENY); // 不符合就拒绝
        consoleFilter.start();
        consoleAppender.addFilter(consoleFilter);
        // 将Console Appender绑定到目标Logger
        targetLogger.addAppender(consoleAppender);

        // --- 给Rolling File Appender添加过滤器:只接收该Logger的WARN及以上级别日志 ---
        EvaluatorFilter<ILoggingEvent> fileFilter = new EvaluatorFilter<>();
        fileFilter.setContext(loggerContext);
        fileFilter.setEvaluator(new ch.qos.logback.classic.boolex.JaninoEventEvaluator() {{
            setContext(loggerContext);
            setExpression("logger.equals(\"" + TARGET_LOGGER + "\") && level >= WARN");
        }});
        fileFilter.setOnMatch(FilterReply.ACCEPT);
        fileFilter.setOnMismatch(FilterReply.DENY);
        fileFilter.start();
        rollingFileAppender.addFilter(fileFilter);
        // 将Rolling File Appender绑定到目标Logger
        targetLogger.addAppender(rollingFileAppender);

        // 设置目标Logger的最低级别为DEBUG:确保所有DEBUG及以上的日志都能被捕获
        targetLogger.setLevel(Level.DEBUG);
    }
}

关键细节说明

  • setAdditive(false):这个很重要,关闭后目标Logger的日志不会再传递给父Logger(比如根Logger),避免同一日志被多次打印。
  • EvaluatorFilter:这是Logback内置的过滤器,结合Janino表达式可以灵活实现过滤逻辑,完全不用自己写Filter类。
  • 目标Logger级别设置:必须把级别设为DEBUG,这样DEBUG及以上的日志才会被捕获,然后交给两个Appender的过滤器分别筛选输出。

验证方法

你可以写个测试接口来验证效果:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LogTestController {

    // 用目标类的Logger名称获取Logger实例
    private static final Logger logger = LoggerFactory.getLogger("com.google.api.client.http.HttpTransport");

    @GetMapping("/test-log")
    public String testLog() {
        logger.debug("这条DEBUG日志只会出现在控制台");
        logger.warn("这条WARN日志会同时出现在控制台和日志文件");
        return "日志测试完成,请查看控制台和logs文件夹";
    }
}

调用接口后,你会看到:

  • 控制台同时显示DEBUG和WARN日志
  • logs/app.log里只有WARN日志

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:48:09