如何在Spring Boot应用中生成含数值类型的JSON日志?
优雅实现SLF4J+Log4j2的JSON日志数值字段类型控制(Spring Boot)
需求梳理
- 基于SLF4J+Log4j2的Spring Boot应用输出JSON格式日志
- 必须包含HTTP请求方法、响应状态码、请求/响应体大小、处理时长等字段,且数值类型字段(状态码、大小、时长)要以数字而非字符串形式序列化
- 日志需适配ElasticSearch/OpenSearch的数值类型查询需求
更简洁的实现:用JSON Template Layout的内置转换能力
你之前没找到思路a的配置方法,其实Log4j2的JSON Template Layout内置了字段类型转换函数,完全可以在模板里完成字符串转数字的操作,不用改ThreadContext实现,也不用依赖Log4j内部API。
步骤1:继续用SLF4J MDC存字符串
不用改原来的MDC存入逻辑,哪怕是数值类字段,依然用SLF4J的MDC.put()或putCloseable()存字符串(比如把状态码存成"200"),保持代码对SLF4J的标准依赖。
步骤2:自定义JSON模板,添加类型转换
复制默认的EcsLayout.json模板到项目的resources目录(命名为custom-ecs-layout.json),然后针对需要转数字的字段,使用toNumber()内置函数做转换。
比如模板中处理HTTP相关字段的部分可以写成:
"http": { "request": { "method": "${mdc:http.request.method}", "body": { "bytes": "${toNumber(mdc:http.request.body.bytes, 0)}" } }, "response": { "status_code": "${toNumber(mdc:http.response.status_code, 0)}", "body": { "bytes": "${toNumber(mdc:http.response.body.bytes, 0)}" }, "duration": "${toNumber(mdc:http.response.duration, 0)}" } }
toNumber()的第二个参数是默认值,当字段不存在或者转换失败时会用这个值兜底,避免日志序列化出错。
步骤3:配置Log4j2使用自定义模板
在log4j2.xml里把JsonTemplateLayout的模板路径指向你自定义的文件:
<Appenders> <Console name="Console" target="SYSTEM_OUT"> <JsonTemplateLayout templateLocation="classpath:custom-ecs-layout.json" /> </Console> </Appenders>
这个方案的优势
- 完全基于SLF4J标准API,不用碰Log4j的内部类(比如
ObjectThreadContextMap),降低代码和日志框架的耦合度 - 类型转换逻辑集中在模板里,后期要调整字段类型或者新增字段,直接改模板就行,不用动代码
- 不用修改ThreadContext的实现类,避免引入线程安全或版本兼容性问题
另一种可选方案:用StructuredDataMessage构建强类型日志
如果不想依赖MDC,也可以直接用Log4j2的StructuredDataMessage构建带强类型字段的日志事件:
import org.apache.logging.log4j.message.StructuredDataMessage; // 构建结构化日志 StructuredDataMessage logMsg = new StructuredDataMessage("http-event", "请求处理完成", "http"); logMsg.put("request.method", "GET"); logMsg.put("response.status_code", 200); // 直接存Integer类型 logMsg.put("response.duration", 125); // 直接存Long类型 logger.info(logMsg);
这种方式下,日志事件本身就携带了数值类型的字段,配合JSON Template Layout可以直接序列化为数字类型,适合需要更精细控制日志结构的场景。
内容的提问来源于stack exchange,提问作者ILLegaL
相关产品推荐
相关产品推荐

