JBoss LogManager转义Log4j2 JSON日志致格式损坏求解
我已将日志从文件文本输出改为标准输出JSON格式,以便将可解析的JSON日志发送至带Kibana的Elastic Stack。WildFly服务器(版本22.0.1.Final或26.1.3.Final)使用的JBoss Logger也已切换为结构化可解析的JSON日志输出到标准输出。JBoss Logger会将Java应用(基于Log4j2)的日志包裹进自身日志消息,并补充额外上下文信息。
当JBoss Logger和Log4j2均配置为JSON格式时,应用的JSON日志在被JBoss Logger处理前会被转义,导致JSON格式损坏,后续工作流无法解析。例如message字段中的引号会变成\"log.level\": \"INFO\",而非正常的"log.level": "INFO"。若将JBoss Logger改回文本格式,应用JSON日志格式恢复正常,但我希望同时保留JBoss日志和应用日志的JSON格式。
如何在开启JBoss Logger JSON格式的前提下,禁用应用日志的转义,让应用Log4j2的JSON日志嵌套在JBoss的JSON日志中仍保持有效格式?
WildFly/JBoss Logger配置
<subsystem xmlns="urn:jboss:domain:logging:8.0"> <console-handler name="CONSOLE"> <level name="INFO"/> <formatter> <named-formatter name="JSON"/> </formatter> </console-handler> <periodic-rotating-file-handler name="FILE" autoflush="true"> <formatter> <named-formatter name="PATTERN"/> </formatter> <file relative-to="jboss.server.log.dir" path="server.log"/> <suffix value=".yyyy-MM-dd"/> <append value="true"/> </periodic-rotating-file-handler> <logger category="com.arjuna"> <level name="WARN"/> </logger> <logger category="io.jaegertracing.Configuration"> <level name="WARN"/> </logger> <logger category="org.jboss.as.config"> <level name="DEBUG"/> </logger> <logger category="sun.rmi"> <level name="WARN"/> </logger> <root-logger> <level name="INFO"/> <handlers> <handler name="CONSOLE"/> <handler name="FILE"/> </handlers> </root-logger> <formatter name="PATTERN"> <pattern-formatter pattern="%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"/> </formatter> <formatter name="COLOR-PATTERN"> <pattern-formatter pattern="%K{level}%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"/> </formatter> <formatter name="JSON"> <json-formatter/> </formatter> </subsystem>
Log4j2(版本2.19.0)配置
<?xml version="1.0" encoding="UTF-8"?> <Configuration packages="org.my_project_x.logging" status="WARN" strict="true" verbose="true"> <Appenders> <Console name="myConsoleLog"> <IgnoreExceptions>false</IgnoreExceptions> <EcsLayout serviceName="my_project_x" serviceVersion="${serverVersion}" includeOrigin="true" eventDataset="my_project_x.log"> <KeyValuePair key="hostname" value="%hostname"/> <KeyValuePair key="environment" value="develop"/> <KeyValuePair key="user.id" value="%userId"/> </EcsLayout> </Console> </Appenders> <Loggers> <Logger name="org.my_project_x"> <Level>INFO</Level> </Logger> <Root> <AppenderRef ref="myConsoleLog"/> <Level>WARN</Level> </Root> </Loggers> </Configuration>
方案1:绕过JBoss Logger,让Log4j2直接输出原始JSON
默认情况下Log4j2的ConsoleAppender输出会被JBoss Logger捕获并二次格式化,要避免嵌套转义,可让Log4j2直接写入标准输出流,并关闭JBoss对应用日志的处理:
- 更新Log4j2的ConsoleAppender配置,指定输出目标为原始标准输出:
<Console name="myConsoleLog" target="SYSTEM_OUT"> <IgnoreExceptions>false</IgnoreExceptions> <EcsLayout serviceName="my_project_x" serviceVersion="${serverVersion}" includeOrigin="true" eventDataset="my_project_x.log"> <KeyValuePair key="hostname" value="%hostname"/> <KeyValuePair key="environment" value="develop"/> <KeyValuePair key="user.id" value="%userId"/> </EcsLayout> </Console>
- 在WildFly的logging subsystem中,为应用日志类别添加
use-parent-handlers="false",阻止JBoss处理这些日志:
<logger category="org.my_project_x" use-parent-handlers="false"> <level name="INFO"/> </logger>
此方案下应用日志直接输出原始JSON,JBoss自身日志保持JSON格式,两者互不干扰,Elastic Stack可分别解析两类日志。
方案2:配置JBoss JSON Formatter解析嵌套JSON
若希望应用日志仍被JBoss Logger包裹,但保持内部JSON有效性,可开启JBoss JSON formatter的消息解析功能,将应用日志的JSON字段合并到顶层JSON对象中:
更新JBoss的JSON formatter配置,添加parse-message="true":
<formatter name="JSON"> <json-formatter parse-message="true"/> </formatter>
该配置会让JBoss Logger解析应用日志的JSON内容,将其中的字段(如log.level、serviceName等)直接合并到自身生成的JSON对象里,而非将整个JSON作为转义字符串放在message字段中。注意:此功能要求应用输出的日志是严格有效的JSON,否则可能导致解析失败。
方案3:统一使用JBoss Logger处理所有日志
若无需保留Log4j2的独立格式,可移除应用中的Log4j2配置,让所有日志通过JBoss Logger输出,统一使用JBoss的JSON formatter:
- 移除应用中的Log4j2配置文件,或排除Log4j2依赖,让应用使用JBoss提供的日志API(SLF4J + JBoss Logger)。
- 在JBoss的logging subsystem中通过
additional-field添加所需的自定义字段:
<formatter name="JSON"> <json-formatter> <additional-field name="serviceName" value="my_project_x"/> <additional-field name="environment" value="develop"/> <additional-field name="hostname" value="${jboss.host.name}"/> </json-formatter> </formatter>
此方式可完全统一日志格式,避免嵌套转义问题,但需修改应用的日志依赖和配置。
内容的提问来源于stack exchange,提问作者Ferdi

