Breaking Down the Log4j JSONLayout


As we’ve seen recently in this post, the Layouts in Log4j are responsible of formatting log messages before they are sent to their final destination. The Log4j framework provides us with a few built-in Layouts like PatternLayout, JSONLayout...

In this post, we’ll focus on the JSONLayout - how it works, how to use it and we’ll see some examples for our mule apps.

What is the JSONLayout?

JSONLayout in Log4j2 is a layout that formats log events as JSON objects. It is particularly useful when integrating with log aggregation systems like ELK (Elasticsearch, Logstash, Kibana), Splunk, or other logging and monitoring tools that process JSON format for structured logging. Shipping the logs of our mule apps in JSON format will enable our log aggregation system to better use its search and analysis capabilities.


Why Using the JSONLayout instead of PatternLayout?

In a previous post, we discussed in detailed Why and When we should use the Log4j JSONLayout for our Mule Apps. Have a look at that post if you want to dive deeper. In a nutshell, the main reasons to use JSONLayout over the default PatternLayout are:
  • Log Format and Structure - Structured logs are easier to parse, query, and analyze using log aggregation and monitoring systems like ELK (Elasticsearch, Logstash, Kibana), Splunk, or Datadog. The PatternLayout produces unstructured or semi-structured text logs, which can be human-readable, but it’s harder for machines to parse.
  • Compatibility with Log Aggregation and Monitoring Systems - If you're sending MuleSoft logs to a log aggregation system (like ELK, Splunk, Graylog, or Loggly), using JSONLayout ensures that the logs are in a native format for those systems. This simplifies integration, reduces the need for parsing logic, and enables faster, more efficient log analysis.
  • Search and Queries - If you want to enable efficient and accurate searches in your logs, JSONLayout is far superior. Fields are explicitly named, and you can directly query them in log aggregation systems. This is especially important for debugging in distributed systems like MuleSoft applications, where you may need to trace requests using identifiers like transactionId or correlationId.
  • Human Vs Machine Readability - While PatternLayout might be better for human-readable logs, JSONLayoutexcels when logs are primarily consumed by machines, such as log aggregation systems, alerting systems, or monitoring dashboards. In modern MuleSoft deployments, logs are often forwarded to such systems, making JSONLayout the better choice.
  • Log Enrichment (Adding Context) - JSONLayout handles log enrichment in a cleaner and more organized way, making it easy for downstream systems to process enriched data. This is particularly useful in MuleSoft applications where tracking business transactions or user sessions across different APIs or systems is critical.
  • Handling Complex Data (Exceptions, Nested Fields) - For handling exceptions and nested fields, JSONLayout provides a more structured approach. This makes log entries more machine-readable and easier to query or analyze programmatically.
So, in summary, if you’re forwarding logs to a log aggregation system, the JSONLayout is, by far, a better choice for the format of our mule apps logs.

How to configure the JSONLayout

Like all the Layouts in the Log4j framework, the JSONLayout configuration is added in the Appenders section of the log4j2.xml

As we saw in the POST FOR APPENDERS, each appender have to specify a Layout. For that we just need to include the XML element JSONLayout as a child element within each appender element. 

The specific configuration of the JSONLayout is done by adding attributes to this element. For example:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<!-- Console appender with JSON layout -->
<Console name="Console" target="SYSTEM_OUT">
<JSONLayout compact="false" eventEol="true" />
</Console>
</Appenders>

<Loggers>
<!-- Root logger using the Console appender -->
<Root level="info">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>

In here, the JSONLayout is included in the Console Appender and it includes the attributes compact and eventEol.

Let’s see next the different options we’ve got of configuration with these JSONLayout attributes:


JSONLayout Attributes

Here’s a detailed list of attributes for the JSONLayout in Log4j2 that would be useful for our Mule apps:

Compact

  • Controls whether the JSON output is compact (i.e., without line breaks) or pretty-printed.
  • Default: true (compact output).

 Example: compact="true"


<Console name="Console" target="SYSTEM_OUT">
<JSONLayout compact="true" eventEol="true" />
</Console>
    

Output:


{"timestamp":"2024-09-06T12:34:56.123+0200","level":"INFO","thread":"main","logger":"com.example.MyClass","message":"Application started"}


Example: compact="false"


<Console name="Console" target="SYSTEM_OUT">
<JSONLayout compact="false" eventEol="true" />
</Console>


Output (Pretty-printed):


{
"timestamp": "2024-09-06T12:34:56.123+0200",
"level": "INFO",
"thread": "main",
"logger": "com.example.MyClass",
"message": "Application started"
}


eventEol

  • Adds a newline character (\n) after each log event. This is useful for ensuring that each log event appears on a new line in the output.
  • Default: false.

Example: eventEol="true"

<Console name="Console" target="SYSTEM_OUT">
<JSONLayout compact="true" eventEol="true" />
</Console>


Output:

{"timestamp":"2024-09-06T12:34:56.123+0200","level":"INFO","thread":"main","logger":"com.example.MyClass","message":"Application started"}
{"timestamp":"2024-09-06T12:34:58.456+0200","level":"INFO","thread":"main","logger":"com.example.MyClass","message":"Another log event"}
(Each log event will be followed by a newline.)

Example: eventEol="false"

<Console name="Console" target="SYSTEM_OUT">
<JSONLayout compact="true" eventEol="false" />
</Console>


Output:

{"timestamp":"2024-09-06T12:34:56.123+0200","level":"INFO","thread":"main","logger":"com.example.MyClass","message":"Application started"}{"timestamp":"2024-09-06T12:34:58.456+0200","level":"INFO","thread":"main","logger":"com.example.MyClass","message":"Another log event"}
(No newline between log events.)


properties

  • Includes MDC (Mapped Diagnostic Context) or ThreadContext key-value pairs in the output. Useful for adding contextual data like transactionId, sessionId, etc.
  • Default: false.

Example: properties="true"

<Console name="Console" target="SYSTEM_OUT">
<JSONLayout compact="true" eventEol="true" properties="true" />
</Console>


Output:

{"timestamp":"2024-09-06T12:34:56.123+0200","level":"INFO","thread":"main","logger":"com.example.MyClass","message":"Application started","MDC":{"transactionId":"txn12345","userId":"user9876"}}
(Here, transactionId and userId are MDC values that were set in the log context.)

Example: properties="false"

<Console name="Console" target="SYSTEM_OUT">
<JSONLayout compact="true" eventEol="true" properties="false" />
</Console>


Output:

{"timestamp":"2024-09-06T12:34:56.123+0200","level":"INFO","thread":"main","logger":"com.example.MyClass","message":"Application started"}
(MDC values are not included in the output.)

includeStacktrace

  • Controls whether stack traces are included when an exception is logged.
  • Default: true.

Example: includeStacktrace="true"

<Console name="Console" target="SYSTEM_OUT">
<JSONLayout compact="true" eventEol="true" includeStacktrace="true" />
</Console>


Output:

{
"timestamp":"2024-09-06T12:34:56.123+0200",
"level":"ERROR",
"thread":"main",
"logger":"com.example.MyClass",
"message":"An error occurred",
"exception": {
"class": "java.lang.NullPointerException",
"message": "Null pointer exception",
"stacktrace": [
"com.example.MyClass.method(MyClass.java:42)",
"com.example.MyClass.main(MyClass.java:28)"
]
}
}
(Full stack trace is included in the JSON output.)

Example: includeStacktrace="false"

<Console name="Console" target="SYSTEM_OUT">
<JSONLayout compact="true" eventEol="true" includeStacktrace="false" />
</Console>


Output:

{
"timestamp":"2024-09-06T12:34:56.123+0200",
"level":"ERROR",
"thread":"main",
"logger":"com.example.MyClass",
"message":"An error occurred"
}
(No stack trace is included in the output.)

complete

  • If set to true, wraps the entire log output in a single JSON object, making the log file a valid JSON object as a whole. If false, each log entry is treated as a standalone JSON object.
  • Default: false.

Example: complete="true"

<Console name="Console" target="SYSTEM_OUT">
<JSONLayout compact="true" eventEol="true" complete="true" />
</Console>


Output:

[
{"timestamp":"2024-09-06T12:34:56.123+0200","level":"INFO","thread":"main","logger":"com.example.MyClass","message":"Application started"},
{"timestamp":"2024-09-06T12:35:56.456+0200","level":"ERROR","thread":"main","logger":"com.example.MyClass","message":"An error occurred"}
]
(All logs are wrapped inside a JSON array, making it a valid JSON file.)

Example: complete="false"

<Console name="Console" target="SYSTEM_OUT">
<JSONLayout compact="true" eventEol="true" complete="false" />
</Console>


Output:

{"timestamp":"2024-09-06T12:34:56.123+0200","level":"INFO","thread":"main","logger":"com.example.MyClass","message":"Application started"}
{"timestamp":"2024-09-06T12:35:56.456+0200","level":"ERROR","thread":"main","logger":"com.example.MyClass","message":"An error occurred"}
(Each log entry is a separate JSON object. The log output does not form a valid JSON array.)


includeNullDelimeter

  • Adds a null byte (\0) as a delimiter at the end of each log event. This is useful for some log processing systems that expect a null byte as a separator.
  • Default: false.

Example: includeNullDelimiter="true"

<Console name="Console" target="SYSTEM_OUT">
<JSONLayout compact="true" eventEol="false" includeNullDelimiter="true" />
</Console>


Output:

{"timestamp":"2024-09-06T12:34:56.123+0200","level":"INFO","thread":"main","logger":"com.example.MyClass","message":"Application started"}\0
(The log entry is followed by a null byte (\0).)

Example: includeNullDelimiter="false"

<Console name="Console" target="SYSTEM_OUT">
<JSONLayout compact="true" eventEol="false" includeNullDelimiter="false" />
</Console>


Output:

{"timestamp":"2024-09-06T12:34:56.123+0200","level":"INFO","thread":"main","logger":"com.example.MyClass","message":"Application started"}
(No null byte is added at the end of the log event.)

objectMessageAsJsonObject

  • determines whether the message object (when it's an object and not a string) is serialized as a JSON object within the log event. When set to true, if the log message is an object, it will be serialized as a JSON object instead of a string. This is useful when you want to log complex objects (such as a POJO or a Map) directly as structured JSON, rather than converting them to a string.
  • Default: false.

Example: objectMessageAsJsonObject="true"

<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<JSONLayout compact="false" objectMessageAsJsonObject="true"/>
</Console>
</Appenders>

When 
objectMessageAsJsonObject is set to true, the User object will be serialized as a JSON object, and the resulting log will look something like this (formatted for readability):

{
"timestamp" : "2024-09-06T12:34:56.789+0200",
"level" : "INFO",
"thread" : "main",
"logger" : "MyClass",
"message" : {
"username" : "JohnDoe",
"email" : "john.doe@example.com",
"age" : 35
}
}

If 
objectMessageAsJsonObject were set to false (or omitted), the User object would be logged as a string: 
{
"timestamp" : "2024-09-06T12:34:56.789+0200",
"level" : "INFO",
"thread" : "main",
"logger" : "MyClass",
"message" : "User{username=JohnDoe, email=john.doe@example.com, age=35}"
}

This attribute is especially helpful when integrating with log aggregation systems like 
ELK, where structured logs improve searchability and log correlation.

Previous Post Next Post