
在使用Logback进行日志管理时,开发者有时会遇到一个令人困惑的现象:即使代码中仅通过编程方式配置了文件输出(例如RollingFileAppender),日志消息仍然会同时显示在控制台上,而并未显式配置ConsoleAppender。这不仅可能导致不必要的输出,也增加了调试的难度。本教程将深入探讨这一现象背后的机制,并提供明确的解决方案。
Logback的默认行为与问题根源
logback作为slf4j的实现之一,其日志系统基于一个层级结构。每个logger实例都有一个潜在的父logger,直至最顶层的root logger。当没有提供logback.xml或logback-test.xml等配置文件时,logback会应用一个默认的配置策略。这个默认策略通常包括一个root logger,并且该root logger会默认关联一个consoleappender,将所有日志事件输出到标准输出流。
考虑以下编程方式配置文件日志的示例代码:
import ch.qos.logback.classic.Level;import ch.qos.logback.classic.Logger;import ch.qos.logback.classic.LoggerContext;import ch.qos.logback.classic.encoder.PatternLayoutEncoder;import ch.qos.logback.core.rolling.RollingFileAppender;import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;import ch.qos.logback.core.util.FileSize;import org.slf4j.LoggerFactory;public class LogbackConfigurator { private String logFilePath = "logs/app.log"; private Level logLevel = Level.INFO; // 获取Logback的LoggerContext private LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); private RollingFileAppender rollingFileAppender; /** * 初始化指定名称的Logger并配置文件输出。 * @param logName Logger的名称。 */ public void initializeLogging(final String logName) { final Logger log = lc.getLogger(logName); log.setLevel(this.logLevel); setupFileLogging(log); log.info("Logging initialized for: {}", logName); // 这条日志会同时输出到文件和控制台 } /** * 为Logger设置滚动文件Appender。 * @param log 要配置的Logger实例。 */ private void setupFileLogging(final Logger log) { rollingFileAppender = new RollingFileAppender(); rollingFileAppender.setContext(lc); // 设置LoggerContext rollingFileAppender.setName("FILE-" + log.getName()); // 为Appender命名 rollingFileAppender.setFile(this.logFilePath); // 配置编码器 PatternLayoutEncoder encoder = new PatternLayoutEncoder(); encoder.setContext(lc); encoder.setPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"); encoder.start(); rollingFileAppender.setEncoder(encoder); // 配置滚动策略 SizeAndTimeBasedRollingPolicy rollingPolicy = new SizeAndTimeBasedRollingPolicy(); rollingPolicy.setContext(lc); // 设置LoggerContext rollingPolicy.setParent(rollingFileAppender); // 关联父Appender rollingPolicy.setFileNamePattern("logs/app.%d{yyyy-MM-dd}.%i.log.gz"); rollingPolicy.setMaxFileSize(FileSize.valueOf("10MB")); rollingPolicy.setMaxHistory(7); // 保留7天历史文件 rollingPolicy.start(); rollingFileAppender.setRollingPolicy(rollingPolicy); rollingFileAppender.start(); log.addAppender(rollingFileAppender); } public static void main(String[] args) { LogbackConfigurator configurator = new LogbackConfigurator(); configurator.initializeLogging("com.example.MyApp"); Logger logger = (Logger) LoggerFactory.getLogger("com.example.MyApp"); logger.info("This is an info message."); logger.warn("This is a warning message."); }}
在上述代码中,尽管我们只为com.example.MyApp这个Logger添加了一个RollingFileAppender,但运行main方法后,日志信息依然会出现在控制台。这正是由于Logback的additivity(叠加性)特性在起作用。
理解Logback的Additivity特性
additivity是Logback(以及Log4j等其他日志框架)中一个关键的Logger属性,默认为true。它的作用是控制日志事件是否会传递给父Logger。当一个Logger接收到一个日志事件时,它会首先将其传递给自己的所有Appender进行处理。如果该Logger的additivity属性为true,它会接着将这个日志事件传递给它的父Logger。这个过程会沿着Logger层级向上一直持续,直到根Logger。
由于根Logger通常默认配置了ConsoleAppender,或者在没有配置文件时Logback会自动为其配置一个,因此即使您的特定Logger只配置了文件Appender,日志事件最终仍会“冒泡”到根Logger,并被其ConsoleAppender捕获并输出到控制台。这就是导致日志意外输出到控制台的根本原因。
解决方案:禁用Logger的Additivity
要阻止日志事件向上层Logger传播,从而避免被根Logger的ConsoleAppender捕获,只需将特定Logger的additivity属性设置为false即可。
在编程方式配置时,这可以通过调用logger.setAdditive(false);来实现。
修改后的initializeLogging方法如下:
import ch.qos.logback.classic.Level;import ch.qos.logback.classic.Logger;import ch.qos.logback.classic.LoggerContext;import ch.qos.logback.classic.encoder.PatternLayoutEncoder;import ch.qos.logback.core.rolling.RollingFileAppender;import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;import ch.qos.logback.core.util.FileSize;import org.slf4j.LoggerFactory;public class LogbackConfigurator { private String logFilePath = "logs/app.log"; private Level logLevel = Level.INFO; private LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); private RollingFileAppender rollingFileAppender; /** * 初始化指定名称的Logger并配置文件输出,同时禁用控制台输出。 * @param logName Logger的名称。 */ public void initializeLogging(final String logName) { final Logger log = lc.getLogger(logName); log.setLevel(this.logLevel); // 关键一步:禁用additivity,阻止日志事件向上层Logger传播 log.setAdditive(false); setupFileLogging(log); log.info("Logging initialized for: {}", logName); // 这条日志将只输出到文件 } /** * 为Logger设置滚动文件Appender。 * @param log 要配置的Logger实例。 */ private void setupFileLogging(final Logger log) { rollingFileAppender = new RollingFileAppender(); rollingFileAppender.setContext(lc); rollingFileAppender.setName("FILE-" + log.getName()); rollingFileAppender.setFile(this.logFilePath); PatternLayoutEncoder encoder = new PatternLayoutEncoder(); encoder.setContext(lc); encoder.setPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n");
以上就是Logback日志输出控制:深入理解与禁用默认控制台输出的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/94026.html
微信扫一扫
支付宝扫一扫