blob: 29db2214f0c0328ab1a4c85aae87ac570ae21481 [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 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`]
[source,json]
----
{
"mdc": "${json:mdc}",
"exception": {
"exception_class": "${json:exception:className}",
"exception_message": "${json:exception:message}",
"stacktrace": "${json:exception:stackTrace:text}"
},
"line_number": "${json:source:lineNumber}",
"class": "${json:source:className}",
"@version": 1,
"source_host": "${hostName}",
"message": "${json:message}",
"thread_name": "${json:thread:name}",
"@timestamp": "${json:timestamp}",
"level": "${json:level}",
"file": "${json:source:fileName}",
"method": "${json:source:methodName}",
"logger_name": "${json:logger: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
| includes the filename and line number in the output (defaults to `false` set
by `log4j.layout.jsonTemplate.locationInfoEnabled` property)
| stackTraceEnabled
| boolean
| includes 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:JsonLayout.json` set by `log4j.layout.jsonTemplate.eventTemplateUri`
property)
| eventTemplateAdditionalFields
| KeyValuePair[]
| 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 the truncated strings (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
One can configure additional event template fields via
`eventTemplateAdditionalFields` as follows:
[source,xml]
----
<JsonTemplateLayout ...>
<EventTemplateAdditionalFields>
<KeyValuePair key="serviceName" value="auth-service"/>
<KeyValuePair key="containerId" value="6ede3f0ca7d9"/>
</EventTemplateAdditionalFields>
</JsonTemplateLayout>
----
[#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 TLAB 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 `LogEvent` 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:JsonLayout.json` provided by the `log4j-layout-json-template`
artifact:
[source,json]
----
{
"instant": {
"epochSecond": "${json:timestamp:epoch:secs,integral}",
"nanoOfSecond": "${json:timestamp:epoch:secs.nanos}"
},
"thread": "${json:thread:name}",
"level": "${json:level}",
"loggerName": "${json:logger:name}",
"message": "${json:message}",
"thrown": {
"message": "${json:exception:message}",
"name": "${json:exception:className}",
"extendedStackTrace": "${json:exception:stackTrace}"
},
"contextStack": "${json:ndc}",
"endOfBatch": "${json:endOfBatch}",
"loggerFqcn": "${json:logger:fqcn}",
"contextMap": "${json:mdc}",
"threadId": "${json:thread:id}",
"threadPriority": "${json:thread:priority}",
"source": {
"class": "${json:source:className}",
"method": "${json:source:methodName}",
"file": "${json:source:fileName}",
"line": "${json:source:lineNumber}"
}
}
----
`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[log4j-jsonevent-layout]
- 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 an always document-store-friendly flat structure.)
Below is the list of supported event template variables:
.`LogEvent` template variables
[cols="1m,4"]
|===
| Variable Name
| Description
| endOfBatch
| `logEvent.isEndOfBatch()`
| exception:className
| `logEvent.getThrown().getClass().getCanonicalName()`
| exception:message
| `logEvent.getThrown().getMessage()`
| exception:stackTrace
| `logEvent.getThrown().getStackTrace()` (inactive when `stackTraceEnabled=false`)
| exception:stackTrace:text
| `logEvent.getThrown().printStackTrace()` (inactive when `stackTraceEnabled=false`)
| exceptionRootCause:className
| the innermost `exception:className` in causal chain
| exceptionRootCause:message
| the innermost `exception:message` in causal chain
| exceptionRootCause:stackTrace[:text]
| the innermost `exception:stackTrace[:text]` in causal chain
| level
| `logEvent.getLevel()`
| level:severity
| https://en.wikipedia.org/wiki/Syslog#Severity_levels[Syslog severity] keyword
of `logEvent.getLevel()`
| level:severity:code
| https://en.wikipedia.org/wiki/Syslog#Severity_levels[Syslog severity] code of
`logEvent.getLevel()`
| logger:fqcn
| `logEvent.getLoggerFqcn()`
| logger:name
| `logEvent.getLoggerName()`
| main:<key>
| performs link:lookups.html#AppMainArgsLookup[Main Argument Lookup] for the
given `key`
| map:<key>
| performs link:lookups.html#MapLookup[Map Lookup] for the given `key`
| marker:name
| `logEvent.getMarker.getName()`
| mdc
.4+| Mapped Diagnostic Context `Map<String, String>` returned by
`logEvent.getContextData()`, where one can merge it with the parent JSON
object via `flatten[=<prefix>]`, filter keys matching a regex `pattern`, just
extract a certain `key`, or `stringify` values
| mdc:flatten[=<prefix>][,stringify]
| mdc:pattern=<pattern>[,flatten=<prefix>][,stringify]
| mdc:key=<key>[,stringify]
| message
| `logEvent.getFormattedMessage()`
| message:json
| if `logEvent.getMessage()` is of type `MultiformatMessage` and supports JSON,
its read value; if is of type `ObjectMessage`, its serialized output via
Jackson `ObjectMapper`; otherwise, `{"message": <formattedMessage>}` object
| ndc[:pattern=<pattern>]
| Nested Diagnostic Context `String[]` returned by `logEvent.getContextStack()`,
where filtering is supported via the supplied regex `pattern`
| pattern:<pattern>
| injects link:layouts.html#PatternLayout[`PatternLayout`] serialized string
described by the `pattern` parameter
| source:className
| `logEvent.getSource().getClassName()`
| source:fileName
| `logEvent.getSource().getFileName()` (inactive when `locationInfoEnabled=false`)
| source:lineNumber
| `logEvent.getSource().getLineNumber()` (inactive when `locationInfoEnabled=false`)
| source:methodName
| `logEvent.getSource().getMethodName()`
| thread:id
| `logEvent.getThreadId()`
| thread:name
| `logEvent.getThreadName()`
| thread:priority
| `logEvent.getThreadPriority()`
| timestamp
.4+| `logEvent.getInstant()` formatted using optional
`pattern` (defaults to `yyyy-MM-dd'T'HH:mm:ss.SSSZZZ` set by
`log4j.layout.jsonTemplate.timestampFormatPattern` property), `timeZone`
(defaults to `TimeZone.getDefault()` set by
`log4j.layout.jsonTemplate.timeZone` property), and `locale` (represented by
`language[_country[_variant]]` pattern, defaults to `Locale.getDefault()` set
by `log4j.layout.jsonTemplate.locale` property) parameters
| timestamp:pattern=<pattern>
| timestamp:timeZone=<timeZone>
| timestamp:locale=<locale>
| timestamp:epoch:nanos
| UTC epoch nanoseconds (of type `long`) derived from `logEvent.getInstant()`
| timestamp:epoch:<secs\|micros\|millis>[,integral]
| UTC epoch seconds, microseconds, or milliseconds (of type `double`) derived from
`logEvent.getInstant()` and, if `integral` is provided, cast to `long`
| timestamp:epoch:secs.<micros\|millis\|nanos>
.3+| UTC epoch fractions (of type `long`) derived from `logEvent.getInstant()`;
`secs.micros` denotes the "fractional part of epoch seconds, in microseconds",
`micros.millis` denotes the "fractional part of epoch microseconds, in
milliseconds", etc.
| timestamp:epoch:micros.<millis\|nanos>
| timestamp:epoch:millis.nanos
|===
In the following table, timestamp template variables are illustrated by
examples:
.`timestamp` template variable examples
[cols="1m,4m"]
|===
| Variable Name
| Output
|timestamp
|2020-02-07T13:38:47.098+02:00
|timestamp:pattern=yyyy-MM-dd'T'HH:mm:ss.SSS'Z',timeZone=UTC,locale=en_US
|2020-02-07T13:38:47.098Z
|timestamp:epoch:secs
|1581082727.982123456
|timestamp:epoch:secs,integral
|1581082727
|timestamp:epoch:millis
|1581082727982.123456
|timestamp:epoch:millis,integral
|1581082727982
|timestamp:epoch:micros
|1581082727982123.456
|timestamp:epoch:millis,integral
|1581082727982123
|timestamp:epoch:nanos
|1581082727982123456
|timestamp:epoch:secs.millis
|0000000000982
|timestamp:epoch:secs.micros
|0000000000982123
|timestamp:epoch:secs.nanos
|0000000000982123456
|timestamp:epoch:millis.micros
|0000000000000123
|timestamp:epoch:millis.nanos
|0000000000000123456
|timestamp:epoch:micros.nanos
|0000000000000000456
|===
[#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": "${json:stackTraceElement:className}",
"method": "${json:stackTraceElement:methodName}",
"file": "${json:stackTraceElement:fileName}",
"line": "${json:stackTraceElement:lineNumber}"
}
----
Below is the list of supported stack trace element template variables:
.`StackTraceElement` template variables
[cols="1m,4m"]
|===
| Variable Name
| Description
| stackTraceElement:className
| stackTraceElement.getClassName()
| stackTraceElement:methodName
| stackTraceElement.getMethodName()
| stackTraceElement:fileName
| stackTraceElement.getFileName()
| stackTraceElement:lineNumber
| stackTraceElement.getLineNumber()
|===
[#template-variables]
=== Template Variables
JSON field lookups are performed using the `${json:<variable-name>}` scheme
where `<variable-name>` is defined as `<resolver-name>[:<resolver-key>]`.
Characters following colon (`:`) are treated as the `resolver-key`.
link:lookups.html[Lookups] (e.g., `${java:version}`, `${env:USER}`,
`${date:MM-dd-yyyy}`) are supported in templates too. Though note that while
`${json:...}` template variables are expected to occupy an entire field, that
is, `"level": "${json:level}"`, a lookup can be mixed within a regular string as
in `"greeting": "Hello, ${env:USER}!"`.
[#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-garbage-free]
=== Is `JsonTemplateLayout` garbage-free?
Given the garbage-free layout behaviour enabler properties
`log4j2.enableDirectEncoders` and `log4j2.garbagefreeThreadContextMap` are set
to `true`, `JsonTemplateLayout` is garbage-free with the following exceptions:
* When recycling strategy is either `dummy` or `queue` but access concurrency
exceeds the configured `capacity`, then the recycler is not garbage-free.
* Since `Throwable#getStackTrace()` clones the original `StackTraceElement[]`,
access to (and hence rendering of) stack traces are not garbage-free.
* Serialization of ``ObjectMessage``s via `${json:message:json}` is 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.