如何编写由Log4j2 JSONLayout自动转换为JSON的Java类?

问题描述

Log4j2的JsonLayout将LogEvent内容转换为JSON,如下所示:

Java:

  logger.info("This is an info message");

输出

{
  "timeMillis" : 1598613343782,"thread" : "main","level" : "INFO","loggerName" : "com.example.Test","message" : "This is an info message","endOfBatch" : false,"loggerFqcn" : "org.apache.logging.log4j.spi.AbstractLogger","contextMap" : { },"threadId" : 1,"threadPriority" : 5
}

但是当将对象(bean)传递给Logger时,它将消息作为字符串吐出。

假设我的豆子是这样的:

public class MyLogBean {
    private String message;
    private String userId;
    private String ip;
    public MyLogBean(String message,String userId,String ip) {
        this.message = message;
        this.userId = userId;
        this.ip = ip;
    }
    public MyLogBean() {}
    public String getMessage() {return message;}
    public void setMessage(String message) {this.message = message;}
    public String getUserId() {return userId;}
    public void setUserId(String userId) {this.userId = userId;}
    public String getIp() {return ip;}
    public void setIp(String ip) {this.ip = ip;}
}

然后,如果尝试使用以下内容编写MyLogBean: Java:

    logger.info(new MyLogBean("UserLogged","[email protected]","192.168.1.56"));

输出

{
  "timeMillis" : 1598613343782,"message" : "com.example.MyLogBean@328cf0e1","threadPriority" : 5
}

它将"com.example.MyLogBean@328cf0e1"打印为消息。

如何使其像这样在JSON中编写消息:

{
  "timeMillis" : 1598613343782,"message" : {
      "message":"UserLogged","userId":"[email protected]","ip":"192.168.1.56"      
  },"threadPriority" : 5
}

我需要MyLogBean类由JsonLayout自动转换为JSON。不幸的是,认情况下,JsonLayout使用toString方法并将所有对象内容都放置为字符串。

我尝试通过使用Log4J Message接口实现MyLogBean类来将其转换为JSON,但是它仍然使用字符串方法getFormattedMessage)。即使我使用字符串到JSON的转换器,消息再次变成字符串,输出也会变成这样:

{
  "message" : "{\"message\":\"UserLogin\",\"userId\":\"john\",\"ip\":\"10.2.3.4\"}"
}

解决方法

文档说您应该这样做:

<JsonLayout objectMessageAsJsonObject="true"/>

如果在所有日志事件上都需要userId和ip,则可以将它们添加到ThreadContextMap中。然后它们将自动出现在contextMap部分中,您只需记录您的消息即可。

另一个选项是在MyLogBean()中包含toString()方法,该方法将相关字段作为JSON字符串返回。

如果您无法修改Bean类,则可以创建自己的IntrospectingMessage,该IntrospectingMessage接受Bean作为参数,并在调用getFormattedMessage时返回所有属性的JSON字符串。

,

事实证明,LogstashLayout是可以解决问题的自定义布局。

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