I have been using log4j for different kind of projects and have some experience with log4j2. All implementations used the default appender and layout. Currently i need to write a application which writes in json format. So i tried the log4j2 JSONLayout layout by setting up a very simple log4j2 logger.
public class JSONLogger {
private static final Logger LOGGER = LogManager.getLogger();
public static void main(String[] args) {
JSONLogger jsonlogger = new JSONLogger() ;
}
public JSONLogger() {
LOGGER.log(Level.FATAL, "hi mum!") ;
int val1 = 10, val2 = 11, val3 = 12;
LOGGER.log(Level.FATAL,"val1={}, val2={}, val3={}", val1, val2, val3);
}
}
jsonLoggerProperties.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG">
<Properties>
<Property name="log-path">/Users/petervannes/NetBeansProjects/JSONLogger/logfiles</Property>
</Properties>
<Appenders>
<RollingFile name="json_file_appender" fileName="${log-path}/jsonlogger.json"
filePattern="${log-path}/%d{yyyyMMdd}_jsonlogger-%i.json" >
<JSONLayout complete="true" compact="false"/>
<Policies>
<SizeBasedTriggeringPolicy size="1 KB" />
</Policies>
<DefaultRolloverStrategy max="4"/>
</RollingFile>
</Appenders>
<Loggers>
<root level="debug" additivity="false">
<AppenderRef ref="json_file_appender"/>
</root>
</Loggers>
</Configuration>
Resulting in an log entry similar to;
, {
"timeMillis" : 1474573600359,
"thread" : "main",
"level" : "FATAL",
"loggerName" : "JSONLogger",
"message" : "val1=10, val2=11, val3=12",
"contextStack" : [ "fieldName2" ],
"endOfBatch" : false,
"loggerFqcn" : "org.apache.logging.log4j.spi.AbstractLogger",
"threadId" : 1,
"threadPriority" : 5
}
What i need is to log to a JSON format like this;
, {
"DateTime" : "08/01/2016 21:33:22.334",
"level" : "FATAL",
"Summary" : "Something has gone wrong",
"ChainManager" : "Manager A",
"Optionals" : { "Key_1": "Value1",
"Key_2": "Value2" }
}
Is this possibile with the log4j2 JSONLayout or is there any other layout i can use to get this format ?
We'll see how to do this for the two most widely used logging libraries: Log4j2 and Logback. Both use Jackson internally for representing logs in the JSON format. For an introduction to these libraries take a look at our introduction to Java Logging article.
Layout class and overrides the format() method to structure the logging information according to a supplied pattern. PatternLayout is also a simple Layout object that provides the following-Bean Property which can be set using the configuration file: Sr.No.
org.apache.log4j.helpers Formats a Date in the format "dd MMM yyyy HH:mm:ss,SSS" for example, "06 Nov 1994 15:49:37,459". Appends to sbuf the date in the format "dd MMM yyyy HH:mm:ss,SSS" for example, "06 Nov 1994 08:49:37,459".
The question is about writing a custom json messages using log4j2.
This is possible since version 2.11 of log4j2 version:
https://issues.apache.org/jira/browse/LOG4J2-2190
The new parameter of for JSONLayout is called
objectMessageAsJsonObject
. Sample project files;
log4j2.properties
status = error
appender.ana_whitespace.type = RollingFile
appender.ana_whitespace.name = ana_whitespace
appender.ana_whitespace.fileName = ${sys:es.logs.base_path:-target}${sys:file.separator}ana_whitespace.log
appender.ana_whitespace.layout.type = JsonLayout
appender.ana_whitespace.layout.propertiesAsList = false
appender.ana_whitespace.layout.compact = false
appender.ana_whitespace.layout.eventEol = true
appender.ana_whitespace.layout.objectMessageAsJsonObject = true
appender.ana_whitespace.layout.complete= true
appender.ana_whitespace.layout.properties= true
appender.ana_whitespace.filePattern = ${sys:es.logs.base_path:-target}${sys:file.separator}ana_whitespace-%d{yyyy-MM-dd}.log
appender.ana_whitespace.filter.1.type = MarkerFilter
appender.ana_whitespace.filter.1.onMismatch=DENY
appender.ana_whitespace.filter.1.onMatch=ACCEPT
appender.ana_whitespace.filter.1.marker=ANA_WHITESPACE
appender.ana_whitespace.policies.type = Policies
appender.ana_whitespace.policies.time.type = TimeBasedTriggeringPolicy
appender.ana_whitespace.policies.time.interval = 1
appender.ana_whitespace.policies.time.modulate = true
appender.ana_whitespace.policies.size.type = SizeBasedTriggeringPolicy
appender.ana_whitespace.policies.size.size = 10 MB
rootLogger.level = info
rootLogger.appenderRef.ana_whitespace.ref = ana_whitespace
Example Java code
package de.es.stemmer;
import java.io.IOException;
import java.util.Map;
import java.util.TreeMap;
import org.apache.http.client.ClientProtocolException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.message.ObjectMessage;
public class JsonLoggerTest {
final static Logger log = LogManager.getLogger(JsonLoggerTest.class);
final static Marker MARKER_WHITESPACE = MarkerManager.getMarker("ANA_WHITESPACE");
public static void main(String[] args) throws ClientProtocolException, IOException {
System.setProperty("es.logs.base_path", "target");
System.setProperty("es.logs.cluster_name", "_cluster");
LoggerContext.getContext().reconfigure();
ThreadContext.put("orig", "MDC_origValue");
ThreadContext.put("source", "MDC_sourceSnippet");
Map<String, String> map = new TreeMap<>();
map.put("orig", "msg_origValue");
map.put("source", "msg_sourceSnippet");
ObjectMessage msg = new ObjectMessage(map);
log.info(MARKER_WHITESPACE, msg);
ThreadContext.remove("orig");
ThreadContext.remove("source");
}
}
JSON Log Entry
[
{
"thread" : "main",
"level" : "INFO",
"loggerName" : "de.es.stemmer.JsonLoggerTest",
"marker" : {
"name" : "ANA_WHITESPACE"
},
"message" : {
"orig" : "msg_origValue",
"source" : "msg_sourceSnippet"
},
"endOfBatch" : false,
"loggerFqcn" : "org.apache.logging.log4j.spi.AbstractLogger",
"instant" : {
"epochSecond" : 1526576578,
"nanoOfSecond" : 184000000
},
"contextMap" : {
"orig" : "MDC_origValue",
"source" : "MDC_sourceSnippet"
},
"threadId" : 1,
"threadPriority" : 5
}
]
I know this is an old question but I think there is a better way to do it.
You should use the JSON Template Layout.
And then you will be able to configure your JsonLayout with a template like this one:
{
"mdc": {
"$resolver": "mdc"
},
"exception": {
"exception_class": {
"$resolver": "exception",
"field": "className"
},
"exception_message": {
"$resolver": "exception",
"field": "message"
},
"stacktrace": {
"$resolver": "exception",
"field": "stackTrace",
"stackTrace": {
"stringified": true
}
}
},
"line_number": {
"$resolver": "source",
"field": "lineNumber"
},
"class": {
"$resolver": "source",
"field": "className"
},
"@version": 1,
"source_host": "${hostName}",
"message": {
"$resolver": "message",
"stringified": true
},
"thread_name": {
"$resolver": "thread",
"field": "name"
},
"@timestamp": {
"$resolver": "timestamp"
},
"level": {
"$resolver": "level",
"field": "name"
},
"file": {
"$resolver": "source",
"field": "fileName"
},
"method": {
"$resolver": "source",
"field": "methodName"
},
"logger_name": {
"$resolver": "logger",
"field": "name"
}
}
There are many configurations available using templates.
See more about it here:
https://logging.apache.org/log4j/2.x/manual/json-template-layout.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With