ramazan polat
ramazan polat

Reputation: 7880

How to write a Java class that is converted to JSON by Log4j2 JSONLayout automatically?

Log4j2's JsonLayout converts the LogEvent content to JSON like this:

Java:

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

Output:

{
  "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
}

But when an object(bean) is passed to Logger, it spits the message as string.

Let's say my bean is something like this:

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;}
}

Then if try to write MyLogBean with something like this: Java:

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

Output:

{
  "timeMillis" : 1598613343782,
  "thread" : "main",
  "level" : "INFO",
  "loggerName" : "com.example.Test",
  "message" : "com.example.MyLogBean@328cf0e1",
  "endOfBatch" : false,
  "loggerFqcn" : "org.apache.logging.log4j.spi.AbstractLogger",
  "contextMap" : { },
  "threadId" : 1,
  "threadPriority" : 5
}

It prints "com.example.MyLogBean@328cf0e1" as message.

How to make it write the message in JSON like this:

{
  "timeMillis" : 1598613343782,
  "thread" : "main",
  "level" : "INFO",
  "loggerName" : "com.example.Test",
  "message" : {
      "message":"UserLogged",
      "userId":"[email protected]",
      "ip":"192.168.1.56"      
  },
  "endOfBatch" : false,
  "loggerFqcn" : "org.apache.logging.log4j.spi.AbstractLogger",
  "contextMap" : { },
  "threadId" : 1,
  "threadPriority" : 5
}

I need MyLogBeanclass to be converted to JSON automatically by JsonLayout. Unfortunately by default, JsonLayout uses toString method and put all object content as string.

I've tried to convert it to JSON by implementing MyLogBean class with Log4J Message interface but it still uses a string method(getFormattedMessage). Even if I use a string to JSON converter, the message again become a string the output become something like this:

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

Upvotes: 2

Views: 1164

Answers (2)

ramazan polat
ramazan polat

Reputation: 7880

UPDATE: With the 2.14.0 release of Log4J2, LogstashLayout is now integrated into Log4J2 itself as JsonTemplateLayout.

It turns out LogstashLayout is the custom layout that does the trick.

  1. Write "message": "${json:message:json}" in LogstashJsonEventLayoutV1.json file.
  2. Java object must implement the MultiformatMessage and return the JSON string in getFromattedMessage method.
  3. getMessageFormats() method also must return JSON.

Upvotes: 2

rgoers
rgoers

Reputation: 9141

The documentation says you should do:

<JsonLayout objectMessageAsJsonObject="true"/>

If you require the userId and ip on all log events you could add them to the ThreadContextMap. Then they would automatically appear in the contextMap section and you could simply log your message.

Another option option would be to include a toString() method in MyLogBean() that returns the relevant fields as a JSON string.

If you cannot modify the bean class you could create your own IntrospectingMessage that accepts the bean as a parameter and returns a JSON string of all the attributes when getFormattedMessage is called.

Upvotes: 1

Related Questions