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

Log4j2:能否将Map消息值中的JSON字符串转为事件输出的结构化JSON?

解决Log4j2 JsonTemplateLayout处理Gson JsonElement的序列化问题

核心问题

当你用StringMapMessage传递包含Gson JsonElement类型的Map参数时,默认的map解析器会直接调用toString(),导致输出带转义的JSON字符串,而非预期的嵌套JSON对象。

可行方案

方案1:用脚本解析器单独处理JsonElement字段

通过merge解析器结合script解析器,先将Map中除request外的字段扁平化到根节点,再单独处理request字段的序列化:

  1. 修改JSON模板为:
{
  "$resolver": "merge",
  "resolvers": [
    {
      "$resolver": "map",
      "flatten": true,
      "excludeKeys": ["request"]
    },
    {
      "request": {
        "$resolver": "script",
        "script": "groovy",
        "source": "import com.google.gson.JsonElement; def req = map.get('request'); req instanceof JsonElement ? req : req"
      }
    }
  ]
}
  1. 确保项目引入Log4j2的Groovy脚本支持依赖(Maven示例):
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-script-groovy</artifactId>
  <version>${log4j2.version}</version>
</dependency>

原理:用merge合并两个解析结果,第一个解析器处理除request外的所有字段并扁平化;第二个解析器通过Groovy脚本直接获取JsonElement对象,Log4j2会自动将其序列化为嵌套JSON对象,而非调用toString()。

方案2:自定义ValueResolver(更灵活)

如果不想依赖脚本引擎,可以自定义专门处理JsonElement的解析器:

  1. 实现ValueResolver接口:
import com.google.gson.JsonElement;
import org.apache.logging.log4j.core.layout.template.json.resolver.ValueResolver;
import org.apache.logging.log4j.core.layout.template.json.resolver.ValueResolverContext;
import org.apache.logging.log4j.core.layout.template.json.resolver.ValueResolverFactory;

import java.util.Map;

public class JsonElementResolver implements ValueResolver {

    private final ValueResolver delegate;

    public JsonElementResolver(ValueResolver delegate) {
        this.delegate = delegate;
    }

    @Override
    public Object resolve(Object value) {
        if (value instanceof JsonElement) {
            return value; // 直接返回JsonElement,让序列化器处理
        }
        return delegate.resolve(value);
    }

    public static class Factory implements ValueResolverFactory {
        @Override
        public ValueResolver create(ValueResolverContext context, Map<String, String> config) {
            ValueResolver delegate = context.getResolverFactory("map").create(context, config);
            return new JsonElementResolver(delegate);
        }
    }
}
  1. 在Log4j2配置中注册自定义解析器:
<JsonTemplateLayout eventTemplateUri="classpath:log-template.json">
    <ValueResolverFactory key="jsonElementMap" className="com.yourpackage.JsonElementResolver$Factory"/>
</JsonTemplateLayout>
  1. 在JSON模板中使用自定义解析器:
{
  "$resolver": "jsonElementMap",
  "flatten": true
}

原理:通过包装默认的map解析器,对JsonElement类型的值做特殊处理,直接返回对象而非调用toString(),实现正确的嵌套JSON输出。

为什么你之前的配置无效

你尝试的format="JSON"和stringified=false参数仅针对字符串类型的JSON内容,而你的request字段是JsonElement对象,不是字符串,因此这些配置无法生效。只有让Log4j2识别到这是一个JSON结构对象,才能正确序列化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 11:24:53