blob: 97a8898a2b647d535ae7d9f860f575f0db8444d1 [file] [log] [blame]
////
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
////
= JSON Template Layout
Volkan Yazıcı <vy@apache.org>
`JsonTemplateLayout` is a customizable, efficient, and garbage-free JSON
emitting layout. It encodes ``LogEvent``s according to the structure described
by the JSON template provided. In a nutshell, it shines with its
* Customizable JSON structure (see `eventTemplate[Uri]` and
`stackTraceElementTemplate[Uri]` parameters)
* Customizable timestamp formatting (see `timestamp` parameter)
[#usage]
== Usage
Adding `log4j-layout-json-template` artifact to your list of dependencies is
enough to enable access to `JsonTemplateLayout` in your Log4j configuration:
[source,xml]
----
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-layout-json-template</artifactId>
<version>${log4j.version}</version>
</dependency>
----
For instance, given the following JSON template modelling the
https://github.com/logstash/log4j-jsonevent-layout[the official Logstash
`JSONEventLayoutV1`] (accessible via `classpath:LogstashJsonEventLayoutV1.json`)
[source,json]
----
{
"mdc": {
"$resolver": "mdc"
},
"exception": {
"exception_class": {
"$resolver": "exception",
"field": "className"
},
"exception_message": {
"$resolver": "exception",
"field": "message",
"stringified": true
},
"stacktrace": {
"$resolver": "exception",
"field": "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"
}
}
----
in combination with the below `log4j2.xml` configuration:
[source,xml]
----
<JsonTemplateLayout eventTemplateUri="classpath:LogstashJsonEventLayoutV1.json"/>
----
or with the below `log4j2.properties` configuration:
[source,ini]
----
appender.console.json.type = JsonTemplateLayout
appender.console.json.eventTemplateUri = classpath:LogstashJsonEventLayoutV1.json
----
`JsonTemplateLayout` emits JSON strings as follows:
[source,json]
----
{
"exception": {
"exception_class": "java.lang.RuntimeException",
"exception_message": "test",
"stacktrace": "java.lang.RuntimeException: test\n\tat org.apache.logging.log4j.JsonTemplateLayoutDemo.main(JsonTemplateLayoutDemo.java:11)\n"
},
"line_number": 12,
"class": "org.apache.logging.log4j.JsonTemplateLayoutDemo",
"@version": 1,
"source_host": "varlik",
"message": "Hello, error!",
"thread_name": "main",
"@timestamp": "2017-05-25T19:56:23.370+02:00",
"level": "ERROR",
"file": "JsonTemplateLayoutDemo.java",
"method": "main",
"logger_name": "org.apache.logging.log4j.JsonTemplateLayoutDemo"
}
----
[#layout-config]
== Layout Configuration
`JsonTemplateLayout` is configured with the following parameters:
.`JsonTemplateLayout` parameters
[cols="1m,1m,4"]
|===
| Parameter Name
| Type
| Description
| charset
| Charset
| `Charset` used for `String` encoding
| locationInfoEnabled
| boolean
| toggles access to the `LogEvent` source; file name, line number, etc.
(defaults to `false` set by `log4j.layout.jsonTemplate.locationInfoEnabled`
property)
| stackTraceEnabled
| boolean
| toggles access to the stack traces (defaults to `true` set by
`log4j.layout.jsonTemplate.stackTraceEnabled` property)
| eventTemplate
| String
| inline JSON template for rendering ``LogEvent``s (has priority over
`eventTemplateUri`, defaults to `null` set by
`log4j.layout.jsonTemplate.eventTemplate` property)
| eventTemplateUri
| String
| URI pointing to the JSON template for rendering ``LogEvent``s (defaults to
`classpath:EcsLayout.json` set by `log4j.layout.jsonTemplate.eventTemplateUri`
property)
| eventTemplateAdditionalFields
| EventTemplateAdditionalField[]
| additional key-value pairs appended to the root of the event template
| stackTraceElementTemplate
| String
| inline JSON template for rendering ``StackTraceElement``s (has priority over
`stackTraceElementTemplateUri`, defaults to `null` set by
`log4j.layout.jsonTemplate.stackTraceElementTemplate` property)
| stackTraceElementTemplateUri
| String
| JSON template for rendering ``StackTraceElement``s (defaults to
`classpath:StackTraceElementLayout.json` set by
`log4j.layout.jsonTemplate.stackTraceElementTemplateUri` property)
| eventDelimiter
| String
| delimiter used for separating emitted ``LogEvent``s (defaults to
`System.lineSeparator()` set by `log4j.layout.jsonTemplate.eventDelimiter`
property)
| maxStringLength
| int
| truncate string values longer than the specified limit (defaults to 16384 set
by `log4j.layout.jsonTemplate.maxStringLength` property)
| truncatedStringSuffix
| String
| suffix to append to strings truncated due to exceeding `maxStringLength`
(defaults to `…` set by `log4j.layout.jsonTemplate.truncatedStringSuffix`
property)
| recyclerFactory
| RecyclerFactory
| recycling strategy that can either be `dummy`, `threadLocal`, or `queue`
(set by `log4j.layout.jsonTemplate.recyclerFactory` property)
|===
[#additional-event-template-fields]
=== Additonal event template fields
Additional event template field is a convenient short-cut to add custom fields
to a template or override the fields of a template. Following configuration
overrides the `host` field of the `GelfLayout.json` template and adds two new
custom fields:
[source,xml]
----
<JsonTemplateLayout eventTemplateUri="classpath:GelfLayout.json">
<EventTemplateAdditionalFields>
<EventTemplateAdditionalField key="host" value="www.apache.org"/>
<EventTemplateAdditionalField key="_serviceName" value="auth-service"/>
<EventTemplateAdditionalField key="_containerId" value="6ede3f0ca7d9"/>
</EventTemplateAdditionalFields>
</JsonTemplateLayout>
----
One can also pass JSON literals into additional fields:
[source,xml]
----
<EventTemplateAdditionalField
key="marker"
type="JSON"
value='{"$resolver": "marker", "field": "name"}'/>
<EventTemplateAdditionalField
key="aNumber"
type="JSON"
value="1"/>
<EventTemplateAdditionalField
key="aList"
type="JSON"
value='[1,2,"string"]'/>
----
[#recycling-strategy]
=== Recycling strategy
`RecyclerFactory` plays a crucial role for determining the memory footprint of
the layout. Template resolvers employ it to create recyclers for objects that
they can reuse. The function of each `RecyclerFactory` and when one should
prefer one over another is explained below:
* `dummy` performs no recycling, hence each recycling attempt will result in a
new instance. This will obviously create a load on the garbage-collector. It
is a good choice for applications with low and medium log rate.
* `threadLocal` performs the best, since every instance is stored in
``ThreadLocal``s and accessed without any synchronization cost. Though this
might not be a desirable option for applications running with hundreds of
threads or more, e.g., a web servlet.
* `queue` is the best of both worlds. It allows recycling of objects up to a
certain number (`capacity`). When this limit is exceeded due to excessive
concurrent load (e.g., `capacity` is 50 but there are 51 threads concurrently
trying to log), it starts allocating. `queue` is a good strategy where
`threadLocal` is not desirable.
+
`queue` also accepts optional `supplier` (of type `java.util.Queue`, defaults to
`org.jctools.queues.MpmcArrayQueue.new` if JCTools is in the classpath;
otherwise `java.util.concurrent.ArrayBlockingQueue.new`) and `capacity` (of
type `int`, defaults to `max(8,2*cpuCount+1)`) parameters:
+
[source]
----
queue:supplier=org.jctools.queues.MpmcArrayQueue.new
queue:capacity=10
queue:supplier=java.util.concurrent.ArrayBlockingQueue.new,capacity=50
----
The default `RecyclerFactory` is `threadLocal`, if
`log4j2.enable.threadlocals=true`; otherwise, `queue`.
[#template-config]
== Template Configuration
Templates are configured by means of the following `JsonTemplateLayout`
parameters:
- `eventTemplate[Uri]` (for serializing ``LogEvent``s)
- `stackTraceElementTemplate[Uri]` (for serializing ``StackStraceElement``s)
- `eventTemplateAdditionalFields` (for extending the used event template)
[#event-templates]
=== Event Templates
`eventTemplate[Uri]` describes the JSON structure `JsonTemplateLayout` uses to
serialize ``LogEvent``s. The default configuration (accessible by
`log4j.layout.jsonTemplate.eventTemplate[Uri]` property) is set to
`classpath:EcsLayout.json` provided by the `log4j-layout-json-template`
artifact:
[source,json]
----
{
"@timestamp": {
"$resolver": "timestamp",
"pattern": {
"format": "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
"timeZone": "UTC"
}
},
"log.level": {
"$resolver": "level",
"field": "name"
},
"message": {
"$resolver": "message",
"stringified": true
},
"process.thread.name": {
"$resolver": "thread",
"field": "name"
},
"log.logger": {
"$resolver": "logger",
"field": "name"
},
"labels": {
"$resolver": "mdc",
"flatten": true,
"stringified": true
},
"tags": {
"$resolver": "ndc"
},
"error.type": {
"$resolver": "exception",
"field": "className"
},
"error.message": {
"$resolver": "exception",
"field": "message"
},
"error.stack_trace": {
"$resolver": "exception",
"field": "stackTrace",
"stringified": true
}
}
----
`log4j-layout-json-template` artifact contains the following predefined event
templates:
- https://github.com/apache/logging-log4j2/tree/master/log4j-layout-json-template/src/main/resources/EcsLayout.json[`EcsLayout.json`]
described by https://www.elastic.co/guide/en/ecs/current/ecs-reference.html[the Elastic Common Schema (ECS) specification]
- https://github.com/apache/logging-log4j2/tree/master/log4j-layout-json-template/src/main/resources/LogstashJsonEventLayoutV1.json[`LogstashJsonEventLayoutV1.json`]
described in https://github.com/logstash/log4j-jsonevent-layout[Logstash
`json_event` pattern for log4j]
- https://github.com/apache/logging-log4j2/tree/master/log4j-layout-json-template/src/main/resources/GelfLayout.json[`GelfLayout.json`]
described by https://docs.graylog.org/en/3.1/pages/gelf.html#gelf-payload-specification[the
Graylog Extended Log Format (GELF) payload specification] with additional
`_thread` and `_logger` fields. (Here it is advised to override the obligatory
`host` field with a user provided constant via `eventTemplateAdditionalFields`
to avoid `hostName` property lookup at runtime, which incurs an extra cost.)
- https://github.com/apache/logging-log4j2/tree/master/log4j-layout-json-template/src/main/resources/JsonLayout.json[`JsonLayout.json`]
providing the exact JSON structure generated by link:layouts.html#JSONLayout[`JsonLayout`]
with the exception of `thrown` field. (`JsonLayout` serializes the `Throwable`
as is via Jackson `ObjectMapper`, whereas `JsonLayout.json` template of
`JsonTemplateLayout` employs the `StackTraceElementLayout.json` template
for stack traces to generate a document-store-friendly flat structure.)
Below is the list of supported event template resolvers:
[#event-template-resolvers]
.`LogEvent` template resolvers
[cols="1m,3,2,2,4"]
|===
| Resolver Name
| Syntax
| Description
| Garbage Footprint
| Examples
| endOfBatch
|
| `logEvent.isEndOfBatch()`
| none
a|
[source,json]
----
{
"$resolver": "endOfBatch"
}
----
| exception
a|
[source]
----
config = field , [ stringified ]
field = "field" -> (
"className" \|
"message" \|
"stackTrace" )
stringified = "stringified" -> boolean
----
a|
Resolves fields of the `Throwable` returned by `logEvent.getThrown()`.
Note that this resolver is toggled by
`log4j.layout.jsonTemplate.stackTraceEnabled` property.
| Since `Throwable#getStackTrace()` clones the original `StackTraceElement[]`,
access to (and hence rendering of) stack traces are not garbage-free.
a|
Resolve `logEvent.getThrown().getClass().getCanonicalName()`:
[source,json]
----
{
"$resolver": "exception",
"field": "className"
}
----
Resolve the stack trace into a list of `StackTraceElement` objects:
[source,json]
----
{
"$resolver": "exception",
"field": "stackTrace"
}
----
Resolve the stack trace into a string field:
[source,json]
----
{
"$resolver": "exception",
"field": "stackTrace",
"stringified": true
}
----
| exceptionRootCause
| identical to `exception` resolver
a|
Resolves the fields of the innermost `Throwable` returned by
`logEvent.getThrown()`.
Note that this resolver is toggled by
`log4j.layout.jsonTemplate.stackTraceEnabled` property.
| identical to `exception` resolver
| identical to `exception` resolver
| level
a|
[source]
----
config = field , [ severity ]
field = "field" -> ( "name" \| "severity" )
severity = severity-field
severity-field = "field" -> ( "keyword" \| "code" )
----
| resolves the fields of the `logEvent.getLevel()`
| none
a|
Resolve the level name:
[source,json]
----
{
"$resolver": "level",
"field": "name"
}
----
Resolve the https://en.wikipedia.org/wiki/Syslog#Severity_levels[Syslog severity]
keyword:
[source,json]
----
{
"$resolver": "level",
"field": "severity",
"severity": {
"field": "keyword"
}
}
----
Resolve the https://en.wikipedia.org/wiki/Syslog#Severity_levels[Syslog severity]
code:
[source,json]
----
{
"$resolver": "level",
"field": "severity",
"severity": {
"field": "code"
}
}
----
| logger
a|
[source]
----
config = "field" -> ( "name" \| "fqcn" )
----
| resolves `logEvent.getLoggerFqcn()` and `logEvent.getLoggerName()`
| none
a|
Resolve the logger name:
[source,json]
----
{
"$resolver": "logger",
"field": "name"
}
----
Resolve the logger's fully qualified class name:
[source,json]
----
{
"$resolver": "logger",
"field": "fqcn"
}
----
| main:<key>
a|
[source]
----
config = ( index \| key )
index = "index" -> number
key = "key" -> string
----
| performs link:lookups.html#AppMainArgsLookup[Main Argument Lookup] for the
given `index` or `key`
| none
a|
Resolve the 1st `main()` method argument:
[source,json]
----
{
"$resolver": "main",
"index": 0
}
----
Resolve the argument coming right after `--userId`:
[source,json]
----
{
"$resolver": "main",
"key": "--userId"
}
----
| map
a|
[source]
----
config = key , [ stringified ]
key = "key" -> string
stringified = "stringified" -> boolean
----
| resolves the given `key` of ``MapMessage``s
| `stringified` flag translates to `String.valueOf(value)`, hence mind
not-`String`-typed values.
a|
Resolve the `userRole` field of the message:
[source,json]
----
{
"$resolver": "map",
"key": "userRole"
}
----
| marker
a|
[source]
----
config = "field" -> "name"
----
| `logEvent.getMarker().getName()`
| none
a|
Resolve the marker name:
[source,json]
----
{
"$resolver": "marker",
"field": "name"
}
----
| mdc
a|
[source]
----
config = singleAccess \| multiAccess
singleAccess = key , [ stringified ]
key = "key" -> string
stringified = "stringified" -> boolean
multi-access = [ pattern ] , [ flatten ] , [ stringified ]
pattern = "pattern" -> string
flatten = "flatten" -> ( boolean \| flattenConfig )
flattenConfig = [ flattenPrefix ]
flattenPrefix = "prefix" -> string
----
a| Mapped Diagnostic Context (MDC), aka. Thread Context Data, resolver.
`singleAccess` resolves the MDC value as is, whilst `multiAccess` resolves a
multitude of MDC values. If `flatten` is provided, `multiAccess` merges the
values 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.
a|
`log4j2.garbagefreeThreadContextMap` flag needs to be turned on to iterate
the map without allocations.
`stringified` allocates a new `String` for values that are not of type `String`.
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.
a|
Resolve the `userRole` MDC value:
[source,json]
----
{
"$resolver": "mdc",
"key": "userRole"
}
----
Resolve the string representation of the `userRank` MDC value:
[source,json]
----
{
"$resolver": "mdc",
"key": "userRank",
"stringified": true
}
----
Resolve all MDC entries into an object:
[source,json]
----
{
"$resolver": "mdc"
}
----
Resolve all MDC entries into an object such that values are converted to string:
[source,json]
----
{
"$resolver": "mdc",
"stringified": true
}
----
Merge all MDC entries whose keys are matching with the `user(Role\|Rank)` regex
into the parent:
[source,json]
----
{
"$resolver": "mdc",
"flatten": true,
"pattern": "user(Role\|Rank)"
}
----
After converting the corresponding entries to string, merge all MDC entries to
parent such that keys are prefixed with `_`:
[source,json]
----
{
"$resolver": "mdc",
"stringified": true,
"flatten": {
"prefix": "_"
}
}
----
| message
a|
[source]
----
config = [ stringified ] , [ fallbackKey ]
stringified = "stringified" -> boolean
fallbackKey = "fallbackKey" -> string
----
a| `logEvent.getMessage()`
| For simple string messages, the resolution is performed without allocations.
For ``ObjectMessage``s and ``MultiformatMessage``s, it depends.
a|
Resolve the message into a string:
[source,json]
----
{
"$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:
[source,json]
----
{
"$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.,
https://www.elastic.co/elasticsearch/[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 the problem:
[source,json]
----
{
"$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.
| ndc
a|
[source]
----
config = [ pattern ]
pattern = "pattern" -> string
----
| Resolves the Nested Diagnostic Context (NDC), aka. Thread Context Stack,
`String[]` returned by `logEvent.getContextStack()`
| none
a|
Resolve all NDC values into a list:
[source,json]
----
{
"$resolver": "ndc"
}
----
Resolve all NDC values matching with the `pattern` regex:
[source,json]
----
{
"$resolver": "ndc",
"pattern": "user(Role\|Rank):\\w+"
}
----
| source
a|
[source]
----
config = "field" -> (
"className" \|
"fileName" \|
"methodName" \|
"lineNumber" )
----
a|
Resolves the fields of the `StackTraceElement` returned by
`logEvent.getSource()`.
Note that this resolver is toggled by
`log4j.layout.jsonTemplate.locationInfoEnabled` property.
| none
a|
Resolve the line number:
[source,json]
----
{
"$resolver": "source",
"field": "lineNumber"
}
----
| thread
a|
[source]
----
config = "field" -> ( "name" \| "id" \| "priority" )
----
| resolves `logEvent.getThreadId()`, `logEvent.getThreadName()`,
`logEvent.getThreadPriority()`
| none
a|
Resolve the thread name:
[source,json]
----
{
"$resolver": "thread",
"field": "name"
}
----
| timestamp
a|
[source]
----
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
----
| resolves `logEvent.getInstant()` in various forms
| none
a|
.`timestamp` template resolver examples
[cols="5,2m"]
!===
! Configuration
! Output
a!
[source,json]
----
{
"$resolver": "timestamp"
}
----
! 2020-02-07T13:38:47.098+02:00
a!
[source,json]
----
{
"$resolver": "timestamp",
"pattern": {
"format": "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
"timeZone": "UTC",
"locale": "en_US"
}
}
----
! 2020-02-07T13:38:47.098Z
a!
[source,json]
----
{
"$resolver": "timestamp",
"epoch": {
"unit": "secs"
}
}
----
! 1581082727.982123456
a!
[source,json]
----
{
"$resolver": "timestamp",
"epoch": {
"unit": "secs",
"rounded": true
}
}
----
! 1581082727
a!
[source,json]
----
{
"$resolver": "timestamp",
"epoch": {
"unit": "secs.nanos"
}
}
----
! 982123456
a!
[source,json]
----
{
"$resolver": "timestamp",
"epoch": {
"unit": "millis"
}
}
----
! 1581082727982.123456
a!
[source,json]
----
{
"$resolver": "timestamp",
"epoch": {
"unit": "millis",
"rounded": true
}
}
----
! 1581082727982
a!
[source,json]
----
{
"$resolver": "timestamp",
"epoch": {
"unit": "millis.nanos"
}
}
----
! 123456
a!
[source,json]
----
{
"$resolver": "timestamp",
"epoch": {
"unit": "nanos"
}
}
----
! 1581082727982123456
!===
|===
[#stack-trace-element-templates]
=== Stack Trace Element Templates
`stackTraceElement[Uri]` describes the JSON structure `JsonTemplateLayout` uses
to format ``StackTraceElement``s. The default configuration (accessible by
`log4j.layout.jsonTemplate.stackTraceElementTemplate[Uri]` property) is set to
`classpath:StackTraceElementLayout.json` provided by the
`log4j-layout-json-template` artifact:
[source,json]
----
{
"class": {
"$resolver": "stackTraceElement",
"field": "className"
},
"method": {
"$resolver": "stackTraceElement",
"field": "methodName"
},
"file": {
"$resolver": "stackTraceElement",
"field": "fileName"
},
"line": {
"$resolver": "stackTraceElement",
"field": "lineNumber"
}
}
----
The allowed template configuration syntax is as follows:
[source]
----
config = "field" -> (
"className" |
"fileName" |
"methodName" |
"lineNumber" )
----
All above accesses to `StackTraceElement` is garbage-free.
[#features]
== Features
Below is a feature comparison matrix between `JsonTemplateLayout` and
alternatives.
.Feature comparison matrix
[cols="3,1,1,1,1"]
|===
| Feature
| `JsonTemplateLayout`
| link:layouts.html#JSONLayout[`JsonLayout`]
| link:layouts.html#GELFLayout[`GelfLayout`]
| https://github.com/elastic/java-ecs-logging/tree/master/log4j2-ecs-layout[`EcsLayout`]
| Java version
| 8
| 8
| 8
| 6
| Dependencies
| None
| Jackson
| None
| None
| Full schema customization?
|
|
|
|
| Timestamp customization?
|
|
|
|
| (Almost) garbage-free?
|
|
|
|
| Custom typed `Message` serialization?
|
|
|
| ?footnote:[Only for ``ObjectMessage``s and if Jackson is in the classpath.]
| Custom typed `MDC` value serialization?
|
|
|
|
| Rendering stack traces as array?
|
|
|
|
| JSON pretty print?
|
|
|
|
| Additional fields?
|
|
|
|
|===
[#faq]
== F.A.Q.
[#faq-lookups]
=== Are lookups supported in templates?
Yes, link:lookups.html[lookups] (e.g., `${java:version}`, `${env:USER}`,
`${date:MM-dd-yyyy}`) are supported in string literals of templates. Though note
that they are not garbage-free.
[#faq-garbage-free]
=== Is `JsonTemplateLayout` garbage-free?
Yes, if the garbage-free layout behaviour toggling properties
`log4j2.enableDirectEncoders` and `log4j2.garbagefreeThreadContextMap` are
enabled. Take into account the following caveats:
* The configured link:#recycling-strategy[recycling strategy] might not be
garbage-free.
* Since `Throwable#getStackTrace()` clones the original `StackTraceElement[]`,
access to (and hence rendering of) stack traces are not garbage-free.
* Serialization of ``MapMessage``s and ``ObjectMessage``s are mostly
garbage-free except for certain types (e.g., `BigDecimal`, `BigInteger`,
``Collection``s with the exception of `List`).
* link:lookups.html[Lookups] (that is, `${...}` variables, excluding
`${json:...}` ones) are not garbage-free.
Don't forget to checkout link:#event-template-resolvers[the notes on garbage footprint of resolvers]
you employ in templates.