在使用JSONLayout时如何将JSON对象添加到log4j2日志中

5
我将展示使用JSONLayout的log4j2,与消息中的对象相同。 例如,我的配置是:

cat log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
<Appenders>
    <Console name="Console" target="SYSTEM_OUT">
        <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
    <File name="Json" fileName="/home/jeus/log/loggerjson/main.log" bufferedIO="true" advertiseURI="file://home/jeus/log/loggerjson/main1.log" advertise="true">
        <JSONLayout compact="true" locationInfo="true" complete="false" eventEol="true" properties="true" propertiesAsList="true"/>
    </File>       
</Appenders>
<Loggers>
    <Root level="info">
        <AppenderRef ref="Json"/>
    </Root>
</Loggers>

我的输出结果是:

cat /home/jeus/log/loggerjson/main.log

{
 "timeMillis":1502359773290,
 "thread":"main",
 "level":"INFO",
 "loggerName":"com.jeus.logger.json.loggerjson.Main",
 "message":"This message is a raw",
 "endOfBatch":false,
 "loggerFqcn":"org.apache.logging.log4j.spi.AbstractLogger",
 "contextMap":[  ],
 "threadId":1,
 "threadPriority":5,
 "source":{  
     "class":"com.jeus.logger.json.loggerjson.Main",
     "method":"main",
     "file":"Main.java",
     "line":61
      }
}

我添加了一个 JSON 对象到日志中,但是消息中没有显示该 JSON 对象,而是使用 \" 字符来显示。

JSON 对象:

{"line_id": 12,"play_name":"Jeus"}

我的日志记录代码:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Main {

       private static final Logger LOGGER = LogManager.getLogger(Main.class);

       public static void main(String[] args) {


            String message = "{\"line_id\": 12,\"play_name\": \"Jeus\"}";
            LOGGER.info(message);

        }
 }

输出结果为:
{  
 "timeMillis":1502361394238,
 "thread":"main",
 "level":"INFO",
 "loggerName":"com.jeus.logger.json.loggerjson.Main",
 "message":"{\"line_id\": 12,\"play_name\": \"Jeus\"}",
 "endOfBatch":false,
 "loggerFqcn":"org.apache.logging.log4j.spi.AbstractLogger",
 "contextMap":[  

 ],
 "threadId":1,
 "threadPriority":5,
 "source":{  
    "class":"com.jeus.logger.json.loggerjson.Main",
    "method":"main",
    "file":"Main.java",
    "line":62
    }
 }

但我将把消息显示为一个类似于 JSON 对象的形式,如下所示:
 "message":{"line_id": 12,"play_name":"Jeus"},

可能有一种方法可以做到这一点,但不确定。请在log4j-user邮件列表上询问,以获取来自完整Log4j2社区的输入。 - Remko Popma
我认为在双引号之前应该显示\(反斜杠),否则生成的日志文件将不是有效的JSON文件。 - Vikas Sachdeva
另一个原因是log4j使用Jackson以JSON格式记录日志消息,如果您在输出流上编写JSON字符串,则会有反斜杠来转义双引号。 - Vikas Sachdeva
4个回答

1
这似乎无法使用内置的JsonLayout实现。无论我尝试什么,它都只会在字段上执行toString而不是正确地进行序列化。
一种解决方法是使用PatternLayout并像JSON格式化。
log4j.appender.frontEndAudit.layout=org.apache.log4j.PatternLayout
log4j.appender.frontEndAudit.layout.ConversionPattern={"timestamp": 
"%d{MM/dd/yyyy HH:mm:ss:SSSS}", "class": "%C", "file": "%F", "level" : 
"%5p", "line_number" : "%L", "logger_name": "frontEndAuditLog", "mdc": 
"ipAddress": "%X{ipAddress}", "requestId":"%X{requestId}", 
"sessionId":"%X{sessionId}"}, "message": %m, "method": "%M", 
"source_host":"%X{sourceHost}", "thread_name": "%t" }%n

这是针对log4j1的,但同样的概念也适用于log4j2。

如果我只添加%X,而不是"ipAddress": "%X{ipAddress}", "requestId":"%X{requestId}",它不应该打印整个值吗?像这样:"ipAddress": "actaulIP", "requestId":"givenRequestID"?我想要这样的东西,因为我的MDC值是动态的,但在log4j1.2属性文件中无法工作。 - Archana Mundaye
如何在log4j.properties中指定JSONFormatter模式?log4j.appender.REPORTSLOG = org.apache.log4j.RollingFileAppender log4j.appender.REPORTSLOG.layout = org.apache.log4j.PatternLayout log4j.appender.REPORTSLOG.File = ../logs/ReportLog.log log4j.appender.REPORTSLOG.layout.ConversionPattern=%m - ReportName=%X{ReportName} JNDI=%X{ReportJNDI}%n如何在log4j.properties中指定JSONFormatter模式? - Archana Mundaye

1

• 在log4j2.xml中,设置objectMessageAsJsonObject选项:

<JSONLayout objectMessageAsJsonObject="true" [...] />

• 使用ObjectMessage(示例代码,使用jackson):

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import org.apache.logging.log4j.*;
import org.apache.logging.log4j.message.ObjectMessage;

JsonNodeFactory f = JsonNodeFactory.instance;
JsonNode event = f.objectNode()
    .put("eventName", "abc")
    .set("person", f.objectNode()
        .put("firstName", "John")
        .put("lastName", "Doe")
    );

// "message":{"eventName":"abc","person":{"firstName":"John","lastName":"Doe"}}
logger.info(new ObjectMessage(event));

// "message":"{\"eventName\":\"abc\",\"person\":{\"firstName\":\"John\",\"lastName\":\"Doe\"}}"
logger.info(new ObjectMessage(event.toString()));

0

原来LogstashLayout是一个自定义布局,可以解决问题。

  1. LogstashJsonEventLayoutV1.json文件中写入"message": "${json:message:json}"
  2. 您必须记录实现MultiformatMessage的Java对象,并在getFromattedMessage方法中返回JSON字符串。
  3. getMessageFormats()方法也必须返回JSON

2
FYI - LogstashLayout已经合并到Log4j中,并将作为JsonTemplateLayout包含在2.14.0版本中。 - rgoers
@rgoers 因为它还没有发布,所以我不得不用 LogstashLayout 来展示解决方案。 - ramazan polat

0

log4j-layout-template-json 已经在版本 2.14.0 中发布,使用 MapMessage 可以完美运行。如果您正在使用 gradle,请添加以下依赖项:

compile 'org.apache.logging.log4j:log4j-api:2.14.0'
compile 'org.apache.logging.log4j:log4j-core:2.14.0'
compile 'org.apache.logging.log4j:log4j-layout-template-json:2.14.0'
compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.10.0'
compile 'com.fasterxml.jackson.dataformat:jackson-databind:2.10.0'

log4j2.yaml看起来像这样

Configuration:
  name: Default
  Appenders:
   Console:
    name: LogToConsole
    target: SYSTEM_OUT    
    JsonTemplateLayout:
      eventTemplateUri: "classpath:LogstashJsonEventLayoutV1.json"
  Loggers:
   AsyncRoot: 
     level: info
     additivity: false
     AppenderRef:
         - ref: LogToConsole

然后扩展MapMessage以记录您想要记录的对象(MyMapMessage),并使用它

private static final org.apache.logging.log4j.Logger LOGGER = LogManager.getLogger(YourClass.class);
LOGGER.error(myMapMessage.getData());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接