JSON Template Layout :: Apache Log4j (original) (raw)
Templates are JSON documents, where objects containing $resolver
members, such as,
{
"$resolver": "message", (1)
"stringified": true (2)
}
1 | Indicating that this object should be replaced with the output from the message event template resolver |
---|---|
2 | Passing a configuration to the message event template resolver |
are interpreted by the JSON Template Layout compiler, and replaced with the referenced event or stack trace template resolver rendering that particular item.
Templates are configured by means of the following JSON Template Layout plugin attributes:
- eventTemplate and eventTemplateUri (for encoding
LogEvent
s) - stackTraceElementTemplate and stackTraceElementTemplateUri (for encoding
StackStraceElement
s) - EventTemplateAdditionalField (for extending the event template)
Event templates
eventTemplate and eventTemplateUri describe the JSON structure JSON Template Layout uses to encode LogEvent
s. JSON Template Layout contains the following predefined event templates:
The default event template modellingthe Elastic Common Schema (ECS) specification
LogstashJsonEventLayoutV1.json
Models Logstash json_event pattern for Log4j
Models the Graylog Extended Log Format (GELF) payload specification with additional _thread
and _logger
fields.
| | If used, it is advised to override the obligatory host field with a user provided constant via additional event template fields to avoid hostName property lookup at runtime, which incurs an extra cost. | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
Models the structure described by Google Cloud Platform structured logging with additional _thread
, _logger
and _exception
fields. The exception trace, if any, is written to the _exception
field as well as the message
field – the former is useful for explicitly searching/analyzing structured exception information, while the latter is Google’s expected place for the exception, and integrates with Google Error Reporting.
Models the exact structure generated by the deprecated JsonLayout with the exception of thrown
field.JsonLayout
serializes the Throwable
as is via Jackson ObjectMapper
, whereas JsonLayout.json
event template employs the StackTraceElementLayout.json
stack trace template for stack traces to generate a document-store-friendly flat structure.
| | This event template is only meant for existing users of the deprecated JsonLayout to migrate to JSON Template Layout without much trouble, and other than that purpose, is not recommended to be used! | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
Event template resolvers
Event template resolvers consume a LogEvent
and render a certain property of it at the point of the JSON where they are declared. For instance, marker
resolver renders the marker of the event, level
resolver renders the level, and so on. An event template resolver is denoted with a special object containing a $resolver
key:
Example event template demonstrating the usage of level
resolver
{
"version": "1.0",
"level": {
"$resolver": "level",
"field": "name"
}
}
Here version
field will be rendered as is, while level
field will be populated by the level resolver. That is, this template will generate JSON similar to the following:
Example JSON generated from the demonstrated event template
{
"version": "1.0",
"level": "INFO"
}
The complete list of available event template resolvers are provided below in detail.
counter
Resolves a number from an internal counter
counter
event template resolver grammar
config = [ start ] , [ overflowing ] , [ stringified ]
start = "start" -> number
overflowing = "overflowing" -> boolean
stringified = "stringified" -> boolean
Unless provided, start
and overflowing
are respectively set to 0
(zero) and true
by default.
When overflowing
is set to true
, the internal counter is created using a long
, which is subject to overflow while incrementing, though faster and garbage-free. Otherwise, a BigInteger
is used, which does not overflow, but incurs allocation costs.
When stringified
is enabled, which is set to false
by default, the resolved number will be converted to a string.
See examples
Resolves a sequence of numbers starting from 0. Once Long.MAX_VALUE
is reached, counter overflows to Long.MIN_VALUE
.
{
"$resolver": "counter"
}
Resolves a sequence of numbers starting from 1000. Once Long.MAX_VALUE
is reached, counter overflows to Long.MIN_VALUE
.
{
"$resolver": "counter",
"start": 1000
}
Resolves a sequence of numbers starting from 0 and keeps on doing as long as JVM heap allows.
{
"$resolver": "counter",
"overflowing": false
}
caseConverter
Converts the case of string values
caseConverter
event template resolver grammar
config = case , input , [ locale ] , [ errorHandlingStrategy ]
input = JSON
case = "case" -> ( "upper" | "lower" )
locale = "locale" -> (
language |
( language , "_" , country ) |
( language , "_" , country , "_" , variant )
)
errorHandlingStrategy = "errorHandlingStrategy" -> (
"fail" |
"pass" |
"replace"
)
replacement = "replacement" -> JSON
input
can be any available template value; e.g., a JSON literal, a lookup string, an object pointing to another resolver.
Unless provided, locale
points to the one returned by JsonTemplateLayoutDefaults.getLocale()
, which is configured by the log4j.layout.jsonTemplate.locale
system property and by default set to the default system locale.
errorHandlingStrategy
determines the behavior when either the input doesn’t resolve to a string value or case conversion throws an exception:
fail
Propagates the failure
pass
Causes the resolved value to be passed as is
replace
Suppresses the failure and replaces it with the replacement
, which is set to null
by default
errorHandlingStrategy
is set to replace
by default.
Most of the time JSON logs are persisted to a storage solution (e.g., Elasticsearch) that keeps a statically-typed index on fields. Hence, if a field is always expected to be of type string, using non-string replacement
s or pass
in errorHandlingStrategy
might result in type incompatibility issues at the storage level.
| | Unless the input value is passed intact or replaced, case conversion is not garbage-free. | | -------------------------------------------------------------------------------------------- |
See examples
Convert the resolved log level strings to upper-case:
{
"$resolver": "caseConverter",
"case": "upper",
"input": {
"$resolver": "level",
"field": "name"
}
}
Convert the resolved USER
environment variable to lower-case using nl_NL
locale:
{
"$resolver": "caseConverter",
"case": "lower",
"locale": "nl_NL",
"input": "${env:USER}"
}
Convert the resolved sessionId
thread context data (MDC) to lower-case:
{
"$resolver": "caseConverter",
"case": "lower",
"input": {
"$resolver": "mdc",
"key": "sessionId"
}
}
Above, if sessionId
MDC resolves to a, say, number, case conversion will fail. Since errorHandlingStrategy
is set to replace
and replacement is set to null
by default, the resolved value will be null
. One can suppress this behavior and let the resolved sessionId
number be left as is:
{
"$resolver": "caseConverter",
"case": "lower",
"input": {
"$resolver": "mdc",
"key": "sessionId"
},
"errorHandlingStrategy": "pass"
}
or replace it with a custom string:
{
"$resolver": "caseConverter",
"case": "lower",
"input": {
"$resolver": "mdc",
"key": "sessionId"
},
"errorHandlingStrategy": "replace",
"replacement": "unknown"
}
endOfBatch
Resolves LogEvent#isEndOfBatch()
boolean flag
exception
Resolves fields of the Throwable
returned by LogEvent#getThrown()
exception
event template resolver grammar
config = field , [ stringified ] , [ stackTrace ]
field = "field" -> ( "className" | "message" | "stackTrace" )
stackTrace = "stackTrace" -> (
[ stringified ]
, [ elementTemplate ]
)
stringified = "stringified" -> ( boolean | truncation )
truncation = "truncation" -> (
[ suffix ]
, [ pointMatcherStrings ]
, [ pointMatcherRegexes ]
)
suffix = "suffix" -> string
pointMatcherStrings = "pointMatcherStrings" -> string[]
pointMatcherRegexes = "pointMatcherRegexes" -> string[]
elementTemplate = "elementTemplate" -> object
stringified
is set to false
by default.stringified
at the root level is deprecated in favor of stackTrace.stringified
, which has precedence if both are provided.
pointMatcherStrings
and pointMatcherRegexes
enable the truncation of stringified stack traces after the given matching point. If both parameters are provided, pointMatcherStrings
will be checked first.
If a stringified stack trace truncation takes place, it will be indicated with a`suffix`, which by default is set to the configure truncatedStringSuffix
in the layout, unless explicitly provided. Every truncation suffix is prefixed with a newline.
Stringified stack trace truncation operates in Caused by:
and Suppressed:
label blocks. That is, matchers are executed against each label in isolation.
elementTemplate
is an object describing the template to be used while resolving the StackTraceElement
array. If stringified
is set to true
, elementTemplate
will be discarded. By default, elementTemplate
is set to null
and rather populated from the layout configuration. That is, the stack trace element template can also be provided using stackTraceElementTemplate and stackTraceElementTemplateUri layout configuration attributes. The template to be employed is determined in the following order:
elementTemplate
provided in the resolver configuration- stackTraceElementTemplate layout configuration attribute (The default is populated from the
log4j.layout.jsonTemplate.stackTraceElementTemplate
system property.) - stackTraceElementTemplateUri layout configuration attribute (The default is populated from the
log4j.layout.jsonTemplate.stackTraceElementTemplateUri
system property.)
See Stack trace element templates for the list of available resolvers in a stack trace element template.
Note that this resolver is toggled by the stackTraceEnabled layout configuration attribute.
| | Since Throwable#getStackTrace() clones the original StackTraceElement[], access to (and hence rendering of) stack traces are not garbage-free. Each pointMatcherRegexes item triggers a Pattern#matcher() call, which is not garbage-free either. | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
See examples
Resolve LogEvent#getThrown().getClass().getCanonicalName()
:
{
"$resolver": "exception",
"field": "className"
}
Resolve the stack trace into a list of StackTraceElement
objects:
{
"$resolver": "exception",
"field": "stackTrace"
}
Resolve the stack trace into a string field:
{
"$resolver": "exception",
"field": "stackTrace",
"stackTrace": {
"stringified": true
}
}
Resolve the stack trace into a string field such that the content will be truncated after the given point matcher:
{
"$resolver": "exception",
"field": "stackTrace",
"stackTrace": {
"stringified": {
"truncation": {
"suffix": "... [truncated]",
"pointMatcherStrings": ["at javax.servlet.http.HttpServlet.service"]
}
}
}
}
Resolve the stack trace into an object described by the provided stack trace element template:
{
"$resolver": "exception",
"field": "stackTrace",
"stackTrace": {
"elementTemplate": {
"class": {
"$resolver": "stackTraceElement",
"field": "className"
},
"method": {
"$resolver": "stackTraceElement",
"field": "methodName"
},
"file": {
"$resolver": "stackTraceElement",
"field": "fileName"
},
"line": {
"$resolver": "stackTraceElement",
"field": "lineNumber"
}
}
}
}
See Stack trace element templates for further details on resolvers available for StackTraceElement
templates.
exceptionRootCause
Resolves the fields of the innermost Throwable
returned by LogEvent#getThrown()
. Its syntax and garbage-footprint are identical to the exception resolver.
level
Resolves the fields of the LogEvent#getLevel()
level
event template resolver grammar
config = field , [ severity ]
field = "field" -> ( "name" | "severity" )
severity = severity-field
severity-field = "field" -> ( "keyword" | "code" )
See examples
Resolve the level name:
{
"$resolver": "level",
"field": "name"
}
{
"$resolver": "level",
"field": "severity",
"severity": {
"field": "keyword"
}
}
{
"$resolver": "level",
"field": "severity",
"severity": {
"field": "code"
}
}
logger
Resolves LogEvent#getLoggerFqcn()
and LogEvent#getLoggerName()
.
logger
event template grammar
config = "field" -> ( "name" | "fqcn" )
See examples
Resolve the logger name:
{
"$resolver": "logger",
"field": "name"
}
Resolve the logger’s fully qualified class name:
{
"$resolver": "logger",
"field": "fqcn"
}
main
main
event template resolver grammar
config = ( index | key )
index = "index" -> number
key = "key" -> string
See examples
Resolve the 1st main()
method argument:
{
"$resolver": "main",
"index": 0
}
Resolve the argument coming right after --userId
:
{
"$resolver": "main",
"key": "--userId"
}
map
Resolves MapMessage
s. See Map resolver for details.
marker
Resolves the marker of the event
marker
event template resolver grammar
config = "field" -> ( "name" | "parents" )
See examples
Resolve the marker name:
{
"$resolver": "marker",
"field": "name"
}
Resolve the names of the marker’s parents:
{
"$resolver": "marker",
"field": "parents"
}
mdc
Resolves the thread context map, aka. Mapped Diagnostic Context (MDC). See Map resolver for details.
| | log4j2.garbagefreeThreadContextMap flag needs to be turned on to iterate the map without allocations. | | -------------------------------------------------------------------------------------------------------- |
message
Resolves the message of the event
message
event template resolver grammar
config = [ stringified ] , [ fallbackKey ]
stringified = "stringified" -> boolean
fallbackKey = "fallbackKey" -> string
| | For simple string messages, the resolution is performed without allocations. For ObjectMessages and MultiformatMessages, it depends. | | --------------------------------------------------------------------------------------------------------------------------------------- |
See examples
Resolve the message into a string:
{
"$resolver": "message",
"stringified": true
}
Resolve the message such that if it is an ObjectMessage
or a MultiformatMessage
with JSON support, its type (string, list, object, etc.) will be retained:
{
"$resolver": "message"
}
Given the above configuration, a SimpleMessage
will generate a "sample log message"
, whereas a MapMessage
will generate a {"action": "login", "sessionId": "87asd97a"}
. Certain indexed log storage systems (e.g.,Elasticsearch) will not allow both values to coexist due to type mismatch: one is a string
while the other is an object
. Here one can use a fallbackKey
to work around this problem:
{
"$resolver": "message",
"fallbackKey": "formattedMessage"
}
Using this configuration, a SimpleMessage
will generate a {"formattedMessage": "sample log message"}
, and a MapMessage
will generate a {"action": "login", "sessionId": "87asd97a"}
. Note that both emitted JSONs are of type object
and have no type-conflicting fields.
messageParameter
Resolves LogEvent#getMessage().getParameters()
messageParameter
event template resolver grammar
config = [ stringified ] , [ index ]
stringified = "stringified" -> boolean
index = "index" -> number
| | Regarding garbage footprint, stringified flag translates to String.valueOf(value), hence mind values that are not String-typed. Further, LogEvent#getMessage() is expected to implement ParameterVisitable interface, which is the case if log4j2.enableThreadlocals property set to true. | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
See examples
Resolve the message parameters into an array:
{
"$resolver": "messageParameter"
}
Resolve the string representation of all message parameters into an array:
{
"$resolver": "messageParameter",
"stringified": true
}
Resolve the first message parameter:
{
"$resolver": "messageParameter",
"index": 0
}
Resolve the string representation of the first message parameter:
{
"$resolver": "messageParameter",
"index": 0,
"stringified": true
}
ndc
Resolves the thread context stack – aka. Nested Diagnostic Context (NDC), aka – String[]
returned by LogEvent#getContextStack()
ndc
event template resolver grammar
config = [ pattern ]
pattern = "pattern" -> string
See examples
Resolve all NDC values into a list:
Resolve all NDC values matching with the pattern
regex:
{
"$resolver": "ndc",
"pattern": "user(Role|Rank):\\w+"
}
pattern
pattern
event template resolver grammar
config = pattern , [ stackTraceEnabled ]
pattern = "pattern" -> string
stackTraceEnabled = "stackTraceEnabled" -> boolean
Unlike providing the pattern
attribute to Pattern Layout in a configuration file, property substitutions found in the pattern
will not be resolved.
The default value of stackTraceEnabled
is inherited from the parent JSON Template Layout.
| | This resolver is mostly intended as an emergency lever when all other JSON Template Layout resolvers fall short of addressing your need. If you find yourself using this, it is highly likely you are either doing something wrong, or JSON Template Layout needs some improvement. In either case, you are advised to share your use case with maintainers in one of our support channels. | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
See examples
Resolve the string produced by %p %c{1.} [%t] %X{userId} %X %m%ex
pattern:
{
"$resolver": "pattern",
"pattern": "%p %c{1.} [%t] %X{userId} %X %m%ex"
}
source
Resolves the fields of the StackTraceElement
returned by LogEvent#getSource()
source
event template resolver grammar
config = "field" -> (
"className" |
"fileName" |
"methodName" |
"lineNumber" )
Note that this resolver is toggled by the locationInfoEnabled layout configuration attribute.
| | Capturing the source location information is an expensive operation, and is not garbage-free.The logger resolver can generally be used as a zero-cost substitute for className. See this section of the layouts page for details. | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
See examples
Resolve the line number:
{
"$resolver": "source",
"field": "lineNumber"
}
thread
Resolves LogEvent#getThreadId()
, LogEvent#getThreadName()
, LogEvent#getThreadPriority()
thread
event template resolver grammar
config = "field" -> ( "name" | "id" | "priority" )
See examples
Resolve the thread name:
{
"$resolver": "thread",
"field": "name"
}
timestamp
Resolves LogEvent#getInstant()
timestamp
event template resolver grammar
config = [ patternConfig | epochConfig ]
patternConfig = "pattern" -> ( [ format ] , [ timeZone ] , [ locale ] )
format = "format" -> string
timeZone = "timeZone" -> string
locale = "locale" -> (
language |
( language , "_" , country ) |
( language , "_" , country , "_" , variant )
)
epochConfig = "epoch" -> ( unit , [ rounded ] )
unit = "unit" -> (
"nanos" |
"millis" |
"secs" |
"millis.nanos" |
"secs.nanos" |
)
rounded = "rounded" -> boolean
See examples
Table 1. timestamp template resolver examples
Configuration | Output |
---|---|
{ "$resolver": "timestamp" } | 2020-02-07T13:38:47.098+02:00 |
{ "$resolver": "timestamp", "pattern": { "format": "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "timeZone": "UTC", "locale": "en_US" } } | 2020-02-07T13:38:47.098Z |
{ "$resolver": "timestamp", "epoch": { "unit": "secs" } } | 1581082727.982123456 |
{ "$resolver": "timestamp", "epoch": { "unit": "secs", "rounded": true } } | 1581082727 |
{ "$resolver": "timestamp", "epoch": { "unit": "secs.nanos" } } | 982123456 |
{ "$resolver": "timestamp", "epoch": { "unit": "millis" } } | 1581082727982.123456 |
{ "$resolver": "timestamp", "epoch": { "unit": "millis", "rounded": true } } | 1581082727982 |
{ "$resolver": "timestamp", "epoch": { "unit": "millis.nanos" } } | 123456 |
{ "$resolver": "timestamp", "epoch": { "unit": "nanos" } } | 1581082727982123456 |
Map resolver
ReadOnlyStringMap is Log4j’s Map<String, Object>
equivalent with garbage-free accessors, and is heavily employed throughout the code base. It is the data structure backing both thread context map (aka., Mapped Diagnostic Context (MDC)) and MapMessage implementations. Hence template resolvers for both of these are provided by a single backend: ReadOnlyStringMapResolver
. Put another way, both mdc and map resolvers support identical configuration, behaviour, and garbage footprint, which are detailed below.
Map resolver grammar
config = singleAccess | multiAccess
singleAccess = key , [ stringified ]
key = "key" -> string
stringified = "stringified" -> boolean
multiAccess = [ pattern ] , [ replacement ] , [ flatten ] , [ stringified ]
pattern = "pattern" -> string
replacement = "replacement" -> string
flatten = "flatten" -> ( boolean | flattenConfig )
flattenConfig = [ flattenPrefix ]
flattenPrefix = "prefix" -> string
singleAccess
resolves a single field, whilst multiAccess
resolves a multitude of fields. If flatten
is provided, multiAccess
merges the fields with the parent, otherwise creates a new JSON object containing the values.
Enabling stringified
flag converts each value to its string representation.
Regex provided in the pattern
is used to match against the keys. If provided, replacement
will be used to replace the matched keys. These two are analogous to Pattern.compile(pattern).matcher(key).matches()
and Pattern.compile(pattern).matcher(key).replaceAll(replacement)
calls.
| | Regarding garbage footprint, stringified flag translates to String.valueOf(value), hence mind values that are not String-typed. pattern and replacement incur pattern matcher allocation costs. Writing certain non-primitive values (e.g., BigDecimal, Set, etc.) to JSON generates garbage, though most (e.g., int, long, String, List, boolean[], etc.) don’t. | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
See examples
"$resolver"
is left out in the following examples, since it is to be defined by the actual resolver, e.g., map
, mdc
.
Resolve the value of the field keyed with user:role
:
{
"$resolver": "…",
"key": "user:role"
}
Resolve the string representation of the user:rank
field value:
{
"$resolver": "…",
"key": "user:rank",
"stringified": true
}
Resolve all fields into an object:
Resolve all fields into an object such that values are converted to string:
{
"$resolver": "…",
"stringified": true
}
Resolve all fields whose keys match with the user:(role|rank)
regex into an object:
{
"$resolver": "…",
"pattern": "user:(role|rank)"
}
Resolve all fields whose keys match with the user:(role|rank)
regex into an object after removing the user:
prefix in the key:
{
"$resolver": "…",
"pattern": "user:(role|rank)",
"replacement": "$1"
}
Merge all fields whose keys are matching with the user:(role|rank)
regex into the parent:
{
"$resolver": "…",
"flatten": true,
"pattern": "user:(role|rank)"
}
After converting the corresponding field values to string, merge all fields to parent such that keys are prefixed with _
:
{
"$resolver": "…",
"stringified": true,
"flatten": {
"prefix": "_"
}
}
Stack trace element templates
exception and exceptionRootCause event template resolvers can encode an exception stack trace (i.e., StackTraceElement
returned by Throwable#getStackTrace()
) into a JSON array. While doing so, they employ the underlying JSON templating infrastructure again. The stack trace template used by these event template resolvers to encode StackTraceElement
s can be configured in following ways:
elementTemplate
provided in the resolver configuration- stackTraceElementTemplate layout configuration attribute (The default is populated from the
log4j.layout.jsonTemplate.stackTraceElementTemplate
system property.) - stackTraceElementTemplateUri layout configuration attribute (The default is populated from the
log4j.layout.jsonTemplate.stackTraceElementTemplateUri
system property.)
By default, stackTraceElementTemplateUri is set to classpath:StackTraceElementLayout.json
, which references to a classpath resource bundled with JSON Template Layout:
StackTraceElementLayout.json
bundled as a classpath resource
{
"class": {
"$resolver": "stackTraceElement",
"field": "className"
},
"method": {
"$resolver": "stackTraceElement",
"field": "methodName"
},
"file": {
"$resolver": "stackTraceElement",
"field": "fileName"
},
"line": {
"$resolver": "stackTraceElement",
"field": "lineNumber"
}
}
Stack trace element template resolvers
Similar to Event template resolvers consuming a LogEvent
and rendering a certain property of it at the point of the JSON where they are declared, stack trace element template resolvers consume a StackTraceElement
.
The complete list of available event template resolvers are provided below in detail.
stackTraceElement
Resolves certain fields of a StackTraceElement
Stack trace template resolver grammar
config = "field" -> (
"className" |
"fileName" |
"methodName" |
"lineNumber" )
All above accesses to StackTraceElement
is garbage-free.