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

如何让JSONObject在LogStashEncoder的StructuredArgument中正常序列化?

问题原因及解决方案

核心原因

LogstashEncoder 底层依赖 Jackson 实现对象序列化,而 org.json.JSONObject 的内部存储结构无法被 Jackson 默认序列化器正确解析:

  • 旧版本(如20180130)的 JSONObject 未公开内部存储数据的 Map,导致序列化后输出空对象 {};
  • 新版本(如20230618)虽然暴露了 empty、mapType 等属性,但这些并非实际存储的JSON内容,所以输出不符合预期。

而 HashMap 和自定义POJO(如你的 Order)能正常序列化,是因为Jackson对标准集合类和带getter方法的POJO有默认的序列化逻辑。


解决方案

方案1:直接使用Map代替JSONObject(最简单)

既然 HashMap 本身可以被Jackson正确序列化为JSON对象,完全不需要转换为 JSONObject,直接传递原始Map即可:

Map<String, String> user = new HashMap<>();
user.put("firstName", "Jane");

Order order = new Order("123");

logger.info("msg with one KV and 2 patterns {} {}"
    , StructuredArguments.keyValue("user", user)
    , StructuredArguments.keyValue("order", order)
);

方案2:将JSONObject转换为Jackson JsonNode

利用Jackson的 ObjectMapper 将 JSONObject 转换为Jackson原生的 JsonNode,这样LogstashEncoder就能正确识别并序列化:

Map<String, String> user = new HashMap<>();
user.put("firstName", "Jane");

JSONObject userDetails = new JSONObject(user);
ObjectMapper mapper = new ObjectMapper();
JsonNode userDetailsNode = mapper.readTree(userDetails.toString());

Order order = new Order("123");

logger.info("msg with one KV and 2 patterns {} {} {}"
    , StructuredArguments.keyValue("user", user)
    , StructuredArguments.keyValue("userDetails", userDetailsNode)
    , StructuredArguments.keyValue("order", order)
);

方案3:自定义Jackson序列化器处理JSONObject

如果必须使用 org.json.JSONObject,可以编写自定义序列化器,让Jackson正确解析其内部数据:

  1. 编写自定义序列化器:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.json.JSONObject;
import java.io.IOException;

public class JSONObjectSerializer extends JsonSerializer<JSONObject> {
    @Override
    public void serialize(JSONObject value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        // 将JSONObject转换为Map后序列化
        gen.writeObject(value.toMap());
    }
}
  1. 编写ObjectMapper装饰器,注册自定义序列化器:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import net.logstash.logback.decorate.JsonMapperDecorator;
import org.json.JSONObject;

public class CustomObjectMapperDecorator implements JsonMapperDecorator {
    @Override
    public ObjectMapper decorate(ObjectMapper mapper) {
        SimpleModule module = new SimpleModule();
        module.addSerializer(JSONObject.class, new JSONObjectSerializer());
        mapper.registerModule(module);
        return mapper;
    }
}
  1. 修改logback.xml配置,让LogstashEncoder使用自定义ObjectMapper:
<configuration>
    <appender name="jsonConsoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <customObjectMapperDecorator class="your.package.CustomObjectMapperDecorator"/>
        </encoder>
    </appender>
    <root level="info">
        <appender-ref ref="jsonConsoleAppender" />
    </root>
</configuration>

方案4:使用StructuredArguments.rawValue直接输出JSON字符串

如果不在意日志message中userDetails的格式,可以直接传递JSONObject的toString结果,并使用rawValue让LogstashEncoder将其解析为JSON对象:

logger.info("msg with one KV and 2 patterns {} {} {}"
    , StructuredArguments.keyValue("user", user)
    , StructuredArguments.rawValue("userDetails", userDetails.toString())
    , StructuredArguments.keyValue("order", order)
);

注意:此方案下,message中的userDetails会显示为JSON字符串(如{"firstName":"Jane"}),但结构化字段中的userDetails会是正确的JSON对象。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 17:42:45