如何通过编程方式移除Logback输出JSON日志中的部分属性
如何通过编程方式移除Logback JSON日志中的指定属性
目前Java生态中Logback输出JSON格式日志几乎都是基于logstash-logback-encoder组件实现,以下是三种可落地的编程实现方案,对应你给出的日志示例,可以按需移除version、level_value这类不需要的属性。
方案1:按需注册字段Provider,从源头不生成不需要的字段
logstash-logback-encoder的每个输出字段都对应独立的JsonProvider,编程初始化编码器时,只注册你需要保留的字段对应的Provider即可,不需要的字段不注册就不会出现在最终输出里。
示例代码:
import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.core.ConsoleAppender; import net.logstash.logback.encoder.LogstashEncoder; import net.logstash.logback.composite.loggingevent.*; import org.slf4j.LoggerFactory; public class CustomLogbackInitializer { public static void initJsonLogger() { LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); LogstashEncoder jsonEncoder = new LogstashEncoder(); // 清空编码器默认加载的所有Provider jsonEncoder.getProviders().removeAllProviders(); // 逐个添加需要保留的字段对应的Provider jsonEncoder.getProviders().addProvider(new LoggingEventFormattedTimestampJsonProvider()); // 保留time字段 jsonEncoder.getProviders().addProvider(new MessageJsonProvider()); // 保留message字段 jsonEncoder.getProviders().addProvider(new LoggerNameJsonProvider()); // 保留logger_name字段 jsonEncoder.getProviders().addProvider(new ThreadNameJsonProvider()); // 保留thread_name字段 jsonEncoder.getProviders().addProvider(new LogLevelJsonProvider()); // 保留level字段 jsonEncoder.getProviders().addProvider(new MdcJsonProvider()); // 保留MDC中注入的自定义字段,比如示例里的myid // 不需要version、level_value字段,就不注册VersionJsonProvider、LogLevelValueJsonProvider即可 jsonEncoder.setContext(context); jsonEncoder.start(); // 把编码器绑定到对应Appender,完成日志配置 ConsoleAppender appender = new ConsoleAppender(); appender.setEncoder(jsonEncoder); appender.setContext(context); appender.start(); context.getLogger("root").addAppender(appender); } }
这种方案性能最好,因为不需要在序列化阶段做额外判断,从字段生成阶段就过滤掉了不需要的内容,适合字段固定、需要移除多个默认字段的场景。
方案2:通过字段名配置直接屏蔽指定字段
如果只需要移除少量字段,不需要调整整个Provider列表,可以直接通过字段名配置把不需要输出的字段设为null,编码器序列化时会自动跳过这些字段:
LogstashEncoder jsonEncoder = new LogstashEncoder(); // 配置要屏蔽的字段 jsonEncoder.setFieldNames(new LogstashFieldNames() .setVersion(null) // 移除version字段 .setLevelValue(null) // 移除level_value字段 ); // 其余配置保持默认即可 jsonEncoder.start();
这种方案代码量最少,适合只需要移除2-3个默认字段、不需要调整其他默认输出逻辑的场景。
方案3:自定义序列化拦截器,动态过滤任意字段
如果需要根据运行时逻辑动态判断是否移除字段,或者要过滤的字段来自第三方扩展的Provider,可以实现自定义的JSON生成器装饰器,在序列化写入阶段拦截指定字段:
import com.fasterxml.jackson.core.JsonGenerator; import net.logstash.logback.decorate.JsonGeneratorDecorator; import com.fasterxml.jackson.core.util.JsonGeneratorDelegate; import java.io.IOException; import java.util.Set; public class FieldExcludeDecorator implements JsonGeneratorDecorator { // 定义要移除的字段集合 private static final Set<String> EXCLUDE_FIELDS = Set.of("version", "level_value"); @Override public JsonGenerator decorate(JsonGenerator generator) { return new ExcludeGenerator(generator, EXCLUDE_FIELDS); } private static class ExcludeGenerator extends JsonGeneratorDelegate { private final Set<String> excludeFields; private boolean skipCurrentValue = false; public ExcludeGenerator(JsonGenerator delegate, Set<String> excludeFields) { super(delegate); this.excludeFields = excludeFields; } @Override public void writeFieldName(String name) throws IOException { if (excludeFields.contains(name)) { skipCurrentValue = true; return; } skipCurrentValue = false; super.writeFieldName(name); } @Override public void writeString(String value) throws IOException { if (skipCurrentValue) { skipCurrentValue = false; return; } super.writeString(value); } @Override public void writeNumber(int v) throws IOException { if (skipCurrentValue) { skipCurrentValue = false; return; } super.writeNumber(v); } // 其余writeBoolean、writeObject、writeNull等方法按照相同逻辑重写,跳过要排除的字段值即可 } }
注册自定义装饰器到编码器即可生效:
jsonEncoder.addDecorator(new FieldExcludeDecorator());
这种方案灵活性最高,支持任意复杂的过滤规则,缺点是会在序列化阶段增加少量判断逻辑,适合有动态过滤需求的场景。
内容的提问来源于stack exchange,提问作者Bapan Biswas
相关产品推荐
相关产品推荐

