如何在Logback 1.2.3(Java 9环境)中追加前修改日志事件消息?
嘿,刚好我对Logback的这类扩展点挺熟悉的,给你梳理几个可行的方案,不止局限于encoder或layout哦:
可行的Logback日志事件修改方案(类似Serilog Enrichment)
针对你需要在日志事件到达Appender前修改消息文本的需求,Logback提供了几个比encoder/layout更直接的钩子,能真正修改日志事件本身(而不只是输出格式):
方案1:自定义Logger级别的Filter
这个Filter会在日志事件被Logger处理后、转发到Appender之前触发,是修改事件内容的理想时机。你只需要实现ch.qos.logback.classic.spi.Filter<ILoggingEvent>接口,利用Logback提供的MutableLoggingEvent来修改消息:
import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.MutableLoggingEvent; import ch.qos.logback.core.filter.Filter; import ch.qos.logback.core.spi.FilterReply; public class MessageEnrichingFilter extends Filter<ILoggingEvent> { @Override public FilterReply decide(ILoggingEvent event) { // 将只读事件转为可修改的MutableLoggingEvent if (event instanceof MutableLoggingEvent) { MutableLoggingEvent mutableEvent = (MutableLoggingEvent) event; // 这里可以根据需求自由修改消息,比如添加前缀、替换关键词等 String originalMsg = mutableEvent.getMessage(); mutableEvent.setMessage("[ENRICHED] " + originalMsg); } // 返回NEUTRAL让事件继续流向后续的Appender return FilterReply.NEUTRAL; } }
然后在logback.xml里把这个Filter绑定到目标Logger上:
<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> <!-- 给指定包下的Logger添加修改Filter --> <logger name="com.your.project.package"> <filter class="com.your.project.package.MessageEnrichingFilter" /> <appender-ref ref="CONSOLE" /> </logger> <root level="info"> <appender-ref ref="CONSOLE" /> </root> </configuration>
方案2:自定义Appender包装器
如果你只想针对特定Appender修改日志事件,可以创建一个包装现有Appender的自定义Appender,在转发事件前完成修改:
import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.MutableLoggingEvent; import ch.qos.logback.core.Appender; import ch.qos.logback.core.UnsynchronizedAppenderBase; public class EnrichingAppenderWrapper extends UnsynchronizedAppenderBase<ILoggingEvent> { private Appender<ILoggingEvent> delegate; @Override protected void append(ILoggingEvent event) { // 修改事件后转发给真实的Appender ILoggingEvent modifiedEvent = modifyEventContent(event); delegate.doAppend(modifiedEvent); } private ILoggingEvent modifyEventContent(ILoggingEvent event) { if (event instanceof MutableLoggingEvent) { MutableLoggingEvent mutableEvent = (MutableLoggingEvent) event; mutableEvent.setMessage("[APPENDER_ENRICHED] " + mutableEvent.getMessage()); return mutableEvent; } // 若原事件不可变,创建一个可修改的副本 MutableLoggingEvent newEvent = new MutableLoggingEvent(event); newEvent.setMessage("[APPENDER_ENRICHED] " + newEvent.getMessage()); return newEvent; } // 供配置文件设置被包装的目标Appender public void setDelegate(Appender<ILoggingEvent> delegate) { this.delegate = delegate; } }
配置文件里这样使用:
<configuration> <!-- 真实的输出Appender --> <appender name="REAL_CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <!-- 包装后的Appender,负责修改日志事件 --> <appender name="ENRICHING_CONSOLE" class="com.your.project.package.EnrichingAppenderWrapper"> <delegate ref="REAL_CONSOLE" /> </appender> <root level="info"> <appender-ref ref="ENRICHING_CONSOLE" /> </root> </configuration>
方案3:Encoder/Layout(你提到的方式)
如果你的需求只是修改最终输出的消息格式,而不需要修改日志事件本身,那么encoder或layout确实是简单直接的选择。比如自定义Layout:
import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.LayoutBase; public class EnrichingLayout extends LayoutBase<ILoggingEvent> { @Override public String doLayout(ILoggingEvent event) { String modifiedMsg = "[LAYOUT_ENRICHED] " + event.getMessage(); // 按照自定义格式拼接日志内容 return String.format("%s [%s] %-5s %s - %s%n", event.getTimeStamp(), event.getThreadName(), event.getLevel(), event.getLoggerName(), modifiedMsg); } }
配置示例:
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <layout class="com.your.project.package.EnrichingLayout" /> </appender>
总结
- 如果你需要修改日志事件本身(让后续所有Appender都能拿到修改后的消息),优先选择Logger Filter或Appender包装器,这两种方式更贴近Serilog enrichment的核心逻辑;
- 若只是需要调整最终输出格式,用Encoder/Layout足够简单。
内容的提问来源于stack exchange,提问作者Matthew Campbell




