如何在Logback编码器模式中显示实例的标识哈希码?
当然可以实现!而且完全能满足你按类/包配置的需求,不用每次手动在日志里加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

