Reputation: 7880
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 MyLogBean
class 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
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.
"message": "${json:message:json}"
in LogstashJsonEventLayoutV1.json
file.MultiformatMessage
and return the JSON string in getFromattedMessage
method.getMessageFormats()
method also must return JSON
.Upvotes: 2
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