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

如何在Logback编码器模式中显示实例的标识哈希码?

在Logback中添加System.identityHashCode到日志(支持按类/包配置)

当然可以实现!而且完全能满足你按类/包配置的需求,不用每次手动在日志里加System.identityHashCode()。下面是一步步的具体方案,适配你当前用的Logback 1.2.3 + SLF4J 1.7.25(不用升级到快照版也能正常运行):

1. 自定义Logback转换器

首先需要写一个自定义转换器,让Logback能识别并生成System.identityHashCode的值。这里分两种常见场景:

场景A:显示日志参数中第一个对象的哈希码

如果你的日志调用通常是这种形式:logger.info("处理用户: {}", user);,想要自动带上user的标识哈希码,可以用这个转换器:

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.pattern.ClassicConverter;

public class IdentityHashCodeConverter extends ClassicConverter {
    @Override
    public String convert(ILoggingEvent event) {
        Object[] args = event.getArgumentArray();
        // 取第一个非空参数的identityHashCode
        if (args != null && args.length > 0 && args[0] != null) {
            return String.format(" [hash:%d]", System.identityHashCode(args[0]));
        }
        return ""; // 没有符合条件的参数时返回空串
    }
}

场景B:显示Logger实例的哈希码

如果想显示当前日志所属Logger的标识哈希码(每个类的Logger是单例,同一类的日志哈希码固定),可以用这个版本:

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.pattern.ClassicConverter;

public class IdentityHashCodeConverter extends ClassicConverter {
    @Override
    public String convert(ILoggingEvent event) {
        return String.format(" [logger-hash:%d]", System.identityHashCode(event.getLogger()));
    }
}

2. 在Logback配置中注册转换器

在logback.xml的<configuration>标签内,添加<conversionRule>把自定义转换器和一个占位符绑定(比如%identityHash):

<configuration>
    <!-- 注册自定义转换器,conversionWord是日志pattern中要用的占位符 -->
    <conversionRule conversionWord="identityHash"
                    converterClass="com.yourpackage.IdentityHashCodeConverter" />

    <!-- 你的控制台Appender配置,现在可以用%identityHash了 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}%identityHash - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

3. 按类/包配置是否显示哈希码

如果只想让特定类或包的日志显示哈希码,有两种灵活的实现方式:

方式一:用TurboFilter全局控制

这种方式适合全局统一规则,比如所有com.yourpackage.service包下的日志自动显示哈希码:

第一步:写TurboFilter类

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.turbo.TurboFilter;
import ch.qos.logback.core.spi.FilterReply;
import org.slf4j.MDC;
import org.slf4j.Marker;

public class IdentityHashFilter extends TurboFilter {
    // 可配置的包/类前缀,符合条件的日志会显示哈希码
    private String includePrefix = "com.yourpackage.service";

    @Override
    public FilterReply decide(Marker marker, ch.qos.logback.classic.Logger logger, 
                              Level level, String format, Object[] params, Throwable t) {
        if (logger.getName().startsWith(includePrefix)) {
            // 用MDC标记当前线程的日志需要显示哈希码
            MDC.put("SHOW_IDENTITY_HASH", "true");
        } else {
            MDC.remove("SHOW_IDENTITY_HASH");
        }
        return FilterReply.NEUTRAL; // 不拦截日志,只做标记
    }

    // 给配置文件提供setter,方便修改includePrefix
    public void setIncludePrefix(String includePrefix) {
        this.includePrefix = includePrefix;
    }
}

第二步:修改转换器适配标记

更新之前的IdentityHashCodeConverter,只有当MDC标记存在时才返回哈希码:

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.pattern.ClassicConverter;
import org.slf4j.MDC;

public class IdentityHashCodeConverter extends ClassicConverter {
    @Override
    public String convert(ILoggingEvent event) {
        String showHash = MDC.get("SHOW_IDENTITY_HASH");
        if ("true".equals(showHash)) {
            Object[] args = event.getArgumentArray();
            if (args != null && args.length > 0 && args[0] != null) {
                return String.format(" [hash:%d]", System.identityHashCode(args[0]));
            }
        }
        return "";
    }
}

第三步:在logback.xml中注册Filter

<configuration>
    <!-- 注册TurboFilter,配置要开启哈希码的包前缀 -->
    <turboFilter class="com.yourpackage.IdentityHashFilter">
        <includePrefix>com.yourpackage.service</includePrefix>
    </turboFilter>

    <conversionRule conversionWord="identityHash"
                    converterClass="com.yourpackage.IdentityHashCodeConverter" />

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}%identityHash - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

方式二:为特定类/包单独配置Appender

如果需要更精细的控制(比如不同包用不同的日志格式),可以给目标类/包单独配置带哈希码的Appender:

<configuration>
    <conversionRule conversionWord="identityHash"
                    converterClass="com.yourpackage.IdentityHashCodeConverter" />

    <!-- 不带哈希码的通用Appender -->
    <appender name="STDOUT_BASE" 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="STDOUT_WITH_HASH" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}%identityHash - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 给目标包配置专属Appender,additivity设为false避免重复输出 -->
    <logger name="com.yourpackage.service" level="info" additivity="false">
        <appender-ref ref="STDOUT_WITH_HASH" />
    </logger>

    <!-- 其他类用通用Appender -->
    <root level="info">
        <appender-ref ref="STDOUT_BASE" />
    </root>
</configuration>

版本兼容性说明

你当前用的Logback 1.2.3和SLF4J 1.7.25完全支持上述所有特性,不需要升级到1.8.x快照版。如果后续升级版本,这些代码也能兼容,API没有破坏性变更。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:28:28