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

如何在Logback日志JSON根节点写入POJO并修改其属性?

自定义Logback日志格式:修改POJO内置属性方案

核心问题拆解

你遇到的JsonGenerationException本质是JSON结构冲突:Logback的JSON生成器默认已在构建根对象,直接调用writeObject()会尝试在根节点内嵌套另一个对象,破坏JSON结构;而writeObjectField("")或StructuredArguments.fields(pojo)是将POJO的字段平铺到根对象,符合JSON单根节点要求。

要在Provider或Encoder中修改POJO的timestamp、log_level等内置日志属性,可通过以下两种实用方案实现:


方案一:自定义Encoder(最直接可控)

通过实现Logback的Encoder<ILoggingEvent>接口,直接拦截日志事件,将内置属性注入POJO后序列化。

1. 定义自定义日志POJO

public class CustomLogEntry {
    private String timestamp;
    private String logLevel;
    private String message;
    private String threadName;
    private String traceId;
    // 自定义排查字段
    private String customField1;
    private String customField2;

    // getter/setter(若用Lombok可简化)
}

2. 实现自定义Encoder

public class CustomLogEncoder extends EncoderBase<ILoggingEvent> {
    private final ObjectMapper objectMapper = new ObjectMapper();
    // 统一时间戳格式为ISO-8601
    private final DateTimeFormatter timestampFormatter = 
        DateTimeFormatter.ISO_INSTANT.withZone(ZoneId.systemDefault());

    @Override
    public byte[] encode(ILoggingEvent event) {
        CustomLogEntry logEntry = new CustomLogEntry();
        
        // 从ILoggingEvent提取内置属性并设置到POJO
        logEntry.setTimestamp(timestampFormatter.format(Instant.ofEpochMilli(event.getTimeStamp())));
        logEntry.setLogLevel(event.getLevel().toString());
        logEntry.setMessage(event.getFormattedMessage());
        logEntry.setThreadName(event.getThreadName());
        // 从MDC获取链路追踪ID等排查字段
        logEntry.setTraceId(MDC.get("traceId"));

        // 处理日志参数中的自定义字段
        for (Object arg : event.getArgumentArray()) {
            if (arg instanceof CustomExtraFields) {
                CustomExtraFields extra = (CustomExtraFields) arg;
                logEntry.setCustomField1(extra.getField1());
                logEntry.setCustomField2(extra.getField2());
            }
        }

        try {
            // 序列化POJO为JSON,加上换行符符合日志格式
            return (objectMapper.writeValueAsString(logEntry) + getLineSeparator()).getBytes(StandardCharsets.UTF_8);
        } catch (JsonProcessingException e) {
            addError("序列化日志失败", e);
            return new byte[0];
        }
    }

    @Override
    public void start() {
        super.start();
        // 配置ObjectMapper,忽略空字段
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }
}

3. 在logback.xml中配置

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="com.yourpackage.CustomLogEncoder" />
</appender>

<root level="INFO">
    <appender-ref ref="CONSOLE" />
</root>

方案二:自定义StructuredArgument(结合原fields用法)

若想保留StructuredArguments.fields(pojo)的便捷性,可自定义StructuredArgument实现,在写入JSON前修改POJO属性。

1. 自定义StructuredArgument实现

public class CustomLogArgument implements StructuredArgument {
    private final CustomLogEntry logEntry;

    public CustomLogArgument(CustomLogEntry logEntry) {
        this.logEntry = logEntry;
    }

    @Override
    public void writeTo(JsonGenerator generator, ProviderContext context) throws IOException {
        // 从ProviderContext获取当前日志事件
        ILoggingEvent event = context.getLoggingEvent();
        
        // 修改POJO的内置属性
        logEntry.setTimestamp(Instant.ofEpochMilli(event.getTimeStamp()).toString());
        logEntry.setLogLevel(event.getLevel().toString());
        
        // 将POJO字段平铺到JSON根节点
        generator.writeObject(logEntry);
    }

    @Override
    public String toString() {
        return logEntry.toString();
    }
}

2. 业务代码中使用

CustomLogEntry entry = new CustomLogEntry();
entry.setCustomField1("排查字段1");
entry.setCustomField2("排查字段2");

// 日志输出时传入自定义参数
logger.info("用户登录成功", new CustomLogArgument(entry));

关键注意事项

  • POJO可变性:若POJO是不可变类(如用@Value注解),需在构建POJO实例时直接传入timestamp、log_level等属性,可在Encoder或自定义Argument中提前构建实例。
  • 时间戳格式:Logback的getTimeStamp()返回毫秒数,建议转成ISO-8601字符串,方便跨系统解析。
  • 日志级别统一:可将event.getLevel()转成大写字符串(如INFO、ERROR),避免格式不一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 04:16:23