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
相关产品推荐
相关产品推荐

