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

如何在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 FilterAppender包装器,这两种方式更贴近Serilog enrichment的核心逻辑;
  • 若只是需要调整最终输出格式,用Encoder/Layout足够简单。

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

火山引擎 最新活动