From ee07dc34633b9f64e3e816222974b8c489d527cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Sun, 2 Jun 2024 21:10:08 +0200 Subject: [PATCH 01/53] Revamp JTL page --- .../ROOT/examples/hibernate/log4j2.json | 8 +- .../eventTemplateAdditionalField/log4j2.json | 41 + .../log4j2.properties | 38 + .../eventTemplateAdditionalField/log4j2.xml | 52 + .../eventTemplateAdditionalField/log4j2.yaml | 41 + .../json-template-layout/usage/log4j2.json | 20 + .../usage/log4j2.properties | 23 + .../json-template-layout/usage/log4j2.xml | 36 + .../json-template-layout/usage/log4j2.yaml | 29 + .../pages/manual/json-template-layout.adoc | 1371 +++++++++-------- .../modules/ROOT/pages/manual/layouts.adoc | 132 +- 11 files changed, 1045 insertions(+), 746 deletions(-) create mode 100644 src/site/antora/modules/ROOT/examples/manual/json-template-layout/eventTemplateAdditionalField/log4j2.json create mode 100644 src/site/antora/modules/ROOT/examples/manual/json-template-layout/eventTemplateAdditionalField/log4j2.properties create mode 100644 src/site/antora/modules/ROOT/examples/manual/json-template-layout/eventTemplateAdditionalField/log4j2.xml create mode 100644 src/site/antora/modules/ROOT/examples/manual/json-template-layout/eventTemplateAdditionalField/log4j2.yaml create mode 100644 src/site/antora/modules/ROOT/examples/manual/json-template-layout/usage/log4j2.json create mode 100644 src/site/antora/modules/ROOT/examples/manual/json-template-layout/usage/log4j2.properties create mode 100644 src/site/antora/modules/ROOT/examples/manual/json-template-layout/usage/log4j2.xml create mode 100644 src/site/antora/modules/ROOT/examples/manual/json-template-layout/usage/log4j2.yaml diff --git a/src/site/antora/modules/ROOT/examples/hibernate/log4j2.json b/src/site/antora/modules/ROOT/examples/hibernate/log4j2.json index 593aa3c0487..4859561c8ca 100644 --- a/src/site/antora/modules/ROOT/examples/hibernate/log4j2.json +++ b/src/site/antora/modules/ROOT/examples/hibernate/log4j2.json @@ -36,10 +36,10 @@ ], "Root": { - "level": "WARN" - }, - "AppenderRef": { - "ref": "CONSOLE" + "level": "WARN", + "AppenderRef": { + "ref": "CONSOLE" + } } } } diff --git a/src/site/antora/modules/ROOT/examples/manual/json-template-layout/eventTemplateAdditionalField/log4j2.json b/src/site/antora/modules/ROOT/examples/manual/json-template-layout/eventTemplateAdditionalField/log4j2.json new file mode 100644 index 00000000000..b900cdf8dc9 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/json-template-layout/eventTemplateAdditionalField/log4j2.json @@ -0,0 +1,41 @@ +{ + "Configuration": { + "Appenders": { + "Console": { + "name": "CONSOLE", + "JsonTemplateLayout": { + "eventTemplateUri": "classpath:GelfLayout.json", + "eventTemplateAdditionalField": [ + { + "key": "aString", + "value": "foo" //<1> + }, + { + "key": "marker", + "value": "{\"$resolver\": \"marker\", \"field\": \"name\"}", + "format": "JSON" + }, + { + "key": "aNumber", + "value": "1", + "format": "JSON" + }, + { + "key": "aList", + "value": "[1, 2, \"three\"]", + "format": "JSON" + } + ] + } + } + }, + "Loggers": { + "Root": { + "level": "WARN", + "AppenderRef": { + "ref": "CONSOLE" + } + } + } + } +} diff --git a/src/site/antora/modules/ROOT/examples/manual/json-template-layout/eventTemplateAdditionalField/log4j2.properties b/src/site/antora/modules/ROOT/examples/manual/json-template-layout/eventTemplateAdditionalField/log4j2.properties new file mode 100644 index 00000000000..ec1c15aea0f --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/json-template-layout/eventTemplateAdditionalField/log4j2.properties @@ -0,0 +1,38 @@ +# +# 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. +# +appender.0.type = Console +appender.0.name = CONSOLE +appender.0.layout.type = JsonTemplateLayout +appender.0.layout.eventTemplateUri = classpath:GelfLayout.json +appender.0.layout.eventTemplateAdditionalField[0].type = EventTemplateAdditionalField +appender.0.layout.eventTemplateAdditionalField[0].key = aString +appender.0.layout.eventTemplateAdditionalField[0].value = foo #<1> +appender.0.layout.eventTemplateAdditionalField[1].type = EventTemplateAdditionalField +appender.0.layout.eventTemplateAdditionalField[1].key = marker +appender.0.layout.eventTemplateAdditionalField[1].value = {"$resolver": "marker", "field": "name"} +appender.0.layout.eventTemplateAdditionalField[1].format = JSON +appender.0.layout.eventTemplateAdditionalField[2].type = EventTemplateAdditionalField +appender.0.layout.eventTemplateAdditionalField[2].key = aNumber +appender.0.layout.eventTemplateAdditionalField[2].value = 1 +appender.0.layout.eventTemplateAdditionalField[2].format = JSON +appender.0.layout.eventTemplateAdditionalField[3].type = EventTemplateAdditionalField +appender.0.layout.eventTemplateAdditionalField[3].key = aList +appender.0.layout.eventTemplateAdditionalField[3].value = [1, 2, "three"] +appender.0.layout.eventTemplateAdditionalField[3].format = JSON + +rootLogger.level = WARN +rootLogger.appenderRef.0.ref = CONSOLE diff --git a/src/site/antora/modules/ROOT/examples/manual/json-template-layout/eventTemplateAdditionalField/log4j2.xml b/src/site/antora/modules/ROOT/examples/manual/json-template-layout/eventTemplateAdditionalField/log4j2.xml new file mode 100644 index 00000000000..c6b2f32ae88 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/json-template-layout/eventTemplateAdditionalField/log4j2.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/site/antora/modules/ROOT/examples/manual/json-template-layout/eventTemplateAdditionalField/log4j2.yaml b/src/site/antora/modules/ROOT/examples/manual/json-template-layout/eventTemplateAdditionalField/log4j2.yaml new file mode 100644 index 00000000000..0349dd25758 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/json-template-layout/eventTemplateAdditionalField/log4j2.yaml @@ -0,0 +1,41 @@ +# +# 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. +# +Configuration: + + Appenders: + Console: + name: "CONSOLE" + JsonTemplateLayout: + eventTemplateUri: "classpath:GelfLayout.json" + eventTemplateAdditionalField: + - key: "aString" + value: "foo" #<1> + - key: "marker" + value: '{"$resolver": "marker", "field": "name"}' + format: "JSON" + - key: "aNumber" + value: "1" + format: "JSON" + - key: "aList" + value: '[1, 2, "three"]' + format: "JSON" + + Loggers: + Root: + level: "WARN" + AppenderRef: + ref: "CONSOLE" diff --git a/src/site/antora/modules/ROOT/examples/manual/json-template-layout/usage/log4j2.json b/src/site/antora/modules/ROOT/examples/manual/json-template-layout/usage/log4j2.json new file mode 100644 index 00000000000..35fbd8e51e6 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/json-template-layout/usage/log4j2.json @@ -0,0 +1,20 @@ +{ + "Configuration": { + "Appenders": { + "Console": { + "name": "CONSOLE", + "JsonTemplateLayout": { + "eventTemplateUri": "classpath:MyLayout.json" + } + } + }, + "Loggers": { + "Root": { + "level": "WARN", + "AppenderRef": { + "ref": "CONSOLE" + } + } + } + } +} diff --git a/src/site/antora/modules/ROOT/examples/manual/json-template-layout/usage/log4j2.properties b/src/site/antora/modules/ROOT/examples/manual/json-template-layout/usage/log4j2.properties new file mode 100644 index 00000000000..5d0cabbc887 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/json-template-layout/usage/log4j2.properties @@ -0,0 +1,23 @@ +# +# 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. +# +appender.0.type = Console +appender.0.name = CONSOLE +appender.0.layout.type = JsonTemplateLayout +appender.0.layout.eventTemplateUri = classpath:MyLayout.json + +rootLogger.level = WARN +rootLogger.appenderRef.0.ref = CONSOLE diff --git a/src/site/antora/modules/ROOT/examples/manual/json-template-layout/usage/log4j2.xml b/src/site/antora/modules/ROOT/examples/manual/json-template-layout/usage/log4j2.xml new file mode 100644 index 00000000000..89abf7bc371 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/json-template-layout/usage/log4j2.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + diff --git a/src/site/antora/modules/ROOT/examples/manual/json-template-layout/usage/log4j2.yaml b/src/site/antora/modules/ROOT/examples/manual/json-template-layout/usage/log4j2.yaml new file mode 100644 index 00000000000..ebc22455fa2 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/json-template-layout/usage/log4j2.yaml @@ -0,0 +1,29 @@ +# +# 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. +# +Configuration: + + Appenders: + Console: + name: "CONSOLE" + JsonTemplateLayout: + eventTemplateUri: "classpath:MyLayout.json" + + Loggers: + Root: + level: "WARN" + AppenderRef: + ref: "CONSOLE" diff --git a/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc b/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc index c600488a670..97096be97b8 100644 --- a/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc @@ -16,18 +16,15 @@ //// = JSON Template Layout -`JsonTemplateLayout` is a customizable, efficient, and garbage-free JSON -generating layout. It encodes ``LogEvent``s according to the structure described -by the JSON template provided. In a nutshell, it shines with its +JSON Template Layout is a customizable, efficient, and garbage-free JSON generating 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]` xref:#layout-config[layout configuration] parameters) +* Customizable JSON structure (see `eventTemplate[Uri]` and `stackTraceElementTemplate[Uri]` xref:#layout-config[layout configuration] parameters) -* Customizable timestamp formatting (see xref:#event-template-resolver-timestamp[] - event template resolver) +* Customizable timestamp formatting (see xref:#event-template-resolver-timestamp[] event template resolver) -* Feature rich exception formatting (see xref:#event-template-resolver-exception[] - and xref:#event-template-resolver-exceptionRootCause[] event template resolvers) +* Feature rich exception formatting (see xref:#event-template-resolver-exception[] and xref:#event-template-resolver-exceptionRootCause[] event template resolvers) * xref:manual/extending.adoc[Extensible plugin support] @@ -36,402 +33,456 @@ by the JSON template provided. In a nutshell, it shines with its [#usage] == Usage -Adding `log4j-layout-template-json` artifact to your list of dependencies is -enough to enable access to `JsonTemplateLayout` in your Log4j configuration: +Adding `log4j-layout-template-json` artifact to your list of dependencies is enough to enable access to JSON Template Layout in your Log4j configuration: +[tabs] +==== +Maven:: ++ [source,xml,subs="+attributes"] ---- - - org.apache.logging.log4j - log4j-layout-template-json - {log4j-layout-template-json-version} - + + + + + + + + org.apache.logging.log4j + log4j-layout-template-json + runtime + + + + + +---- + +Gradle:: ++ +[source,groovy,subs="+attributes"] +---- +dependencies { + + // Assuming `log4j-bom` is imported <1> + + runtimeOnly // <2> + 'org.apache.logging.log4j:log4j-slf4j2-impl' + +} ---- +==== +<1> Assuming you xref:manual/installation.adoc#bom[imported `org.apache.logging.log4j:log4j-bom`] +<2> For applications, logging implementation dependencies are required at _runtime_. +If you want to use JSON Template Layout for tests, you need to switch the scope to _test_. +See xref:manual/installation.adoc[] for details. -For instance, given the following JSON template modelling -https://www.elastic.co/guide/en/ecs/current/ecs-reference.html[the Elastic Common Schema (ECS) specification] -(accessible via `classpath:EcsLayout.json`) +JSON Template Layout is primarily configured through an *event template* describing the structure log events should be JSON-encoded in. +Event templates themselves are also JSON documents, where objects containing `$resolver` members, such as, [source,json] ---- { - "@timestamp": { + "$resolver": "message", // <1> + "stringified": true // <2> +} +---- +<1> Indicating that this object should be replaced with the output from the xref:#event-template-resolver-message[`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. + +For instance, given the following event template stored in `MyLayout.json` in your classpath: + +[source,json] +---- +{ + "instant": { // <1> "$resolver": "timestamp", "pattern": { "format": "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "timeZone": "UTC" } }, - "ecs.version": "1.2.0", - "log.level": { - "$resolver": "level", - "field": "name" - }, - "message": { + "someConstant": 1, // <2> + "message": { // <3> "$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", - "stackTrace": { - "stringified": true - } } } ---- +<1> Using xref:#event-template-resolver-timestamp[the `timestamp` event template resolver] to populate the `instant` field +<2> Passing a constant that will be rendered as is +<3> Using xref:#event-template-resolver-message[the `message` event template resolver] to populate the `message` field -in combination with the below `log4j2.xml` configuration: +in combination with the below layout configuration: +[tabs] +==== +XML:: ++ +.Snippet from an example {antora-examples-url}/manual/json-template-layout/usage/log4j2.xml[`log4j2.xml`] [source,xml] ---- - +include::example$manual/json-template-layout/usage/log4j2.xml[lines=26..26,indent=0] ---- -or with the below `log4j2.properties` configuration: +JSON:: ++ +.Snippet from an example {antora-examples-url}/manual/json-template-layout/usage/log4j2.json[`log4j2.json`] +[source,json] +---- +include::example$manual/json-template-layout/usage/log4j2.json[lines=6..8,indent=0] +---- + +YAML:: ++ +.Snippet from an example {antora-examples-url}/manual/json-template-layout/usage/log4j2.yaml[`log4j2.yaml`] +[source,xml] +---- +include::example$manual/json-template-layout/usage/log4j2.yaml[lines=22..23,indent=0] +---- -[source,properties] +Properties:: ++ +.Snippet from an example {antora-examples-url}/manual/json-template-layout/usage/log4j2.properties[`log4j2.properties`] +[source,xml] ---- -appender.console.layout.type = JsonTemplateLayout -appender.console.layout.eventTemplateUri = classpath:EcsLayout.json +include::example$manual/json-template-layout/usage/log4j2.properties[lines=19..20,indent=0] ---- +==== -`JsonTemplateLayout` generates JSON as follows: +JSON Template Layout generates JSON as follows: [source,json] ---- -{ - "@timestamp": "2017-05-25T19:56:23.370Z", - "ecs.version": "1.2.0", - "log.level": "ERROR", - "message": "Hello, error!", - "process.thread.name": "main", - "log.logger": "org.apache.logging.log4j.JsonTemplateLayoutDemo", - "error.type": "java.lang.RuntimeException", - "error.message": "test", - "error.stack_trace": "java.lang.RuntimeException: test\n\tat org.apache.logging.log4j.JsonTemplateLayoutDemo.main(JsonTemplateLayoutDemo.java:11)\n" -} +{"instant":"2017-05-25T19:56:23.370Z","someConstant":1,"message":"Hello, error!"} //<1> ---- +<1> JSON pretty-printing is not supported for performance reasons. + +Good news is *JSON Template Layout is perfectly production-ready without any configuration!* +The event template defaults to `EcsLayout.json`, bundled in the classpath, modelling https://www.elastic.co/guide/en/ecs/current/ecs-reference.html[the Elastic Common Schema (ECS) specification]. +JSON Template Layout bundles several more xref:#event-templates[predefined event templates] modeling popular JSON-based log formats. [#layout-config] -== Layout Configuration +== Layout configuration + +This section explains how to configure JSON Template Layout plugin element in a Log4j configuration file. + +[TIP] +==== +Are you trying to implement your own event (or stack trace) template and looking for help on available resolvers? +Please refer to xref:#template-config[] instead. +==== -`JsonTemplateLayout` is configured with the following parameters: +[#plugin-attrs] +=== Plugin attributes -.`JsonTemplateLayout` parameters -[cols="1m,1m,4"] +JSON Template Layout plugin configuration accepts the following attributes: + +[#plugin-attr-charset] +==== `charset` + +[cols="2h,6"] |=== -| Parameter Name -| Type -| Description - -| charset -| Charset -| `Charset` used for `String` encoding - -| [[locationInfoEnabled]]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) - -| eventTemplateRootObjectKey -| String -| if present, the event template is put into a JSON object composed of a single - member with the provided key (defaults to `null` set by - `log4j.layout.jsonTemplate.eventTemplateRootObjectKey` - property) - -| eventTemplateAdditionalField -| 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 -| URI pointing to the JSON template for rendering ``StackTraceElement``s - (defaults to `classpath:StackTraceElementLayout.json` set by - `log4j.layout.jsonTemplate.stackTraceElementTemplateUri` property) - -| eventDelimiter -| String -| delimiter used for separating rendered ``LogEvent``s (defaults to - `System.lineSeparator()` set by `log4j.layout.jsonTemplate.eventDelimiter` - property) - -| nullEventDelimiterEnabled -| boolean -| append `\0` (`null`) character to the end of every `eventDelimiter` - separating rendered ``LogEvent``s (defaults to `false` set by - `log4j.layout.jsonTemplate.nullEventDelimiterEnabled` property) - -| [[maxStringLength]] 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) +|Type |`Charset` +|Default value |`UTF-8` +|Configuration property |`log4j.layout.jsonTemplate.charset` |=== -[#additional-event-template-fields] -=== Additional event template fields +`Charset` used for encoding the produced JSON into bytes -Additional event template fields are a convenient short-cut to add custom fields -to a template or override the existing ones. Following configuration overrides -the `host` field of the `GelfLayout.json` template and adds two new custom -fields: +[#plugin-attr-locationInfoEnabled] +==== [[locationInfoEnabled]]`locationInfoEnabled` -.XML configuration with additional fields -[source,xml] ----- - - - - - ----- +[cols="2h,6"] +|=== +|Type |`boolean` +|Default value |`false` +|Configuration property |`log4j.layout.jsonTemplate.locationInfoEnabled` +|=== -The default `format` for the added new fields are `String`. -One can also provide JSON-formatted additional fields: +Toggles access to the `LogEvent` source; file name, line number, etc. -.XML-formatted configuration with JSON-formatted additional fields -[source,xml] ----- - - - - - ----- - -Additional event template fields can very well be introduced using properties-, -YAML-, and JSON-formatted configurations: - -.Properties-formatted configuration with JSON-formatted additional fields -[source,properties] ----- -appender.console.layout.type = JsonTemplateLayout -appender.console.layout.eventTemplateUri = classpath:GelfLayout.json -appender.console.layout.eventTemplateAdditionalField[0].type = EventTemplateAdditionalField -appender.console.layout.eventTemplateAdditionalField[0].key = marker -appender.console.layout.eventTemplateAdditionalField[0].value = {"$resolver": "marker", "field": "name"} -appender.console.layout.eventTemplateAdditionalField[0].format = JSON -appender.console.layout.eventTemplateAdditionalField[1].type = EventTemplateAdditionalField -appender.console.layout.eventTemplateAdditionalField[1].key = aNumber -appender.console.layout.eventTemplateAdditionalField[1].value = 1 -appender.console.layout.eventTemplateAdditionalField[1].format = JSON -appender.console.layout.eventTemplateAdditionalField[2].type = EventTemplateAdditionalField -appender.console.layout.eventTemplateAdditionalField[2].key = aList -appender.console.layout.eventTemplateAdditionalField[2].value = [1, 2, "three"] -appender.console.layout.eventTemplateAdditionalField[2].format = JSON ----- - -.YAML-formatted configuration with JSON-formatted additional fields -[source,yaml] ----- -JsonTemplateLayout: - eventTemplateAdditionalField: - - key: "marker" - value: '{"$resolver": "marker", "field": "name"}' - format: "JSON" - - key: "aNumber" - value: "1" - format: "JSON" - - key: "aList" - value: '[1, 2, "three"]' - format: "JSON" ----- - -.JSON-formatted configuration with JSON-formatted additional fields +[#plugin-attr-stackTraceEnabled] +==== `stackTraceEnabled` + +[cols="2h,6"] +|=== +|Type |`boolean` +|Default value |`true` +|Configuration property |`log4j.layout.jsonTemplate.stackTraceEnabled` +|=== + +Toggles access to the stack traces + +[#plugin-attr-eventTemplate] +==== `eventTemplate` + +[cols="2h,6"] +|=== +|Type |`String` +|Default value |`null` +|Configuration property |`log4j.layout.jsonTemplate.eventTemplate` +|=== + +Inline xref:#event-templates[event template] JSON for rendering ``LogEvent``s. +If present, this configuration overrides xref:#plugin-attr-eventTemplateUri[]. + +[#plugin-attr-eventTemplateUri] +==== `eventTemplateUri` + +[cols="2h,6"] +|=== +|Type |`String` +|Default value |`classpath:EcsLayout.json` +|Configuration property |`log4j.layout.jsonTemplate.eventTemplateUri` +|=== + +URI pointing to the xref:#event-templates[event template] JSON for rendering ``LogEvent``s. +This configuration is overriden by xref:#plugin-attr-eventTemplate[], if present. + +[#plugin-attr-eventTemplateRootObjectKey] +==== `eventTemplateRootObjectKey` + +[cols="2h,6"] +|=== +|Type |`String` +|Default value |`null` +|Configuration property |`log4j.layout.jsonTemplate.eventTemplateRootObjectKey` +|=== + +If present, the event template is put into a JSON object composed of a single member with the provided key. + +[#plugin-attr-stackTraceElementTemplate] +==== `stackTraceElementTemplate` + +[cols="2h,6"] +|=== +|Type |`String` +|Default value |`null` +|Configuration property |`log4j.layout.jsonTemplate.stackTraceElementTemplate` +|=== + +Inline xref:#stack-trace-element-templates[stack trace element template] JSON for rendering ``StackTraceElement``s +If present, this configuration overrides xref:#plugin-attr-stackTraceElementTemplateUri[]. + +[#plugin-attr-stackTraceElementTemplateUri] +==== `stackTraceElementTemplateUri` + +[cols="2h,6"] +|=== +|Type |`String` +|Default value |`classpath:StackTraceElementLayout.json` +|Configuration property |`log4j.layout.jsonTemplate.stackTraceElementTemplateUri` +|=== + +URI pointing to the xref:#stack-trace-element-templates[stack trace element template] JSON for rendering ``StackTraceElement``s. +This configuration is overriden by xref:#plugin-attr-stackTraceElementTemplate[], if present. + +[#plugin-attr-eventDelimiter] +==== `eventDelimiter` + +[cols="2h,6"] +|=== +|Type |`String` +|Default value |`System.lineSeparator()` +|Configuration property |`log4j.layout.jsonTemplate.eventDelimiter` +|=== + +Delimiter used for separating rendered ``LogEvent``s. +if xref:#plugin-attr-nullEventDelimiterEnabled[] is `true`, this value will be suffixed with `\0` (null) character. + +[#plugin-attr-nullEventDelimiterEnabled] +==== `nullEventDelimiterEnabled` + +[cols="2h,6"] +|=== +|Type |`String` +|Default value |`false` +|Configuration property |`log4j.layout.jsonTemplate.nullEventDelimiterEnabled` +|=== + +If `true`, xref:#plugin-attr-eventDelimiter[] will be suffixed with `\0` (null) character. + +[#plugin-attr-maxStringLength] +==== `maxStringLength` + +[cols="2h,6"] +|=== +|Type |`int` +|Default value |`16384` (16 KiB) +|Configuration property |`log4j.layout.jsonTemplate.maxStringLength` +|=== + +Causes _truncation_ of string values longer than the specified limit. +When a string value is truncated, its length will be shortened to the `maxStringLength` provided and xref:#plugin-attr-truncatedStringSuffix[] will be appended to indicate the truncation. + +[WARNING] +==== +Note that this doesn't cap the maximum length of the JSON document produced! +Consider a JSON document rendered using a `LogEvent` containing 20,000 thread context map (aka. MDC) entries such that each key/value is less than 16,384 characters. +This document will certainly exceed 16,384 characters, yet it will not be subject to any truncation, since every string value in the JSON document is less than 16,384 characters. +That is, + +.An example JSON document exceeding 16,384 characters in length, yet subject to no truncation [source,json] ---- { - "JsonTemplateLayout": { - "eventTemplateAdditionalField": [ - { - "key": "marker", - "value": "{\"$resolver\": \"marker\", \"field\": \"name\"}", - "format": "JSON" - }, - { - "key": "aNumber", - "value": "1", - "format": "JSON" - }, - { - "key": "aList", - "value": "[1, 2, \"three\"]", - "format": "JSON" - } - ] + "mdc": { + "key00001": "value00001", + "key00002": "value00002", + "key00003": "value00003", + // ... + "key16384": "value16384", } } ---- +==== -[#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 behavior 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. +[#plugin-attr-truncatedStringSuffix] +==== `truncatedStringSuffix` + +[cols="2h,6"] +|=== +|Type |`String` +|Default value |`…` +|Configuration property |`log4j.layout.jsonTemplate.truncatedStringSuffix` +|=== + +Suffix to append to strings truncated due to exceeding xref:#plugin-attr-maxStringLength[] + +[#plugin-attr-recyclerFactory] +==== `recyclerFactory` + +[cols="2h,6"] +|=== +|Type |`String` +|Default value |Refer to xref:#recycling-strategy[] +|Configuration property |`log4j.layout.jsonTemplate.recyclerFactory` +|=== + +Name of the xref:#recycling-strategy[] employed + +[#layout-elements] +=== Plugin elements + +JSON Template Layout plugin configuration accepts the following elements: + +[#layout-element-EventTemplateAdditionalField] +==== [[additional-event-template-fields]] `EventTemplateAdditionalField` + +Additional event template fields are convenient shortcuts to add custom fields to a template or override existing ones. +You can specify an additional event template field using an `EventTemplateAdditionalField` element composed of following attributes: + +`key`:: Entry key +`value`:: Entry value +`format`:: +Format of the value. +Accepted values are: +`STRING` (default)::: Indicates that the entry value will be string-encoded +`JSON`::: Indicates the the entry value is already formatted in JSON and will be appended to the event template verbatim + +Below we share an example configuration overriding the `GelfLayout.json` event template with certain custom fields: + +[tabs] +==== +XML:: + -`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: +.Snippet from an example {antora-examples-url}/manual/json-template-layout/eventTemplateAdditionalField/log4j2.xml[`log4j2.xml`] +[source,xml] +---- +include::example$manual/json-template-layout/eventTemplateAdditionalField/log4j2.xml[lines=26..42,indent=0] +---- + +JSON:: + -.Example configurations of `queue` recycling strategy -[source] +.Snippet from an example {antora-examples-url}/manual/json-template-layout/eventTemplateAdditionalField/log4j2.json[`log4j2.json`] +[source,json] ---- -queue:supplier=org.jctools.queues.MpmcArrayQueue.new -queue:capacity=10 -queue:supplier=java.util.concurrent.ArrayBlockingQueue.new,capacity=50 +include::example$manual/json-template-layout/eventTemplateAdditionalField/log4j2.json[lines=6..29,indent=0] ---- -The default `RecyclerFactory` is `threadLocal`, if -`log4j2.enable.threadlocals=true`; otherwise, `queue`. +YAML:: ++ +.Snippet from an example {antora-examples-url}/manual/json-template-layout/eventTemplateAdditionalField/log4j2.yaml[`log4j2.yaml`] +[source,xml] +---- +include::example$manual/json-template-layout/eventTemplateAdditionalField/log4j2.yaml[lines=22..35,indent=0] +---- -See <> for details on how to introduce custom -`RecyclerFactory` implementations. +Properties:: ++ +.Snippet from an example {antora-examples-url}/manual/json-template-layout/eventTemplateAdditionalField/log4j2.properties[`log4j2.properties`] +[source,xml] +---- +include::example$manual/json-template-layout/eventTemplateAdditionalField/log4j2.properties[lines=19..35,indent=0] +---- +==== +<1> Since the `format` attribute is not explicitly set, the default (i.e., `STRING`) will be used [#template-config] -== Template Configuration +== Template configuration + +Templates are JSON documents, where objects containing `$resolver` members, such as, + +[source,json] +---- +{ + "$resolver": "message", // <1> + "stringified": true // <2> +} +---- +<1> Indicating that this object should be replaced with the output from the xref:#event-template-resolver-message[`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 `JsonTemplateLayout` -parameters: +Templates are configured by means of the following JSON Template Layout plugin attributes: -- `eventTemplate[Uri]` (for serializing ``LogEvent``s) -- `stackTraceElementTemplate[Uri]` (for serializing ``StackStraceElement``s) -- `eventTemplateAdditionalField` (for extending the used event template) +- xref:#plugin-attr-eventTemplate[] and xref:#plugin-attr-eventTemplateUri[] (for encoding ``LogEvent``s) +- xref:#plugin-attr-stackTraceElementTemplate[] and xref:#plugin-attr-stackTraceElementTemplateUri[] (for encoding ``StackStraceElement``s) +- xref:#layout-element-EventTemplateAdditionalField[] (for extending the 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-template-json` -artifact, which contains the following predefined event templates: - -- https://github.com/apache/logging-log4j2/tree/main/log4j-layout-template-json/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/main/log4j-layout-template-json/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/main/log4j-layout-template-json/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 - xref:#additional-event-template-fields[additional event template fields] - to avoid `hostName` property lookup at runtime, which incurs an extra cost.) - -- https://github.com/apache/logging-log4j2/tree/main/log4j-layout-template-json/src/main/resources/GcpLayout.json[`GcpLayout.json`] - described by https://cloud.google.com/logging/docs/structured-logging[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 https://cloud.google.com/error-reporting[Google Error Reporting]. - -- https://github.com/apache/logging-log4j2/tree/main/log4j-layout-template-json/src/main/resources/JsonLayout.json[`JsonLayout.json`] - providing the exact JSON structure generated by xref:manual/layouts.adoc#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.) +=== Event templates + +xref:#plugin-attr-eventTemplate[] and xref:#plugin-attr-eventTemplateUri[] describe the JSON structure JSON Template Layout uses to encode ``LogEvent``s. +JSON Template Layout contains the following predefined event templates: + +{project-github-url}/tree/2.x/log4j-layout-template-json/src/main/resources/EcsLayout.json[`EcsLayout.json`]:: +The **default event template** modelling the https://www.elastic.co/guide/en/ecs/current/ecs-reference.html[the Elastic Common Schema (ECS) specification] + +{project-github-url}/tree/2.x/log4j-layout-template-json/src/main/resources/LogstashJsonEventLayoutV1.json[`LogstashJsonEventLayoutV1.json`]:: +Models https://github.com/logstash/log4j-jsonevent-layout[Logstash `json_event` pattern for Log4j] + +{project-github-url}/tree/2.x/log4j-layout-template-json/src/main/resources/GelfLayout.json[`GelfLayout.json`]:: +Models 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. ++ +[TIP] +==== +If used, it is advised to override the obligatory `host` field with a user provided constant via xref:#additional-event-template-fields[additional event template fields] to avoid `hostName` property lookup at runtime, which incurs an extra cost. +==== + +{project-github-url}/tree/2.x/log4j-layout-template-json/src/main/resources/GcpLayout.json[`GcpLayout.json`]:: +Models the structure described by https://cloud.google.com/logging/docs/structured-logging[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 https://cloud.google.com/error-reporting[Google Error Reporting]. + +{project-github-url}/tree/2.x/log4j-layout-template-json/src/main/resources/JsonLayout.json[`JsonLayout.json`]:: +Models the exact structure generated by the deprecated xref:manual/layouts.adoc#JSONLayout[`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. ++ +[WARNING] +==== +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 +==== 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: +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 [source,json] @@ -457,12 +508,14 @@ That is, this template will generate JSON similar to the following: } ---- -The complete list of available event template resolvers are provided below in -detail. +The complete list of available event template resolvers are provided below in detail. [#event-template-resolver-counter] ===== `counter` +Resolves a number from an internal counter + +.`counter` event template resolver grammar [source] ---- config = [ start ] , [ overflowing ] , [ stringified ] @@ -471,26 +524,17 @@ overflowing = "overflowing" -> boolean stringified = "stringified" -> boolean ---- -Resolves a number from an internal counter. +Unless provided, `start` and `overflowing` are respectively set to `0` (zero) and `true` by default. -Unless provided, `start` and `overflowing` are respectively set to 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. +When `stringified` is enabled, which is set to `false` by default, the resolved number will be converted to a string. -[WARNING] +.See examples +[%collapsible] ==== -When `overflowing` is set to `true`, the internal counter is created using a -`long`, which is subject to overflow while incrementing, though garbage-free. -Otherwise, a `BigInteger` is used, which does not overflow, but incurs -allocation costs. -==== - -====== Examples - -Resolves a sequence of numbers starting from 0. Once `Long.MAX_VALUE` is -reached, counter overflows to `Long.MIN_VALUE`. +Resolves a sequence of numbers starting from 0. Once `Long.MAX_VALUE` is reached, counter overflows to `Long.MIN_VALUE`. [source,json] ---- @@ -499,8 +543,7 @@ reached, counter overflows to `Long.MIN_VALUE`. } ---- -Resolves a sequence of numbers starting from 1000. Once `Long.MAX_VALUE` is -reached, counter overflows to `Long.MIN_VALUE`. +Resolves a sequence of numbers starting from 1000. Once `Long.MAX_VALUE` is reached, counter overflows to `Long.MIN_VALUE`. [source,json] ---- @@ -510,8 +553,7 @@ reached, counter overflows to `Long.MIN_VALUE`. } ---- -Resolves a sequence of numbers starting from 0 and keeps on doing as long as -JVM heap allows. +Resolves a sequence of numbers starting from 0 and keeps on doing as long as JVM heap allows. [source,json] ---- @@ -520,10 +562,14 @@ JVM heap allows. "overflowing": false } ---- +==== [#event-template-resolver-caseConverter] ===== `caseConverter` +Converts the case of string values + +.`caseConverter` event template resolver grammar [source] ---- config = case , input , [ locale ] , [ errorHandlingStrategy ] @@ -542,40 +588,29 @@ errorHandlingStrategy = "errorHandlingStrategy" -> ( replacement = "replacement" -> JSON ---- -Converts the case of string values. - -`input` can be any available template value; e.g., a JSON literal, a lookup -string, an object pointing to another resolver. +`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 -`log4j.layout.jsonTemplate.locale` system property and by default set to the -default system locale. +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: +`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 +`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. +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. [WARNING] ==== -Unless the input value is ``pass``ed intact or ``replace``d, case conversion is -not garbage-free. +Unless the input value is ``pass``ed intact or ``replace``d, case conversion is not garbage-free. ==== -====== Examples - +.See examples +[%collapsible] +==== Convert the resolved log level strings to upper-case: [source,json] @@ -590,8 +625,7 @@ Convert the resolved log level strings to upper-case: } ---- -Convert the resolved `USER` environment variable to lower-case using `nl_NL` -locale: +Convert the resolved `USER` environment variable to lower-case using `nl_NL` locale: [source,json] ---- @@ -618,9 +652,8 @@ Convert the resolved `sessionId` thread context data (MDC) to lower-case: ---- 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: +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: [source,json] ---- @@ -650,22 +683,19 @@ or replace it with a custom string: "replacement": "unknown" } ---- +==== [#event-template-resolver-endOfBatch] ===== `endOfBatch` -[source,json] ----- -{ - "$resolver": "endOfBatch" -} ----- - -Resolves `logEvent.isEndOfBatch()` boolean flag. +Resolves `LogEvent#isEndOfBatch()` boolean flag [#event-template-resolver-exception] ===== `exception` +Resolves fields of the `Throwable` returned by `LogEvent#getThrown()` + +.`exception` event template resolver grammar [source] ---- config = field , [ stringified ] , [ stackTrace ] @@ -689,60 +719,46 @@ pointMatcherRegexes = "pointMatcherRegexes" -> string[] elementTemplate = "elementTemplate" -> object ---- -Resolves fields of the `Throwable` returned by `logEvent.getThrown()`. - -`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. +`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. +`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 configured `truncatedStringSuffix` in -the layout, unless explicitly provided. Every truncation suffix is prefixed with -a newline. +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. +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[Uri]` layout configuration parameters. The template -to be employed is determined in the following order: +`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 xref:#plugin-attr-stackTraceElementTemplate[] and xref:#plugin-attr-stackTraceElementTemplateUri[] layout configuration attributes. +The template to be employed is determined in the following order: . `elementTemplate` provided in the resolver configuration -. `stackTraceElementTemplate` parameter from layout configuration -(the default is populated from `log4j.layout.jsonTemplate.stackTraceElementTemplate` -system property) +. xref:#plugin-attr-stackTraceElementTemplate[] layout configuration attribute +(The default is populated from the `log4j.layout.jsonTemplate.stackTraceElementTemplate` system property.) -. `stackTraceElementTemplateUri` parameter from layout configuration -(the default is populated from `log4j.layout.jsonTemplate.stackTraceElementTemplateUri` -system property) +. xref:#plugin-attr-stackTraceElementTemplateUri[] layout configuration attribute +(The default is populated from the `log4j.layout.jsonTemplate.stackTraceElementTemplateUri` system property.) -See <> -for the list of available resolvers in a stack trace element template. +See xref:#stack-trace-element-templates[] for the list of available resolvers in a stack trace element template. -Note that this resolver is toggled by -`log4j.layout.jsonTemplate.stackTraceEnabled` property. +Note that this resolver is toggled by the xref:#plugin-attr-stackTraceEnabled[] layout configuration attribute. [WARNING] ==== -Since `Throwable#getStackTrace()` clones the original `StackTraceElement[]`, -access to (and hence rendering of) stack traces are not garbage-free. +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. +Each `pointMatcherRegexes` item triggers a `Pattern#matcher()` call, which is not garbage-free either. ==== -====== Examples - -Resolve `logEvent.getThrown().getClass().getCanonicalName()`: +.See examples +[%collapsible] +==== +Resolve `LogEvent#getThrown().getClass().getCanonicalName()`: [source,json] ---- @@ -775,8 +791,7 @@ Resolve the stack trace into a string field: } ---- -Resolve the stack trace into a string field such that the content will be -truncated after the given point matcher: +Resolve the stack trace into a string field such that the content will be truncated after the given point matcher: [source,json] ---- @@ -794,8 +809,7 @@ truncated after the given point matcher: } ---- -Resolve the stack trace into an object described by the provided stack trace -element template: +Resolve the stack trace into an object described by the provided stack trace element template: [source,json] ---- @@ -825,19 +839,21 @@ element template: } ---- -See <> for further details on resolvers available -for ``StackTraceElement`` templates. +See xref:#stack-trace-element-templates[] for further details on resolvers available for `StackTraceElement` templates. +==== [#event-template-resolver-exceptionRootCause] ===== `exceptionRootCause` -Resolves the fields of the innermost `Throwable` returned by -`logEvent.getThrown()`. Its syntax and garbage-footprint are identical to the -xref:#event-template-resolver-exception[] resolver. +Resolves the fields of the innermost `Throwable` returned by `LogEvent#getThrown()`. +Its syntax and garbage-footprint are identical to the xref:#event-template-resolver-exception[] resolver. [#event-template-resolver-level] ===== `level` +Resolves the fields of the `LogEvent#getLevel()` + +.`level` event template resolver grammar [source] ---- config = field , [ severity ] @@ -846,10 +862,9 @@ severity = severity-field severity-field = "field" -> ( "keyword" | "code" ) ---- -Resolves the fields of the `logEvent.getLevel()`. - -====== Examples - +.See examples +[%collapsible] +==== Resolve the level name: [source,json] @@ -860,8 +875,7 @@ Resolve the level name: } ---- -Resolve the https://en.wikipedia.org/wiki/Syslog#Severity_levels[Syslog severity] -keyword: +Resolve the https://en.wikipedia.org/wiki/Syslog#Severity_levels[Syslog severity] keyword: [source,json] ---- @@ -874,8 +888,7 @@ keyword: } ---- -Resolve the https://en.wikipedia.org/wiki/Syslog#Severity_levels[Syslog severity] -code: +Resolve the https://en.wikipedia.org/wiki/Syslog#Severity_levels[Syslog severity] code: [source,json] ---- @@ -887,19 +900,22 @@ code: } } ---- +==== [#event-template-resolver-logger] ===== `logger` +Resolves `LogEvent#getLoggerFqcn()` and `LogEvent#getLoggerName()`. + +.`logger` event template grammar [source] ---- config = "field" -> ( "name" | "fqcn" ) ---- -Resolves `logEvent.getLoggerFqcn()` and `logEvent.getLoggerName()`. - -====== Examples - +.See examples +[%collapsible] +==== Resolve the logger name: [source,json] @@ -919,10 +935,14 @@ Resolve the logger's fully qualified class name: "field": "fqcn" } ---- +==== [#event-template-resolver-main] ===== `main` +Performs xref:manual/lookups.adoc#AppMainArgsLookup[Main Argument Lookup] for the given `index` or `key` + +.`main` event template resolver grammar [source] ---- config = ( index | key ) @@ -930,11 +950,9 @@ index = "index" -> number key = "key" -> string ---- -Performs xref:manual/lookups.adoc#AppMainArgsLookup[Main Argument Lookup] for the -given `index` or `key`. - -====== Examples - +.See examples +[%collapsible] +==== Resolve the 1st `main()` method argument: [source,json] @@ -954,25 +972,28 @@ Resolve the argument coming right after `--userId`: "key": "--userId" } ---- +==== [#event-template-resolver-map] ===== `map` -Resolves ``MapMessage``s. See link:#map-resolver-template[Map Resolver Template] -for details. +Resolves ``MapMessage``s. +See link:#map-resolver-template[] for details. [#event-template-resolver-marker] ===== `marker` +Resolves the xref:manual/markers.adoc[marker] of the event + +.`marker` event template resolver grammar [source] ---- config = "field" -> ( "name" | "parents" ) ---- -Resolves `logEvent.getMarker()`. - -====== Examples - +.See examples +[%collapsible] +==== Resolve the marker name: [source,json] @@ -992,22 +1013,25 @@ Resolve the names of the marker's parents: "field": "parents" } ---- +==== [#event-template-resolver-mdc] ===== `mdc` -Resolves Mapped Diagnostic Context (MDC), aka. Thread Context Data. See -link:#map-resolver-template[Map Resolver Template] for details. +Resolves the xref:manual/thread-context.adoc[thread context] map, aka. Mapped Diagnostic Context (MDC). +See link:#map-resolver-template[] for details. [WARNING] ==== -`log4j2.garbagefreeThreadContextMap` flag needs to be turned on to iterate -the map without allocations. +`log4j2.garbagefreeThreadContextMap` flag needs to be turned on to iterate the map without allocations. ==== [#event-template-resolver-message] ===== `message` +Resolves the xref:manual/messages.adoc[message] of the event + +.`message` event template resolver grammar [source] ---- config = [ stringified ] , [ fallbackKey ] @@ -1015,16 +1039,15 @@ stringified = "stringified" -> boolean fallbackKey = "fallbackKey" -> string ---- -Resolves `logEvent.getMessage()`. - [WARNING] ==== For simple string messages, the resolution is performed without allocations. For ``ObjectMessage``s and ``MultiformatMessage``s, it depends. ==== -====== Examples - +.See examples +[%collapsible] +==== Resolve the message into a string: [source,json] @@ -1035,9 +1058,7 @@ Resolve the message into a string: } ---- -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: +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] ---- @@ -1046,12 +1067,9 @@ will be retained: } ---- -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: +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 this problem: [source,json] ---- @@ -1061,14 +1079,16 @@ Here one can use a `fallbackKey` to work around the problem: } ---- -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. +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. +==== [#event-template-resolver-messageParameter] ===== `messageParameter` +Resolves `LogEvent#getMessage().getParameters()` + +.`messageParameter` event template resolver grammar [source] ---- config = [ stringified ] , [ index ] @@ -1076,18 +1096,15 @@ stringified = "stringified" -> boolean index = "index" -> number ---- -Resolves `logEvent.getMessage().getParameters()`. - [WARNING] ==== -Regarding garbage footprint, `stringified` flag translates to -`String.valueOf(value)`, hence mind not-`String`-typed values. Further, -`logEvent.getMessage()` is expected to implement `ParameterVisitable` interface, -which is the case if `log4j2.enableThreadlocals` property set to true. +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`. ==== -====== Examples - +.See examples +[%collapsible] +==== Resolve the message parameters into an array: [source,json] @@ -1127,21 +1144,23 @@ Resolve the string representation of the first message parameter: "stringified": true } ---- +==== [#event-template-resolver-ndc] ===== `ndc` +Resolves the xref:manual/thread-context.adoc[thread context] stack – aka. Nested Diagnostic Context (NDC), aka – `String[]` returned by `LogEvent#getContextStack()` + +.`ndc` event template resolver grammar [source] ---- config = [ pattern ] pattern = "pattern" -> string ---- -Resolves the Nested Diagnostic Context (NDC), aka. Thread Context Stack, -`String[]` returned by `logEvent.getContextStack()`. - -====== Examples - +.See examples +[%collapsible] +==== Resolve all NDC values into a list: [source,json] @@ -1160,10 +1179,14 @@ Resolve all NDC values matching with the `pattern` regex: "pattern": "user(Role|Rank):\\w+" } ---- +==== [#event-template-resolver-pattern] ===== `pattern` +Resolver delegating to xref:manual/layouts.adoc#pattern-layout[`PatternLayout`] + +.`pattern` event template resolver grammar [source] ---- config = pattern , [ stackTraceEnabled ] @@ -1171,13 +1194,18 @@ pattern = "pattern" -> string stackTraceEnabled = "stackTraceEnabled" -> boolean ---- -Resolver delegating to xref:manual/layouts.adoc#pattern-layout[`PatternLayout`]. - -The default value of `stackTraceEnabled` is inherited from the parent -`JsonTemplateLayout`. +The default value of `stackTraceEnabled` is inherited from the parent JSON Template Layout. -====== Examples +[NOTE] +==== +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 {logging-services-url}/support.html[our support channels]. +==== +.See examples +[%collapsible] +==== Resolve the string produced by `%p %c{1.} [%t] %X\{userId} %X %m%ex` pattern: [source,json] @@ -1187,10 +1215,14 @@ Resolve the string produced by `%p %c{1.} [%t] %X\{userId} %X %m%ex` pattern: "pattern": "%p %c{1.} [%t] %X{userId} %X %m%ex" } ---- +==== [#event-template-resolver-source] ===== `source` +Resolves the fields of the `StackTraceElement` returned by `LogEvent#getSource()` + +.`source` event template resolver grammar [source] ---- config = "field" -> ( @@ -1200,21 +1232,18 @@ config = "field" -> ( "lineNumber" ) ---- -Resolves the fields of the `StackTraceElement` returned by -`logEvent.getSource()`. - -Note that this resolver is toggled by -`log4j.layout.jsonTemplate.locationInfoEnabled` property. +Note that this resolver is toggled by the xref:#plugin-attr-locationInfoEnabled[] layout configuration attribute. [WARNING] ==== -Mind that capturing the source location information is an expensive operation, and is not garbage-free. -Note that xref:#event-template-resolver-logger[the `logger` resolver] can generally be used as a zero-cost substitute for `className`. +Capturing the source location information is an expensive operation, and is not garbage-free. +xref:#event-template-resolver-logger[The `logger` resolver] can generally be used as a zero-cost substitute for `className`. See xref:manual/layouts.adoc#LocationInformation[this section of the layouts page] for details. ==== -====== Examples - +.See examples +[%collapsible] +==== Resolve the line number: [source,json] @@ -1224,20 +1253,22 @@ Resolve the line number: "field": "lineNumber" } ---- +==== [#event-template-resolver-thread] ===== `thread` +Resolves `LogEvent#getThreadId()`, `LogEvent#getThreadName()`, `LogEvent#getThreadPriority()` + +.`thread` event template resolver grammar [source] ---- config = "field" -> ( "name" | "id" | "priority" ) ---- -Resolves `logEvent.getThreadId()`, `logEvent.getThreadName()`, -`logEvent.getThreadPriority()`. - -====== Examples - +.See examples +[%collapsible] +==== Resolve the thread name: [source,json] @@ -1247,10 +1278,14 @@ Resolve the thread name: "field": "name" } ---- +==== [#event-template-resolver-timestamp] ===== `timestamp` +Resolves `LogEvent#getInstant()` + +.`timestamp` event template resolver grammar [source] ---- config = [ patternConfig | epochConfig ] @@ -1275,12 +1310,11 @@ unit = "unit" -> ( rounded = "rounded" -> boolean ---- -Resolves `logEvent.getInstant()` in various forms. - -====== Examples - +.See examples +[%collapsible] +==== .`timestamp` template resolver examples -[cols="5,2m"] +[cols="3,2m"] |=== | Configuration | Output @@ -1394,18 +1428,17 @@ a| ---- | 1581082727982123456 |=== +==== [#map-resolver-template] -==== Map Resolver Template +==== Map resolver -`ReadOnlyStringMap` is Log4j's `Map` equivalent with -garbage-free accessors and heavily employed throughout the code base. It is the -data structure backing both Mapped Diagnostic Context (MDC), aka. Thread Context -Data 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. +link:../javadoc/log4j-api/org/apache/logging/log4j/util/ReadOnlyStringMap.html[`ReadOnlyStringMap`] is Log4j's `Map` equivalent with garbage-free accessors, and is heavily employed throughout the code base. +It is the data structure backing both xref:manual/thread-context.adoc[thread context] map (aka., Mapped Diagnostic Context (MDC)) and link:../javadoc/log4j-api/org/apache/logging/log4j/message/MapMessage.html[`MapMessage`] implementations. +Hence template resolvers for both of these are provided by a single backend: `ReadOnlyStringMapResolver`. +Put another way, both xref:#event-template-resolver-mdc[] and xref:#event-template-resolver-map[] resolvers support identical configuration, behaviour, and garbage footprint, which are detailed below. +.Map resolver grammar [source] ---- config = singleAccess | multiAccess @@ -1422,31 +1455,28 @@ 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. +`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 -effectively equivalent to `Pattern.compile(pattern).matcher(key).matches()` and -`Pattern.compile(pattern).matcher(key).replaceAll(replacement)` calls. +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. [WARNING] ==== -Regarding garbage footprint, `stringified` flag translates to -`String.valueOf(value)`, hence mind not-`String`-typed values. +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. +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. ==== -`"$resolver"` is left out in the following examples, since it is to be -defined by the actual resolver, e.g., `map`, `mdc`. +.See examples +[%collapsible] +==== +`"$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`: @@ -1488,8 +1518,7 @@ Resolve all fields into an object such that values are converted to string: } ---- -Resolve all fields whose keys match with the `user:(role|rank)` regex into an -object: +Resolve all fields whose keys match with the `user:(role|rank)` regex into an object: [source,json] ---- @@ -1499,8 +1528,7 @@ object: } ---- -Resolve all fields whose keys match with the `user:(role|rank)` regex into an -object after removing the `user:` prefix in the key: +Resolve all fields whose keys match with the `user:(role|rank)` regex into an object after removing the `user:` prefix in the key: [source,json] ---- @@ -1511,8 +1539,7 @@ object after removing the `user:` prefix in the key: } ---- -Merge all fields whose keys are matching with the `user:(role|rank)` regex into -the parent: +Merge all fields whose keys are matching with the `user:(role|rank)` regex into the parent: [source,json] ---- @@ -1523,8 +1550,7 @@ the parent: } ---- -After converting the corresponding field values to string, merge all fields to -parent such that keys are prefixed with `_`: +After converting the corresponding field values to string, merge all fields to parent such that keys are prefixed with `_`: [source,json] ---- @@ -1536,22 +1562,26 @@ parent such that keys are prefixed with `_`: } } ---- +==== [#stack-trace-element-templates] -=== Stack Trace Element Templates +=== Stack trace element templates -xref:#event-template-resolver-exception[] and -xref:#event-template-resolver-exceptionRootCause[] event template resolvers can -serialize an exception stack trace (i.e., `StackTraceElement[]` returned by -`Throwable#getStackTrace()`) into a JSON array. While doing so, JSON templating -infrastructure is used again. +xref:#event-template-resolver-exception[] and xref:#event-template-resolver-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: -`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-template-json` artifact: +. `elementTemplate` provided in the resolver configuration + +. xref:#plugin-attr-stackTraceElementTemplate[] layout configuration attribute +(The default is populated from the `log4j.layout.jsonTemplate.stackTraceElementTemplate` system property.) + +. xref:#plugin-attr-stackTraceElementTemplateUri[] layout configuration attribute +(The default is populated from the `log4j.layout.jsonTemplate.stackTraceElementTemplateUri` system property.) +By default, xref:#plugin-attr-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 [source,json] ---- { @@ -1574,8 +1604,19 @@ to format ``StackTraceElement``s. The default configuration (accessible by } ---- -The allowed template configuration syntax is as follows: +[#stack-trace-element-template-resolvers] +==== Stack trace element template resolvers + +Similar to xref:#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. + +[#stack-trace-element-template-resolver-stackTraceElement] +===== `stackTraceElement` + +Resolves certain fields of a `StackTraceElement` + +.Stack trace template resolver grammar [source] ---- config = "field" -> ( @@ -1587,13 +1628,53 @@ config = "field" -> ( All above accesses to `StackTraceElement` is garbage-free. +[#recycling-strategy] +== Recycling strategy + +Encoding a `LogEvent` without a load on the garbage-collector can yield significant performance benefits. +JSON Template Layout contains the `Recycler` interface to implement xref:manual/garbagefree.adoc[]. +A `Recycler` is created using a `RecyclerFactory`, which implements a particular recycling strategy. +JSON Template Layout contains the following predefined recycler factories: + +`dummy`:: +It 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`:: +It 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 a considerable number of threads, e.g., a web servlet. + +`queue`:: +It 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 encode), it starts allocating. +`queue` is a good strategy where `threadLocal` is not desirable. ++ +`queue` accepts following optional parameters: ++ +`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` +`capacity`::: of type `int`, defaults to `max(8,2*cpuCount+1)` + ++ +Some example configurations of `queue` recycling strategy are as follows: + +* `queue:supplier=org.jctools.queues.MpmcArrayQueue.new` (use `MpmcArrayQueue` from JCTools) +* `queue:capacity=10` (set the queue capacity to 10) +* `queue:supplier=java.util.concurrent.ArrayBlockingQueue.new,capacity=50` (use `ArrayBlockingQueue` with a capacity of 50) + +The default `RecyclerFactory` is `threadLocal`, if `log4j2.enable.threadlocals=true`; otherwise, `queue`. +The effective recycler factory can be configured using the xref:#plugin-attr-recyclerFactory[] plugin attribute. + +Next to predefined ones, you can introduce custom `RecyclerFactory` implementations too. +See <> for details. + [#extending] == Extending -`JsonTemplateLayout` relies on Log4j xref:manual/plugins.adoc[plugin system] to build -up the features it provides. This enables feature customization a breeze for -users. As of this moment, following features are implemented by means of -plugins: +JSON Template Layout relies on xref:manual/plugins.adoc[the Log4j plugin system] to compose the features it provides. +This enables feature customization a breeze for users. +As of this moment, following features are implemented by means of plugins: * Event template resolvers (e.g., `exception`, `message`, `level` event template resolvers) * Event template interceptors (e.g., injection of `eventTemplateAdditionalField`) @@ -1602,34 +1683,28 @@ plugins: Following sections cover these in detail. [#extending-plugins] -=== Plugin Preliminaries +=== Plugin preliminaries -Log4j plugin system is the de facto extension mechanism embraced by various -Log4j components, including `JsonTemplateLayout`. Plugins make it possible -for extensible components _receive_ feature implementations without any explicit -links in between. It is analogous to a -https://en.wikipedia.org/wiki/Dependency_injection[dependency injection] -framework, but curated for Log4j-specific needs. +Log4j plugin system is the de facto extension mechanism embraced by various Log4j components, including JSON Template Layout. +Plugins make it possible for extensible components to _receive_ feature implementations without any explicit links in between. +It is analogous to a https://en.wikipedia.org/wiki/Dependency_injection[dependency injection] framework, but curated for Log4j-specific needs. -In a nutshell, you annotate your classes with `@Plugin` and their (`static`) -creator methods with `@PluginFactory`. Last, you inform the Log4j plugin system -to discover these custom classes. This can be done either using `packages` -declared in your Log4j configuration or by various other ways described in -xref:manual/plugins.adoc[plugin system documentation]. +In a nutshell, you annotate your classes with `@Plugin` and their (`static`) creator methods with `@PluginFactory`. +Last, you inform the Log4j plugin system to discover these custom classes. +This is done using running the `org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor` annotation processor while building your project. +Refer to xref:manual/plugins.adoc[] for details. [#extending-event-resolvers] -=== Extending Event Resolvers +=== Extending event template resolvers -All available xref:#event-template-resolvers[event template resolvers] are simple -plugins employed by `JsonTemplateLayout`. To add new ones, one just needs to -create their own `EventResolver` and instruct its injection via a -`@Plugin`-annotated `EventResolverFactory` class. +All available xref:#event-template-resolvers[event template resolvers] are simple plugins employed by JSON Template Layout. +To add new ones, one just needs to create their own `EventResolver` and instruct its injection via a `@Plugin`-annotated `EventResolverFactory` class. -For demonstration purposes, below we will create a `randomNumber` event resolver. +For demonstration purposes, below we will create a `randomNumber` event template resolver. Let's start with the actual resolver: [source,java] -.Custom random number event resolver +.Custom random number event template resolver ---- package com.acme.logging.log4j.layout.template.json; @@ -1676,8 +1751,8 @@ public final class RandomNumberResolver implements EventResolver { private final double hiExcLimit; - RandomNumberResolver(final TemplateResolverConfig config) { - final List rangeArray = config.getList("range", Number.class); + RandomNumberResolver(TemplateResolverConfig config) { + List rangeArray = config.getList("range", Number.class); if (rangeArray == null) { this.loIncLimit = 0D; this.hiExcLimit = 1D; @@ -1698,22 +1773,18 @@ public final class RandomNumberResolver implements EventResolver { } @Override - public void resolve( - final LogEvent value, - final JsonWriter jsonWriter) { - final double randomNumber = - loIncLimit + (hiExcLimit - loIncLimit) * Math.random(); + public void resolve(LogEvent value, JsonWriter jsonWriter) { + double randomNumber = loIncLimit + (hiExcLimit - loIncLimit) * Math.random(); jsonWriter.writeNumber(randomNumber); } } ---- -Next create a `EventResolverFactory` class to register `RandomNumberResolver` -into the Log4j plugin system. +Next, create an `EventResolverFactory` class to admit `RandomNumberResolver` into the event resolver factory plugin registry. [source,java] -.Resolver factory class to register `RandomNumberResolver` into the Log4j plugin system +.Resolver factory class to admit `RandomNumberResolver` into the event resolver factory plugin registry ---- package com.acme.logging.log4j.layout.template.json; @@ -1731,8 +1802,7 @@ import org.apache.logging.log4j.layout.template.json.resolver.TemplateResolverFa @Plugin(name = "RandomNumberResolverFactory", category = TemplateResolverFactory.CATEGORY) public final class RandomNumberResolverFactory implements EventResolverFactory { - private static final RandomNumberResolverFactory INSTANCE = - new RandomNumberResolverFactory(); + private static final RandomNumberResolverFactory INSTANCE = new RandomNumberResolverFactory(); private RandomNumberResolverFactory() {} @@ -1747,23 +1817,25 @@ public final class RandomNumberResolverFactory implements EventResolverFactory { } @Override - public RandomNumberResolver create( - final EventResolverContext context, - final TemplateResolverConfig config) { + public RandomNumberResolver create(EventResolverContext context, TemplateResolverConfig config) { return new RandomNumberResolver(config); } } ---- -Almost complete. Last, we need to inform the Log4j plugin system to discover -these custom classes: +Done! +Let's use our custom event resolver: [source,xml] -.Log4j configuration employing custom `randomNumber` resolver +.Log4j configuration employing the custom `randomNumber` event resolver ---- - + ---- -All available event template resolvers are located in -`org.apache.logging.log4j.layout.template.json.resolver` package. It is a fairly -rich resource for inspiration while implementing new resolvers. +All available event template resolvers are located in `org.apache.logging.log4j.layout.template.json.resolver` package. +It is a fairly rich resource for inspiration while implementing new resolvers. [#extending-template-resolver] -=== Intercepting the Template Resolver Compiler +=== Intercepting the template resolver compiler -`JsonTemplateLayout` allows interception of the template resolver compilation, -which is the process converting a template into a Java function performing the -JSON serialization. This interception mechanism is internally used to implement -`eventTemplateRootObjectKey` and `eventTemplateAdditionalField` features. In a -nutshell, one needs to create a `@Plugin`-annotated class extending from -`EventResolverInterceptor` interface. +JSON Template Layout allows interception of the template resolver compilation, which is the process converting a template into a Java function performing the JSON encoding. +This interception mechanism is internally used to implement xref:#plugin-attr-eventTemplateRootObjectKey[] and xref:#layout-element-EventTemplateAdditionalField[] features. +In a nutshell, one needs to create a `@Plugin`-annotated class extending from the `EventResolverInterceptor` interface. -To see the interception in action, check out the `EventRootObjectKeyInterceptor` -class which is responsible for implementing the `eventTemplateRootObjectKey` -feature: +To see the interception in action, check out the `EventRootObjectKeyInterceptor` class which is responsible for implementing the `eventTemplateRootObjectKey` feature: [source,java] .Event interceptor to add `eventTemplateRootObjectKey`, if present @@ -1817,9 +1883,7 @@ public class EventRootObjectKeyInterceptor implements EventResolverInterceptor { } @Override - public Object processTemplateBeforeResolverInjection( - final EventResolverContext context, - final Object node) { + public Object processTemplateBeforeResolverInjection(EventResolverContext context, Object node) { String eventTemplateRootObjectKey = context.getEventTemplateRootObjectKey(); return eventTemplateRootObjectKey != null ? Collections.singletonMap(eventTemplateRootObjectKey, node) @@ -1829,18 +1893,15 @@ public class EventRootObjectKeyInterceptor implements EventResolverInterceptor { } ---- -Here, `processTemplateBeforeResolverInjection()` method checks if the user has -provided an `eventTemplateRootObjectKey`. If so, it wraps the root `node` with a -new object; otherwise, returns the `node` as is. Note that `node` refers to the -root Java object of the event template read by `JsonReader`. +Here, `processTemplateBeforeResolverInjection()` method checks if the user has provided an `eventTemplateRootObjectKey`. +If so, it wraps the root `node` with a new object; otherwise, returns the `node` as is. +Note that `node` refers to the root Java object of the event template read by `JsonReader`. [#extending-recycler] -=== Extending Recycler Factories +=== Extending recycler factories -`recyclerFactory` input `String` read from the layout configuration is converted -to a `RecyclerFactory` using the default `RecyclerFactoryConverter` extending -from `TypeConverter`. If one wants to change this behavior, -they simply need to add their own `TypeConverter` implementing +xref:#plugin-attr-recyclerFactory[] layout configuration attribute is converted from `String` to a `RecyclerFactory` using the default `RecyclerFactoryConverter` extending from `TypeConverter`. +If one wants to change this behavior, they simply need to add their own `TypeConverter` implementing `Comparable>` to prioritize their custom converter. [source,java] @@ -1852,13 +1913,12 @@ import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.config.plugins.convert.TypeConverter; import org.apache.logging.log4j.core.config.plugins.convert.TypeConverters; -@Plugin(name = "AcmeRecyclerFactoryConverter", category = TypeConverters.CATEGORY) -public final class AcmeRecyclerFactoryConverter - implements TypeConverter, Comparable> { +@Plugin(name = "ExampleRecyclerFactoryConverter", category = TypeConverters.CATEGORY) +public final class ExampleRecyclerFactoryConverter implements TypeConverter, Comparable> { @Override - public RecyclerFactory convert(final String recyclerFactorySpec) { - return AcmeRecyclerFactory.ofSpec(recyclerFactorySpec); + public RecyclerFactory convert(String recyclerFactorySpec) { + return ExampleRecyclerFactory.ofSpec(recyclerFactorySpec); } @Override @@ -1869,14 +1929,12 @@ public final class AcmeRecyclerFactoryConverter } ---- -Here note that `compareTo()` always returns -1 to rank it higher compared to -other matching converters. +Here note that `compareTo()` always returns -1 to rank it higher compared to other matching converters. [#features] == Features -Below is a feature comparison matrix between `JsonTemplateLayout` and -alternatives. +Below is a feature comparison matrix between JSON Template Layout and alternatives. .Feature comparison matrix [cols="3,1,1,1,1"] @@ -1983,10 +2041,10 @@ To get the most out of it, mind the following checklist: [#faq-lookups] === Are lookups supported in templates? -Yes, xref:manual/lookups.adoc[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. +Yes, xref:manual/lookups.adoc[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-recursive-collection] === Are recursive collections supported? No. Consider a `Message` containing a recursive value as follows: @@ -1997,29 +2055,22 @@ Object[] recursiveCollection = new Object[1]; recursiveCollection[0] = recursiveCollection; ---- -While the exact exception might vary, you will most like get a -`StackOverflowError` for trying to render `recursiveCollection` into a -`String`. Note that this is also the default behaviour for other Java standard -library methods, e.g., `Arrays.toString()`. Hence mind self references while -logging. +While the exact exception might vary, you will most like get a `StackOverflowError` for trying to render `recursiveCollection` into a `String`. +Note that this is also the default behaviour for other Java standard library methods, e.g., `Arrays.toString()`. +Hence mind self references while logging. [#faq-garbage-free] -=== Is `JsonTemplateLayout` garbage-free? +=== Is JSON Template Layout garbage-free? Yes, if xref:manual/garbagefree.adoc[garbage-free logging] is enabled. Take into account the following caveats: -* The configured link:#recycling-strategy[recycling strategy] might not be - garbage-free. +* 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. +* 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, except `List`). +* Serialization of ``MapMessage``s and ``ObjectMessage``s are mostly garbage-free except for certain types (e.g., `BigDecimal`, `BigInteger`, ``Collection``s, except `List`). * xref:manual/lookups.adoc[Lookups] (that is, `${...}` variables) are not garbage-free. -Don't forget to check out xref:#event-template-resolvers[the notes on garbage footprint of resolvers] -you employ in templates. +Don't forget to check out xref:#event-template-resolvers[the notes on garbage footprint of resolvers] you employ in templates. diff --git a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc index 5b514265d6e..209341268db 100644 --- a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc @@ -422,112 +422,80 @@ include::partial$manual/dependencies-log4j-layout-json.adoc[] [#JSONTemplateLayout] == JSON Template Layout -`JsonTemplateLayout` is a customizable, efficient, and garbage-free JSON-emitting layout. +JSON Template Layout is a customizable, efficient, and garbage-free JSON generating layout. It encodes ``LogEvent``s according to the structure described by the JSON template provided. -For instance, given the following JSON template modelling https://github.com/logstash/log4j-jsonevent-layout[the official -Logstash `JSONEventLayoutV1`] +For instance, given the following event template stored in `MyLayout.json` in your classpath: [source,json] ---- { - "mdc": { - "$resolver": "mdc" - }, - "exception": { - "exception_class": { - "$resolver": "exception", - "field": "className" - }, - "exception_message": { - "$resolver": "exception", - "field": "message" - }, - "stacktrace": { - "$resolver": "exception", - "field": "stackTrace", - "stackTrace": { - "stringified": true - } + "instant": { // <1> + "$resolver": "timestamp", + "pattern": { + "format": "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", + "timeZone": "UTC" } }, - "line_number": { - "$resolver": "source", - "field": "lineNumber" - }, - "class": { - "$resolver": "source", - "field": "className" - }, - "@version": 1, - "source_host": "${hostName}", - "message": { + "someConstant": 1, // <2> + "message": { // <3> "$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" } } ---- +<1> Using the `timestamp` event template resolver to populate the `instant` field +<2> Passing a constant that will be rendered as is +<3> Using the `message` event template resolver to populate the `message` field -in combination with the below Log4j configuration: +in combination with the below layout configuration: +[tabs] +==== +XML:: ++ +.Snippet from an example {antora-examples-url}/manual/json-template-layout/usage/log4j2.xml[`log4j2.xml`] [source,xml] ---- - +include::example$manual/json-template-layout/usage/log4j2.xml[lines=26..26,indent=0] ---- -JSON Template Layout will render JSON documents as follows: +JSON:: ++ +.Snippet from an example {antora-examples-url}/manual/json-template-layout/usage/log4j2.json[`log4j2.json`] +[source,json] +---- +include::example$manual/json-template-layout/usage/log4j2.json[lines=6..8,indent=0] +---- + +YAML:: ++ +.Snippet from an example {antora-examples-url}/manual/json-template-layout/usage/log4j2.yaml[`log4j2.yaml`] +[source,xml] +---- +include::example$manual/json-template-layout/usage/log4j2.yaml[lines=22..23,indent=0] +---- + +Properties:: ++ +.Snippet from an example {antora-examples-url}/manual/json-template-layout/usage/log4j2.properties[`log4j2.properties`] +[source,xml] +---- +include::example$manual/json-template-layout/usage/log4j2.properties[lines=19..20,indent=0] +---- +==== + +JSON Template Layout generates JSON 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" -} +{"instant":"2017-05-25T19:56:23.370Z","someConstant":1,"message":"Hello, error!"} //<1> ---- +<1> JSON pretty-printing is not supported for performance reasons. -.Additional runtime dependencies are required for using JSON Template Layout -[%collapsible,id=log4j-layout-template-json-deps] -===== -include::partial$manual/dependencies-log4j-layout-template-json.adoc[] -===== +Good news is JSON Template Layout is perfectly production-ready without any configuration! +It bundles several predefined event templates modeling popular JSON-based log formats. -See xref:manual/json-template-layout.adoc[JSON Template Layout] page for the complete documentation. +Read more on xref:manual/json-template-layout.adoc[]... [id=pattern-layout] == [[PatternLayout]] Pattern Layout From 439d20641fdfb733c435117ee1175e8c61c1b7b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Sun, 2 Jun 2024 22:23:01 +0200 Subject: [PATCH 02/53] Revamp JTL page --- .../modules/ROOT/pages/manual/json-template-layout.adoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc b/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc index 97096be97b8..3327645cb9a 100644 --- a/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc @@ -364,12 +364,12 @@ Suffix to append to strings truncated due to exceeding xref:#plugin-attr-maxStri Name of the xref:#recycling-strategy[] employed -[#layout-elements] +[#plugin-elements] === Plugin elements JSON Template Layout plugin configuration accepts the following elements: -[#layout-element-EventTemplateAdditionalField] +[#plugin-element-EventTemplateAdditionalField] ==== [[additional-event-template-fields]] `EventTemplateAdditionalField` Additional event template fields are convenient shortcuts to add custom fields to a template or override existing ones. @@ -442,7 +442,7 @@ Templates are configured by means of the following JSON Template Layout plugin a - xref:#plugin-attr-eventTemplate[] and xref:#plugin-attr-eventTemplateUri[] (for encoding ``LogEvent``s) - xref:#plugin-attr-stackTraceElementTemplate[] and xref:#plugin-attr-stackTraceElementTemplateUri[] (for encoding ``StackStraceElement``s) -- xref:#layout-element-EventTemplateAdditionalField[] (for extending the event template) +- xref:#plugin-element-EventTemplateAdditionalField[] (for extending the event template) [#event-templates] === Event templates @@ -1854,7 +1854,7 @@ It is a fairly rich resource for inspiration while implementing new resolvers. === Intercepting the template resolver compiler JSON Template Layout allows interception of the template resolver compilation, which is the process converting a template into a Java function performing the JSON encoding. -This interception mechanism is internally used to implement xref:#plugin-attr-eventTemplateRootObjectKey[] and xref:#layout-element-EventTemplateAdditionalField[] features. +This interception mechanism is internally used to implement xref:#plugin-attr-eventTemplateRootObjectKey[] and xref:#plugin-element-EventTemplateAdditionalField[] features. In a nutshell, one needs to create a `@Plugin`-annotated class extending from the `EventResolverInterceptor` interface. To see the interception in action, check out the `EventRootObjectKeyInterceptor` class which is responsible for implementing the `eventTemplateRootObjectKey` feature: From 454c42177a0fabc5be315f12cbd45ed1a772358b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Tue, 4 Jun 2024 10:37:03 +0200 Subject: [PATCH 03/53] Move Pattern Layout to a separate page --- src/site/antora/antora.tmpl.yml | 4 +- src/site/antora/antora.yml | 4 +- .../marker-pattern-selector/log4j2.json | 28 + .../marker-pattern-selector/log4j2.properties | 27 + .../marker-pattern-selector/log4j2.xml | 40 + .../marker-pattern-selector/log4j2.yaml | 33 + .../script-pattern-selector/log4j2.json | 37 + .../script-pattern-selector/log4j2.properties | 41 + .../script-pattern-selector/log4j2.xml | 50 + .../script-pattern-selector/log4j2.yaml | 46 + .../manual/pattern-layout/usage/log4j2.json | 20 + .../pattern-layout/usage/log4j2.properties | 23 + .../manual/pattern-layout/usage/log4j2.xml | 36 + .../manual/pattern-layout/usage/log4j2.yaml | 29 + src/site/antora/modules/ROOT/nav.adoc | 1 + .../ROOT/pages/manual/configuration.adoc | 20 +- .../modules/ROOT/pages/manual/extending.adoc | 46 - .../ROOT/pages/manual/garbagefree.adoc | 2 +- .../ROOT/pages/manual/getting-started.adoc | 2 +- .../ROOT/pages/manual/installation.adoc | 3 +- .../pages/manual/json-template-layout.adoc | 36 +- .../modules/ROOT/pages/manual/layouts.adoc | 1476 +------------- .../ROOT/pages/manual/pattern-layout.adoc | 1705 +++++++++++++++++ .../ROOT/pages/manual/performance.adoc | 2 +- .../manual/plugin-extension-intro.adoc | 39 + src/site/resources/.htaccess | 39 + 26 files changed, 2265 insertions(+), 1524 deletions(-) create mode 100644 src/site/antora/modules/ROOT/examples/manual/pattern-layout/marker-pattern-selector/log4j2.json create mode 100644 src/site/antora/modules/ROOT/examples/manual/pattern-layout/marker-pattern-selector/log4j2.properties create mode 100644 src/site/antora/modules/ROOT/examples/manual/pattern-layout/marker-pattern-selector/log4j2.xml create mode 100644 src/site/antora/modules/ROOT/examples/manual/pattern-layout/marker-pattern-selector/log4j2.yaml create mode 100644 src/site/antora/modules/ROOT/examples/manual/pattern-layout/script-pattern-selector/log4j2.json create mode 100644 src/site/antora/modules/ROOT/examples/manual/pattern-layout/script-pattern-selector/log4j2.properties create mode 100644 src/site/antora/modules/ROOT/examples/manual/pattern-layout/script-pattern-selector/log4j2.xml create mode 100644 src/site/antora/modules/ROOT/examples/manual/pattern-layout/script-pattern-selector/log4j2.yaml create mode 100644 src/site/antora/modules/ROOT/examples/manual/pattern-layout/usage/log4j2.json create mode 100644 src/site/antora/modules/ROOT/examples/manual/pattern-layout/usage/log4j2.properties create mode 100644 src/site/antora/modules/ROOT/examples/manual/pattern-layout/usage/log4j2.xml create mode 100644 src/site/antora/modules/ROOT/examples/manual/pattern-layout/usage/log4j2.yaml create mode 100644 src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc create mode 100644 src/site/antora/modules/ROOT/partials/manual/plugin-extension-intro.adoc diff --git a/src/site/antora/antora.tmpl.yml b/src/site/antora/antora.tmpl.yml index 32b48b1ac66..6be7c65d8ca 100644 --- a/src/site/antora/antora.tmpl.yml +++ b/src/site/antora/antora.tmpl.yml @@ -39,8 +39,8 @@ start_page: index.adoc asciidoc: attributes: # Commons - antora-examples-url: "https://raw.githubusercontent.com/apache/logging-log4j2/2.x/src/site/antora/modules/ROOT/examples" - project-github-url: "${scm.url}" + antora-examples-url: "${scm.url}/tree/${scm.tag}/src/site/antora/modules/ROOT/examples" + project-github-url: "${scm.url}/tree/${scm.tag}" project-name: "Log4j" project-id: "log4j" java-target-version: "${maven.compiler.target}" diff --git a/src/site/antora/antora.yml b/src/site/antora/antora.yml index f1ce58368ea..0feb6d46f3c 100644 --- a/src/site/antora/antora.yml +++ b/src/site/antora/antora.yml @@ -39,8 +39,8 @@ start_page: index.adoc asciidoc: attributes: # Commons - antora-examples-url: "https://raw.githubusercontent.com/apache/logging-log4j2/2.x/src/site/antora/modules/ROOT/examples" - project-github-url: "https://github.com/apache/logging-log4j2/" + antora-examples-url: "https://github.com/apache/logging-log4j2/tree/2.x/src/site/antora/modules/ROOT/examples" + project-github-url: "https://github.com/apache/logging-log4j2/tree/2.x" project-name: "Log4j" project-id: "log4j" java-target-version: "8" diff --git a/src/site/antora/modules/ROOT/examples/manual/pattern-layout/marker-pattern-selector/log4j2.json b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/marker-pattern-selector/log4j2.json new file mode 100644 index 00000000000..901d8ea1247 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/marker-pattern-selector/log4j2.json @@ -0,0 +1,28 @@ +{ + "Configuration": { + "Appenders": { + "Console": { + "name": "CONSOLE", + "PatternLayout": { + "MarkerPatternSelector": { + "defaultPattern": "[%-5level] %c{1.} %msg%n", + "PatternMatch": [ + { + "key": "FLOW", + "pattern": "[%-5level] %c{1.} ====== %C{1.}.%M:%L %msg ======%n" + } + ] + } + } + } + }, + "Loggers": { + "Root": { + "level": "WARN", + "AppenderRef": { + "ref": "CONSOLE" + } + } + } + } +} diff --git a/src/site/antora/modules/ROOT/examples/manual/pattern-layout/marker-pattern-selector/log4j2.properties b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/marker-pattern-selector/log4j2.properties new file mode 100644 index 00000000000..06d4d144258 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/marker-pattern-selector/log4j2.properties @@ -0,0 +1,27 @@ +# +# 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. +# +appender.0.type = Console +appender.0.name = CONSOLE +appender.0.layout.type = PatternLayout +appender.0.layout.patternSelector.type = MarkerPatternSelector +appender.0.layout.patternSelector.defaultPattern = %-5p [%t]: %m%n +appender.0.layout.patternSelector.patternMatch.0.type = PatternMatch +appender.0.layout.patternSelector.patternMatch.0.key = FLOW +appender.0.layout.patternSelector.patternMatch.0.pattern = [%-5level] %c{1.} ====== %C{1.}.%M:%L %msg ======%n + +rootLogger.level = WARN +rootLogger.appenderRef.0.ref = CONSOLE diff --git a/src/site/antora/modules/ROOT/examples/manual/pattern-layout/marker-pattern-selector/log4j2.xml b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/marker-pattern-selector/log4j2.xml new file mode 100644 index 00000000000..3b376487544 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/marker-pattern-selector/log4j2.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/site/antora/modules/ROOT/examples/manual/pattern-layout/marker-pattern-selector/log4j2.yaml b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/marker-pattern-selector/log4j2.yaml new file mode 100644 index 00000000000..9574ae89f60 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/marker-pattern-selector/log4j2.yaml @@ -0,0 +1,33 @@ +# +# 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. +# +Configuration: + + Appenders: + Console: + name: "CONSOLE" + PatternLayout: + MarkerPatternSelector: + defaultPattern: "%-5p [%t]: %m%n" + PatternMatch: + - key: "FLOW" + pattern: "[%-5level] %c{1.} ====== %C{1.}.%M:%L %msg ======%n" + + Loggers: + Root: + level: "WARN" + AppenderRef: + ref: "CONSOLE" diff --git a/src/site/antora/modules/ROOT/examples/manual/pattern-layout/script-pattern-selector/log4j2.json b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/script-pattern-selector/log4j2.json new file mode 100644 index 00000000000..3bb5ddb9ef1 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/script-pattern-selector/log4j2.json @@ -0,0 +1,37 @@ +{ + "Configuration": { + "Appenders": { + "Console": { + "name": "CONSOLE", + "PatternLayout": { + "ScriptPatternSelector": { + "defaultPattern": "[%-5level] %c{1.} %msg%n", + "Script": { + "name": "BeanShellSelector", + "language": "bsh", + "scriptText": "if (logEvent.getLoggerName().equals(\"NoLocation\")) { return \"NoLocation\"; } else if (logEvent.getMarker() != null && logEvent.getMarker().isInstanceOf(\"FLOW\")) { return \"Flow\"; } else { return null; }" + }, + "PatternMatch": [ + { + "key": "NoLocation", + "pattern": "[%-5level] %c{1.} %msg%n" + }, + { + "key": "Flow", + "pattern": "[%-5level] %c{1.} ====== %C{1.}.%M:%L %msg ======%n" + } + ] + } + } + } + }, + "Loggers": { + "Root": { + "level": "WARN", + "AppenderRef": { + "ref": "CONSOLE" + } + } + } + } +} diff --git a/src/site/antora/modules/ROOT/examples/manual/pattern-layout/script-pattern-selector/log4j2.properties b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/script-pattern-selector/log4j2.properties new file mode 100644 index 00000000000..ec7b7407ccf --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/script-pattern-selector/log4j2.properties @@ -0,0 +1,41 @@ +# +# 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. +# +appender.0.type = Console +appender.0.name = CONSOLE +appender.0.layout.type = PatternLayout +appender.0.layout.patternSelector.type = ScriptPatternSelector +appender.0.layout.patternSelector.defaultPattern = [%-5level] %c{1.} %C{1.}.%M.%L %msg%n +appender.0.layout.patternSelector.script.type = Script +appender.0.layout.patternSelector.script.name = BeanShellSelector +appender.0.layout.patternSelector.script.language = bsh +appender.0.layout.patternSelector.script.scriptText =\ +if (logEvent.getLoggerName().equals("NoLocation")) {\ + return "NoLocation";\ +} else if (logEvent.getMarker() != null && logEvent.getMarker().isInstanceOf("FLOW")) {\ + return "Flow";\ +} else {\ + return null;\ +} +appender.0.layout.patternSelector.patternMatch.0.type = PatternMatch +appender.0.layout.patternSelector.patternMatch.0.key = NoLocation +appender.0.layout.patternSelector.patternMatch.0.pattern = [%-5level] %c{1.} %msg%n +appender.0.layout.patternSelector.patternMatch.1.type = PatternMatch +appender.0.layout.patternSelector.patternMatch.1.key = Flow +appender.0.layout.patternSelector.patternMatch.1.pattern = [%-5level] %c{1.} ====== %C{1.}.%M:%L %msg ======%n + +rootLogger.level = WARN +rootLogger.appenderRef.0.ref = CONSOLE diff --git a/src/site/antora/modules/ROOT/examples/manual/pattern-layout/script-pattern-selector/log4j2.xml b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/script-pattern-selector/log4j2.xml new file mode 100644 index 00000000000..816b1ad03ae --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/script-pattern-selector/log4j2.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/site/antora/modules/ROOT/examples/manual/pattern-layout/script-pattern-selector/log4j2.yaml b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/script-pattern-selector/log4j2.yaml new file mode 100644 index 00000000000..02ad8df0b8c --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/script-pattern-selector/log4j2.yaml @@ -0,0 +1,46 @@ +# +# 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. +# +Configuration: + + Appenders: + Console: + name: "CONSOLE" + PatternLayout: + ScriptPatternSelector: + defaultPattern: "%-5p [%t]: %m%n" + Script: + name: "BeanShellSelector" + language: "bsh" + scriptText: | + if (logEvent.getLoggerName().equals("NoLocation")) { + return "NoLocation"; + } else if (logEvent.getMarker() != null && logEvent.getMarker().isInstanceOf("FLOW")) { + return "Flow"; + } else { + return null; + } + PatternMatch: + - key: "NoLocation" + pattern: "[%-5level] %c{1.} %msg%n" + - key: "Flow" + pattern: "[%-5level] %c{1.} ====== %C{1.}.%M:%L %msg ======%n" + + Loggers: + Root: + level: "WARN" + AppenderRef: + ref: "CONSOLE" diff --git a/src/site/antora/modules/ROOT/examples/manual/pattern-layout/usage/log4j2.json b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/usage/log4j2.json new file mode 100644 index 00000000000..f7f90b42995 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/usage/log4j2.json @@ -0,0 +1,20 @@ +{ + "Configuration": { + "Appenders": { + "Console": { + "name": "CONSOLE", + "PatternLayout": { + "pattern": "%-5p [%t]: %m%n" + } + } + }, + "Loggers": { + "Root": { + "level": "WARN", + "AppenderRef": { + "ref": "CONSOLE" + } + } + } + } +} diff --git a/src/site/antora/modules/ROOT/examples/manual/pattern-layout/usage/log4j2.properties b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/usage/log4j2.properties new file mode 100644 index 00000000000..561dce7202f --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/usage/log4j2.properties @@ -0,0 +1,23 @@ +# +# 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. +# +appender.0.type = Console +appender.0.name = CONSOLE +appender.0.layout.type = PatternLayout +appender.0.layout.pattern = %-5p [%t]: %m%n + +rootLogger.level = WARN +rootLogger.appenderRef.0.ref = CONSOLE diff --git a/src/site/antora/modules/ROOT/examples/manual/pattern-layout/usage/log4j2.xml b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/usage/log4j2.xml new file mode 100644 index 00000000000..0043667de28 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/usage/log4j2.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + diff --git a/src/site/antora/modules/ROOT/examples/manual/pattern-layout/usage/log4j2.yaml b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/usage/log4j2.yaml new file mode 100644 index 00000000000..3d226e0ba77 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/pattern-layout/usage/log4j2.yaml @@ -0,0 +1,29 @@ +# +# 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. +# +Configuration: + + Appenders: + Console: + name: "CONSOLE" + PatternLayout: + pattern: "%-5p [%t]: %m%n" + + Loggers: + Root: + level: "WARN" + AppenderRef: + ref: "CONSOLE" diff --git a/src/site/antora/modules/ROOT/nav.adoc b/src/site/antora/modules/ROOT/nav.adoc index 1c0af5b572d..a3221fef6a4 100644 --- a/src/site/antora/modules/ROOT/nav.adoc +++ b/src/site/antora/modules/ROOT/nav.adoc @@ -45,6 +45,7 @@ ** xref:manual/appenders.adoc[] ** xref:manual/layouts.adoc[] *** xref:manual/json-template-layout.adoc[] +*** xref:manual/pattern-layout.adoc[] ** xref:manual/lookups.adoc[] ** xref:manual/filters.adoc[] ** xref:manual/scripts.adoc[] diff --git a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc index 3651893ca89..ab744fc7203 100644 --- a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc @@ -257,29 +257,19 @@ See also <> for exceptions to the rules above. Log4j Core's logging pipeline is quite complex (see xref:manual/architecture.adoc[Architecture]), but most users only require these elements: Loggers:: -+ -Loggers are the entry point of the logging pipeline, which is directly used in the code. +xref:manual/api.adoc#loggers[Loggers] are the entry point of the logging pipeline, which is directly used in the code. Their configuration must specify which level of messages they log and to which appenders they send the messages. -+ -See <> for details. +We will cover them while <>. [id=configuring-appenders] [[Appenders]] Appenders:: -+ -Appenders are the exit point of the logging pipeline. +xref:manual/appenders.adoc[] are the exit point of the logging pipeline. They decide which resource (console, file, database, or similar) the log event is sent to. In the examples of this chapter, we will only use the xref:manual/appenders.adoc#consoleappender[console appender] and the xref:manual/appenders.adoc#fileappender[file appender]. -+ -See xref:manual/appenders.adoc[Appender configuration] for details. Layouts:: -+ -Layouts tell appenders how to format the log event: text, JSON, XML, or similar. -In the examples of this chapter, we will only use the xref:manual/layouts.adoc#pattern-layout[textual pattern layout] -and -xref:manual/json-template-layout.adoc[JSON template layout]. -+ -See xref:manual/layouts.adoc[Layout configuration] for details. +xref:manual/layouts.adoc[] tell appenders how to format the log event: text, JSON, XML, or similar. +In the examples of this chapter, we will only use xref:manual/pattern-layout.adoc[] and xref:manual/json-template-layout.adoc[]. A moderately complex configuration might look like this: diff --git a/src/site/antora/modules/ROOT/pages/manual/extending.adoc b/src/site/antora/modules/ROOT/pages/manual/extending.adoc index 554ec169487..f36ad675319 100644 --- a/src/site/antora/modules/ROOT/pages/manual/extending.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/extending.adoc @@ -417,52 +417,6 @@ public class SampleLayout extends AbstractStringLayout { } ---- -[#PatternConverters] -== PatternConverters - -PatternConverters are used by the PatternLayout to format the log event -into a printable `String`. Each Converter is responsible for a single kind -of manipulation, however Converters are free to format the event in -complex ways. For example, there are several converters that manipulate -Throwables and format them in various ways. - -A PatternConverter must first declare itself as a Plugin using the -standard `@Plugin` annotation and the `@Namespace` annotation with the value "Converter". Furthermore, the Converter must also specify the -`@ConverterKeys` annotation to define the tokens that can be specified in -the pattern (preceded by a '%' character) to identify the Converter. - -Unlike most other Plugins, Converters do not use a `@PluginFactory`. -Instead, each Converter is required to provide a static `newInstance` -method that accepts an array of `String` as the only parameter. The -`String[]` is the values that are specified within the curly braces -that can follow the converter key. - -The following shows the skeleton of a Converter plugin. - -[source,java] ----- -@Plugin("query") -@ConverterKeys({"q", "query"}) -public final class QueryConverter extends LogEventPatternConverter { - - public QueryConverter(String[] options) { - } - - public static QueryConverter newInstance(final String[] options) { - return new QueryConverter(options); - } - - @Override - public void format(LogEvent event, StringBuilder toAppendTo) { - // get the data from 'event', to the work and append the result to 'toAppendTo'. - } -} ----- - -A pattern to use this converter could be specified as `... %q ...` or `... %q\{argument} ...`. -The "argument" will be passed as first (and only) value to the `options` parameter of the -`newInstance(...)` method. - [#Plugin_Builders] == Plugin Builders diff --git a/src/site/antora/modules/ROOT/pages/manual/garbagefree.adoc b/src/site/antora/modules/ROOT/pages/manual/garbagefree.adoc index d06ea276c4a..e776c37267b 100644 --- a/src/site/antora/modules/ROOT/pages/manual/garbagefree.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/garbagefree.adoc @@ -80,7 +80,7 @@ To understand which configuration knobs exhibit what kind of allocation behaviou // Maintain the alphabetical ordering while making changes, please! * xref:manual/layouts.adoc#GELFLayout[`GelfLayout`] * xref:manual/json-template-layout.adoc#faq-garbage-free[`JsonTemplateLayout`] -* xref:manual/layouts.adoc#PatternLayout-gcfree[`PatternLayout`] +* xref:manual/pattern-layout.adoc#garbage-free[`PatternLayout`] .Implementation notes [%collapsible] diff --git a/src/site/antora/modules/ROOT/pages/manual/getting-started.adoc b/src/site/antora/modules/ROOT/pages/manual/getting-started.adoc index 7735ffce892..4e63d2bbe31 100644 --- a/src/site/antora/modules/ROOT/pages/manual/getting-started.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/getting-started.adoc @@ -437,7 +437,7 @@ Contrast to xref:#config-app[an application's more conservative Log4j setup], fo . the logs are pretty-printed to the console, and . logging verbosity is increased. -While it is not recommended to use xref:manual/layouts.adoc#pattern-layout[Pattern Layout] in production for security reasons, it is a good choice for tests to encode log events. +While it is not recommended to use xref:manual/pattern-layout.adoc[] in production for security reasons, it is a good choice for tests to encode log events. We will use it to pretty-print the log event to the console with extra fields: timestamp, thread name, log level, class name, etc. The rest of the configuration should look familiar from earlier sections. diff --git a/src/site/antora/modules/ROOT/pages/manual/installation.adoc b/src/site/antora/modules/ROOT/pages/manual/installation.adoc index 97881b7e8a8..42edc9f2727 100644 --- a/src/site/antora/modules/ROOT/pages/manual/installation.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/installation.adoc @@ -542,8 +542,7 @@ rootLogger.appenderRef.0.ref = CONSOLE ---- ==== -<1> While xref:manual/layouts.adoc#pattern-layout[Pattern Layout] is a good first choice and preferable for tests, we recommend -using a structured format such as xref:manual/json-template-layout.adoc[] for production deployments. +<1> While xref:manual/pattern-layout.adoc[] is a good first choice and preferable for tests, we recommend using a structured format such as xref:manual/json-template-layout.adoc[] for production deployments. To use these formats, the following additional dependencies are required: diff --git a/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc b/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc index 3327645cb9a..d1ad77494a1 100644 --- a/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc @@ -16,7 +16,7 @@ //// = JSON Template Layout -JSON Template Layout is a customizable, efficient, and garbage-free JSON generating layout. +JSON Template Layout is a customizable, xref:#performance[efficient], and xref:#faq-garbage-free[garbage-free] JSON generating layout. It encodes ``LogEvent``s according to the structure described by the JSON template provided. In a nutshell, it shines with its @@ -30,6 +30,12 @@ In a nutshell, it shines with its * Customizable object xref:#recycling-strategy[recycling strategy] +[TIP] +==== +JSON Template Layout is intended for production deployments where the generated logs are expected to be delivered to an external log ingestion system such as Elasticsearch or Google Cloud Logging. +While running tests or developing locally, you can use xref:manual/pattern-layout.adoc[] for human-readable log output. +==== + [#usage] == Usage @@ -164,8 +170,8 @@ Good news is *JSON Template Layout is perfectly production-ready without any con The event template defaults to `EcsLayout.json`, bundled in the classpath, modelling https://www.elastic.co/guide/en/ecs/current/ecs-reference.html[the Elastic Common Schema (ECS) specification]. JSON Template Layout bundles several more xref:#event-templates[predefined event templates] modeling popular JSON-based log formats. -[#layout-config] -== Layout configuration +[#config] +== Configuration This section explains how to configure JSON Template Layout plugin element in a Log4j configuration file. @@ -1184,7 +1190,7 @@ Resolve all NDC values matching with the `pattern` regex: [#event-template-resolver-pattern] ===== `pattern` -Resolver delegating to xref:manual/layouts.adoc#pattern-layout[`PatternLayout`] +Resolver delegating to xref:manual/pattern-layout.adoc[] .`pattern` event template resolver grammar [source] @@ -1669,30 +1675,14 @@ The effective recycler factory can be configured using the xref:#plugin-attr-rec Next to predefined ones, you can introduce custom `RecyclerFactory` implementations too. See <> for details. -[#extending] -== Extending - -JSON Template Layout relies on xref:manual/plugins.adoc[the Log4j plugin system] to compose the features it provides. -This enables feature customization a breeze for users. +:plugin-extension-intro-component-name: JSON Template Layout +include::partial$manual/plugin-extension-intro.adoc[tag=part1] As of this moment, following features are implemented by means of plugins: * Event template resolvers (e.g., `exception`, `message`, `level` event template resolvers) * Event template interceptors (e.g., injection of `eventTemplateAdditionalField`) * Recycler factories - -Following sections cover these in detail. - -[#extending-plugins] -=== Plugin preliminaries - -Log4j plugin system is the de facto extension mechanism embraced by various Log4j components, including JSON Template Layout. -Plugins make it possible for extensible components to _receive_ feature implementations without any explicit links in between. -It is analogous to a https://en.wikipedia.org/wiki/Dependency_injection[dependency injection] framework, but curated for Log4j-specific needs. - -In a nutshell, you annotate your classes with `@Plugin` and their (`static`) creator methods with `@PluginFactory`. -Last, you inform the Log4j plugin system to discover these custom classes. -This is done using running the `org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor` annotation processor while building your project. -Refer to xref:manual/plugins.adoc[] for details. +include::partial$manual/plugin-extension-intro.adoc[tag=part2] [#extending-event-resolvers] === Extending event template resolvers diff --git a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc index 209341268db..66be951a8be 100644 --- a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc @@ -207,9 +207,22 @@ Configure as follows to send to a Graylog 2.x server with TCP: | mapMessageExcludes | String | A comma separated list of attributes from the MapMessage to exclude when formatting the event. This attribute only applies when includeMapMessage="true" is specified. If mapMessageIncludes are also specified this attribute will be ignored. | mapMessageIncludes | String | A comma separated list of attributes from the MapMessage to include when formatting the event. This attribute only applies when includeMapMessage="true" is specified. If mapMessageExcludes are also specified this attribute will override them. MapMessage fields specified here that have no value will be omitted. | mapPrefix | String | A String to prepend to all elements of the MapMessage when rendered as a field. Defaults to an empty String. -| messagePattern | String | The pattern to use to format the String. A messagePattern and patternSelector cannot both be specified. If both are present the message pattern will be ignored and an error will be logged. If not supplied only the text derived from the logging message will be used. See PatternLayout for information on the pattern strings. +| [[GELFLayout-messagePattern]] messagePattern +| String +| The pattern to use to format the `String`. +A `messagePattern` and xref:#GELFLayout-patternSelector[`patternSelector`] cannot both be specified. +If both are present, the message pattern will be ignored and an error will be logged. +If not supplied, only the text derived from the log message will be used. +See xref:manual/pattern-layout.adoc[] for information on the pattern strings. | omitEmptyFields | boolean | If true fields which are null or are zero-length strings will not be included as a field in the Gelf JSON. This setting will not affect whether those fields appear in the message fields. The default value is false. -| patternSelector | PatternSelector | The PatternSelector to use to format the String. A messagePattern and patternSelector cannot both be specified. If both are present the message pattern will be ignored and an error will be logged. If not supplied only the text derived from the logging message will be used. See PatternSelectors for information on how to specify a PatternSelector. See PatternLayout for information on the pattern strings. +| [[GELFLayout-patternSelector]] patternSelector +| xref:manual/pattern-layout.adoc#plugin-element-PatternSelector[PatternSelector] +| The `PatternSelector` to use to format the `String`. +A xref:#GELFLayout-messagePattern[`messagePattern`] and `patternSelector` cannot both be specified. +If both are present, the message pattern will be ignored and an error will be logged. +If not supplied, only the text derived from the logging message will be used. +See xref:manual/pattern-layout.adoc#plugin-element-PatternSelector[`PatternSelector`] for information on how to specify a `PatternSelector`. +See xref:manual/pattern-layout.adoc[] for information on the pattern strings. | threadContextExcludes | String | A comma separated list of ThreadContext attributes to exclude when formatting the event. This attribute only applies when includeThreadContext="true" is specified. If threadContextIncludes are also specified this attribute will be ignored. | threadContextIncludes | String | A comma separated list of ThreadContext attributes to include when formatting the event. This attribute only applies when includeThreadContext="true" is specified. If threadContextExcludes are also specified this attribute will override them. ThreadContext fields specified here that have no value will be omitted. | ThreadContextPrefix | String | A String to prepend to all elements of the ThreadContextMap when rendered as a field. Defaults to an empty String. @@ -277,12 +290,15 @@ expensive operation and may impact performance. Use with caution. |datePattern |String -|The date format of the logging event. The default is "JVM_ELAPSE_TIME", which outputs the milliseconds since JVM started. For other valid values, refer to the link:#PatternDate[date pattern] of PatternLayout. +|The date format of the log event. +The default is `JVM_ELAPSE_TIME`, which outputs the milliseconds since JVM started. +For other valid values, refer to xref:manual/pattern-layout.adoc#converter-date[the `date` conversion specifier of Pattern Layout]. |timezone |String -|The timezone id of the logging event. If not specified, this layout uses the https://docs.oracle.com/javase/6/docs/api/java/util/TimeZone.html#getDefault()[java.util.TimeZone.getDefault] as the default timezone. Like link:#PatternDate[date pattern] of PatternLayout, you can use the timezone id from -https://docs.oracle.com/javase/6/docs/api/java/util/TimeZone.html#getTimeZone(java.lang.String)[java.util.TimeZone.getTimeZone]. +|The time zone ID of the log event. +If not specified, this layout uses the https://docs.oracle.com/javase/8/docs/api/java/util/TimeZone.html#getDefault()[`TimeZone.getDefault()`] as the default. +You can use time zone IDs supported by https://docs.oracle.com/javase/8/docs/api/java/util/TimeZone.html#getTimeZone(java.lang.String)[`TimeZone.getTimeZone(String)`]. |=== @@ -500,1429 +516,33 @@ Read more on xref:manual/json-template-layout.adoc[]... [id=pattern-layout] == [[PatternLayout]] Pattern Layout -A flexible layout is configurable with a pattern string. -The goal of this class is to format a LogEvent and return the results. -The format of the result depends on the _conversion pattern_. +Pattern Layout is a customizable, efficient, garbage-free, and human-readable string generating layout using a user-provided pattern. +It is analogous to `String#format()` with specialized directives on injecting certain properties of a `LogEvent`. -The conversion pattern is closely related to the conversion pattern of the `printf()` function in C. A conversion pattern is composed of literal text and format control expressions called _conversion specifiers_. - -_Note that any literal text, including *Special Characters*, may be included in the conversion pattern._ Special Characters include *\t*, -*\n*, *\r*, *\f*. -Use *\\* to insert a single backslash into the output. - -Each conversion specifier starts with a percent sign (%) and is followed by optional _format modifiers_ and a _conversion character_. -The conversion character specifies the type of data, e.g. category, priority, date, thread name. -The format modifiers control such things as field width, padding, and left and right justification. -The following is a simple example. - -Let the conversion pattern be *"%-5p [%t]: %m%n"* and assume that the Log4j environment was set to use a PatternLayout. -Then the statements - -.... -Logger logger = LogManager.getLogger("MyLogger"); -logger.debug("Message 1"); -logger.warn("Message 2"); -.... - -would yield the output - -.... -DEBUG [main]: Message 1 -WARN [main]: Message 2 -.... - -Note that there is no explicit separator between text and conversion specifiers. -The pattern parser knows when it has reached the end of a conversion specifier when it reads a conversion character. -In the example above the conversion specifier `%-5p` means the priority of the logging event should be left justified to a width of five characters. - -If the pattern string does not contain a specifier to handle a Throwable being logged, parsing of the pattern will act as if the `%xEx` specifier had been added to the end of the string. -To suppress the formatting of the Throwable completely simply add `%ex\{0}` as a specifier in the pattern string. - -.PatternLayout Parameters -[cols="1m,1,4"] -|=== -|Parameter Name |Type |Description - -|charset -|String -|The character set to use when converting the syslog -String to a byte array. The String must be a valid -http://docs.oracle.com/javase/6/docs/api/java/nio/charset/Charset.html[Charset]. -If not specified, this layout uses the platform default character set. - -|pattern -|String -|A composite pattern string of one or more conversion -patterns from the table below. Cannot be specified with a -PatternSelector. - -|patternSelector -|PatternSelector -|A component that analyzes information -in the LogEvent and determines which pattern should be used to format -the event. The pattern and patternSelector parameters are mutually -exclusive. - -|replace -|RegexReplacement -|Allows portions of the resulting String to -be replaced. If configured, the replace element must specify the regular -expression to match and the substitution. This performs a function -similar to the RegexReplacement converter but applies to the whole -message while the converter only applies to the String its pattern -generates. - -|alwaysWriteExceptions -|boolean -|If `true` (it is by default) exceptions -are always written even if the pattern contains no exception -conversions. This means that if you do not include a way to output -exceptions in your pattern, the default exception formatter will be -added to the end of the pattern. Setting this to `false` disables this -behavior and allows you to exclude exceptions from your pattern output. - -|header -|String -|The optional header string to include at the top of -each log file. - -|footer -|String -|The optional footer string to include at the bottom of -each log file. - -|disableAnsi -|boolean -|If `true` (default is false), do not output ANSI -escape codes. - -|noConsoleNoAnsi -|boolean -|If `true` (default is false) and -`System.console()` is null, do not output ANSI escape codes. -|=== - -.RegexReplacement Parameters -|=== -|Parameter Name |Type |Description - -|regex -|String -|A Java-compliant regular expression to match the resulting string. See -https://docs.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html[Pattern]. - -|replacement -|String -|The string to replace any matched sub-strings with. -|=== - -[#Patterns] -=== Patterns - -The conversions that are provided with Log4j are: - -[cols="1m,3a"] -|=== -|Conversion Pattern |Description - -|*c*\{precision} + -*logger*\{precision} -|Outputs the name of the logger that published the logging event. The -logger conversion specifier can be optionally followed by _precision -specifier_, which consists of a decimal integer, or a pattern starting -with a decimal integer. - -When the precision specifier is an integer value, it reduces the size of -the logger name. If the number is positive, the layout prints the -corresponding number of the rightmost logger name components. If negative, -the layout removes the corresponding number of leftmost logger name -components. If the precision contains periods then the number before the first period -identifies the length to be printed from items that precede tokens in the rest of the pattern. -If the number after the first period is followed by an asterisk it indicates how many of the -rightmost tokens will be printed in full. See the table below for abbreviation examples. - -If the precision contains any non-integer characters, then the layout -abbreviates the name based on the pattern. If the precision integer is -less than one, the layout still prints the right-most token in full. By -default, the layout prints the logger name in full. - -!=== -!Conversion Pattern !Logger Name !Result - -!%c\{1} -!org.apache.commons.Foo -!Foo - -!%c\{2} -!org.apache.commons.Foo -!commons.Foo - -!%c\{10} -!org.apache.commons.Foo -!org.apache.commons.Foo - -!%c{-1} -!org.apache.commons.Foo -!apache.commons.Foo - -!%c{-2} -!org.apache.commons.Foo -!commons.Foo - -!%c{-10} -!org.apache.commons.Foo -!org.apache.commons.Foo - -!%c{1.} -!org.apache.commons.Foo -!o.a.c.Foo - -!%c{1.1.\~.~} -!org.apache.commons.test.Foo -!o.a.~.~.Foo - -!%c{.} -!org.apache.commons.test.Foo -!....Foo - -!%c{1.1.1.*} -!org.apache.commons.test.Foo -!o.a.c.test.Foo - -!%c{1.2.*} -!org.apache.commons.test.Foo -!o.a.c.test.Foo - -!%c{1.3.*} -!org.apache.commons.test.Foo -!o.a.commons.test.Foo - -!%c{1.8.*} -!org.apache.commons.test.Foo -!org.apache.commons.test.Foo - -!=== - -|[[PatternClass]] *C*\{precision} + -*class*\{precision} -|Outputs the fully qualified class name of the caller issuing the logging -request. This conversion specifier can be optionally followed by -_precision specifier_, that follows the same rules as the logger name -converter. - -Generating the class name of the caller -(link:#LocationInformation[location information]) is an expensive -operation and may impact performance. Use with caution. - -|[[PatternDate]] *d*\{pattern} + -*date*\{pattern} -|Outputs the date of the logging event. The date conversion specifier may -be followed by a set of braces containing a date and time pattern string per -https://docs.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html[`SimpleDateFormat`]. - -The predefined _named_ formats are: - -[cols=",",options="header",] -!=== -!Pattern !Example - -!%d\{DEFAULT} -!2012-11-02 14:34:02,123 - -!%d\{DEFAULT_MICROS} -!2012-11-02 14:34:02,123456 - -!%d\{DEFAULT_NANOS} -!2012-11-02 14:34:02,123456789 - -!%d\{ISO8601} -!2012-11-02T14:34:02,781 - -!%d\{ISO8601_BASIC} -!20121102T143402,781 - -!%d\{ISO8601_OFFSET_DATE_TIME_HH} -!2012-11-02'T'14:34:02,781-07 - -!%d\{ISO8601_OFFSET_DATE_TIME_HHMM} -!2012-11-02'T'14:34:02,781-0700 - -!%d\{ISO8601_OFFSET_DATE_TIME_HHCMM} -!2012-11-02'T'14:34:02,781-07:00 - -!%d\{ABSOLUTE} -!14:34:02,781 - -!%d\{ABSOLUTE_MICROS} -!14:34:02,123456 - -!%d\{ABSOLUTE_NANOS} -!14:34:02,123456789 - -!%d\{DATE} -!02 Nov 2012 14:34:02,781 - -!%d\{COMPACT} -!20121102143402781 - -!%d\{UNIX} -!1351866842 - -!%d\{UNIX_MILLIS} -!1351866842781 -!=== - -You can also use a set of braces containing a time zone id per -https://docs.oracle.com/javase/6/docs/api/java/util/TimeZone.html#getTimeZone(java.lang.String)[java.util.TimeZone.getTimeZone]. -If no date format specifier is given then the DEFAULT format is used. - -You can define custom date formats: - -[cols=",",options="header",] -!=== -!Pattern !Example - -!%d{HH:mm:ss,SSS} -!14:34:02,123 - -!%d{HH:mm:ss,nnnn} to %d{HH:mm:ss,nnnnnnnnn} -!14:34:02,1234 to 14:34:02,123456789 - -!%d{dd MMM yyyy HH:mm:ss,SSS} -!02 Nov 2012 14:34:02,123 - -!%d{dd MMM yyyy HH:mm:ss,nnnn} to %d{dd MMM yyyy HH:mm:ss,nnnnnnnnn} -!02 Nov 2012 14:34:02,1234 to 02 Nov 2012 14:34:02,123456789 - -!%d{HH:mm:ss}{GMT+0} -!18:34:02 -!=== - -`%d\{UNIX}` outputs the UNIX time in seconds. `%d\{UNIX_MILLIS}` outputs the -UNIX time in milliseconds. The UNIX time is the difference, in seconds -for UNIX and milliseconds for UNIX_MILLIS, between the current time -and midnight, January 1, 1970 UTC. While the time unit is milliseconds, -the granularity depends on the operating system -(http://msdn.microsoft.com/en-us/windows/hardware/gg463266.aspx[Windows]). -This is an efficient way to output the event time because only a -conversion from long to String takes place, there is no Date formatting -involved. - -Log4j 2.11 adds limited support for timestamps more precise than -milliseconds when running on Java 9. Note that not all -https://docs.oracle.com/javase/9/docs/api/java/time/format/DateTimeFormatter.html[DateTimeFormatter] -formats are supported. Only timestamps in the formats mentioned in the table -above may use the "nano-of-second" pattern letter `n` instead of -the "fraction-of-second" pattern letter `S`. - -Users may revert to a millisecond-precision clock when running on -Java 9 by setting system property `log4j2.Clock` to `SystemMillisClock`. - -|*enc*\{pattern}{[HTML\|XML\|JSON\|CRLF]} + -*encode*\{pattern}{[HTML\|XML\|JSON\|CRLF]} -|Encodes and escapes special characters suitable for output in specific -markup languages. By default, this encodes for HTML if only one option -is specified. The second option is used to specify which encoding format -should be used. This converter is particularly useful for encoding user-provided -data so that the output data is not written improperly or insecurely. - -A typical usage would encode the message `%enc{%m}` but user input could -come from other locations as well, such as the MDC `%enc{%mdc\{key}}` - -Using the HTML encoding format, the following characters are replaced: - -!=== -!Character !Replacement - -!'\r', '\n' -!Converted into string literals "\r" and "\n" respectively - -!&, <, >, ", ', / -!Replaced with the corresponding HTML entity -!=== - -Using the XML encoding format, this follows the escaping rules specified -by https://www.w3.org/TR/xml/[the XML specification]: - -!=== -!Character !Replacement - -!&, <, >, ", ' -!Replaced with the corresponding XML entity -!=== - -Using the JSON encoding format, this follows the escaping rules -specified by https://www.ietf.org/rfc/rfc4627.txt[RFC 4627 section 2.5]: - -!=== -!Character !Replacement - -!U+0000 - U+001F -!\u0000 - \u001F - -!Any other control characters -!Encoded into its `\uABCD` equivalent escaped code point - -!" -!\" - -!\ -!\\ -!=== - -For example, the pattern `{"message": "%enc{%m}\{JSON}"}` could be used -to output a valid JSON document containing the log message as a string -value. - -Using the CRLF encoding format, the following characters are replaced: - -!=== -!Character !Replacement - -!'\r', '\n' -!Converted into literal strings "\r" and "\n" respectively -!=== - -|*equals*\{pattern}\{test}\{substitution} + -*equalsIgnoreCase*\{pattern}\{test}\{substitution} -|Replaces occurrences of 'test', a string, with its replacement -'substitution' in the string resulting from the evaluation of the pattern. -For example, "%equals{[%marker]}{[]}\{}" will replace '[]' strings -produced by events without markers with an empty string. - -The pattern can be arbitrarily complex and in particular can contain -multiple conversion keywords. - -|**ex**\|**exception**\|*throwable* + -{ + -  [ "none" + -   \| "full" + -   \| depth + -   \| "short" + -   \| "short.className" + -   \| "short.fileName" + -   \| "short.lineNumber" + -   \| "short.methodName" + -   \| "short.message" + -   \| "short.localizedMessage"] + -} + -  {filters(package,package,...)} + -  {suffix(_pattern_)} + -  {separator(_separator_)} -|Outputs the Throwable trace bound to the logging event, by default this -will output the full trace as one would normally find with a call to -`Throwable.printStackTrace()`. - -You can follow the throwable conversion word with an option in the form -`%throwable\{option}`. - -`%throwable\{short}` outputs the first line of the Throwable. - -`%throwable{short.className}` outputs the name of the class where the -exception occurred. - -`%throwable{short.methodName}` outputs the method name where the -exception occurred. - -`%throwable{short.fileName}` outputs the name of the class where the -exception occurred. - -`%throwable{short.lineNumber}` outputs the line number where the -exception occurred. - -`%throwable{short.message}` outputs the message. - -`%throwable{short.localizedMessage}` outputs the localized message. - -`%throwable\{n}` outputs the first n lines of the stack trace. - -Specifying `%throwable\{none}` or `%throwable\{0}` suppresses output of -the exception. - -Use `{filters(packages)}` where _packages_ is a list of package names to -suppress matching stack frames from stack traces. - -Use `{suffix(pattern)}` to add the output of _pattern_ at the end of -each stack frame. - -Use a `{separator(...)}` as the end-of-line string. For example: -`separator(\|)`. The default value is the `line.separator` system -property, which is operating system dependent. - -|[[PatternFile]] *F* + -*file* -|Outputs the file name where the logging request was issued. - -Generating the file information (link:#LocationInformation[location -information]) is an expensive operation and may impact performance. Use -with caution. - -|*highlight*\{pattern}\{style} -|Adds ANSI colors to the result of the enclosed pattern based on the -current event's logging level. (See Jansi link:#enable-jansi[configuration].) - -The default colors for each level are: - -!=== -!Level !ANSI color - -!FATAL -!Bright red - -!ERROR -!Bright red - -!WARN -!Yellow - -!INFO -!Green - -!DEBUG -!Cyan - -!TRACE -!Black (looks dark grey) -!=== - -The color names are ANSI names defined in the -link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/AnsiEscape.html[`AnsiEscape`] -class. - -The color and attribute names are standard, but the exact shade, hue, or value. - -.Color table -!=== -!Intensity Code !0 !1 !2 !3 !4 !5 !6 !7 - -!Normal !Black !Red !Green !Yellow !Blue !Magenta !Cyan !White - -!Bright !Black !Red !Green !Yellow !Blue !Magenta !Cyan !White -!=== - -You can use the default colors with: - -.... -%highlight{%d [%t] %-5level: %msg%n%throwable} -.... - -You can override the default colors in the optional `\{style}` option. For -example: - -.... -%highlight{%d [%t] %-5level: %msg%n%throwable}{FATAL=white, ERROR=red, WARN=blue, INFO=black, DEBUG=green, TRACE=blue} -.... - -You can highlight only the portion of the log event: - -.... -%d [%t] %highlight{%-5level: %msg%n%throwable} -.... - -You can style one part of the message and highlight the rest of the log event: - -.... -%style{%d [%t]}{black} %highlight{%-5level: %msg%n%throwable} -.... - -You can also use the STYLE key to use a predefined group of colors: - -.... -%highlight{%d [%t] %-5level: %msg%n%throwable}{STYLE=Logback} -.... - -The STYLE value can be one of: - -* Default: see above -* Logback: -!=== -!Level !ANSI color - -!FATAL !Blinking bright red - -!ERROR !Bright red - -!WARN !Red - -!INFO !Blue - -!DEBUG !Normal - -!TRACE !Normal -!=== - -|[[PatternMap]] *K*\{key} + -*map*\{key} + -*MAP*\{key} -|Outputs the entries in a -link:../javadoc/log4j-api/org/apache/logging/log4j/message/MapMessage.html[MapMessage], -if one is present in the event. The `K` conversion character can be -followed by the key for the map placed between braces, as in -`%K\{clientNumber}` where `clientNumber` is the key. The value of the Map -corresponding to the key will be output. If no additional sub-option -is specified, then the entire contents of the Map key-value pair set is -output using a format {{key1,val1},{key2,val2}} - -|[[PatternLocation]] *l* + -*location* -|Outputs location information of the caller which generates the logging event. - -The location information depends on the JVM implementation but it usually -consists of the fully qualified name of the calling method followed by -the callers source the file name and line number between parentheses. - -Generating link:#LocationInformation[location information] is an -expensive operation and may impact performance. Use with caution. - -|[[PatternLine]] *L* + -*line* -|Outputs the line number from where the logging request was issued. - -Generating line number information (link:#LocationInformation[location -information]) is an expensive operation and may impact performance. Use -with caution. - -|[[PatternMessage]] *m*\{lookups}\{ansi} + -*msg*\{lookups}\{ansi} + -*message\{lookups}\{ansi} -|Outputs the application-supplied message associated with the logging -event. - -Add `\{ansi}` to render messages with ANSI escape codes (requires JAnsi, -see link:#enable-jansi[configuration].) - -The default syntax for embedded ANSI codes is: - -.... -@\|code(,code)* text\|@ -.... - -For example, to render the message `"Hello"` in green, use: - -.... -@\|green Hello\|@ -.... - -To render the message `"Hello"` in bold and red, use: - -.... -@\|bold,red Warning!\|@ -.... - -You can also define custom style names in the configuration with the -syntax: - -.... -%message{ansi}{StyleName=value(,value)*( StyleName=value(,value)*)*}%n -.... - -For example: - -.... -%message{ansi}{WarningStyle=red,bold KeyStyle=white ValueStyle=blue}%n -.... - -The call site can look like this: - -.... -logger.info("@\|KeyStyle {}\|@ = @\|ValueStyle {}\|@", entry.getKey(), entry.getValue()); -.... - -Use `\{lookups}` to log messages like `"${date:YYYY-MM-dd}"` using lookups. -using any lookups. This will replace the date template `{date:YYYY-MM-dd}` -with an actual date. This can be confusing in many cases, and it's often both easier and -more obvious to handle the lookup in code. -This feature is disabled by default and the message string is logged untouched. - -*Note:* Users are *STRONGLY* discouraged from using the lookups option. Doing so may allow uncontrolled user input -containing lookups to take unintended actions. In almost all cases the software developer can accomplish the same tasks -lookups perform directly in the application code. - -|[[PatternMethod]] *M* + -*method* -|Outputs the method name where the logging request was issued. - -Generating the method name of the caller -(link:#LocationInformation[location information]) is an expensive -operation and may impact performance. Use with caution. - -|[[PatternMarker]] *marker* -|The full name of the marker, including parents, if one is present. - -|[[PatternMarkerSimpleName]] *markerSimpleName* -|The simple name of the marker (not including parents), if one is present. - -|[[PatternMaxLength]] *maxLen* + -*maxLength* -|Outputs the result of evaluating the pattern and truncating the result. -If the length is greater than 20, then the output will contain a -trailing ellipsis. If the provided length is invalid, a default value of -100 is used. - -Example syntax: `%maxLen{%p: %c\{1} - %m%notEmpty{ =>%ex\{short}}}\{160}` -will be limited to 160 characters with a trailing ellipsis. Another -example: `%maxLen{%m}\{20}` will be limited to 20 characters and no -trailing ellipsis. - -|[[PatternNewLine]] *n* -|Outputs the platform-dependent line separator character or characters. - -This conversion character offers practically the same performance as -using non-portable line separator strings such as "\n", or "\r\n". Thus, -it is the preferred way of specifying a line separator. - -|[[NanoTime]] *N* + -*nano* -|Outputs the result of `System.nanoTime()` at the time the log event was created. - -|[[Process_ID]] *pid*{[defaultValue]} + -*processId*{[defaultValue]} -|Outputs the process ID if supported by the -underlying platform. An optional default value may be specified to be -shown if the platform does not support process IDs. - -|[[VariablesNotEmpty]] *variablesNotEmpty*\{pattern} + -*varsNotEmpty*\{pattern} + -*notEmpty*\{pattern} -|Outputs the result of evaluating the pattern if and only if all -variables in the pattern are not empty. - -For example: - -.... -%notEmpty{[%marker]} -.... - -|[[PatternLevel]] **p**\|*level*{__level__=_label_, __level__=_label_, -...} **p**\|*level*{length=_n_} -**p**\|*level*{lowerCase=__true__\|_false_} -|Outputs the level of the logging event. You provide a level name map in -the form "level=value, level=value" where the level is the name of the Level -and value is the value that should be displayed instead of the name of -the Level. - -For example: - -.... -%level{WARN=Warning, DEBUG=Debug, ERROR=Error, TRACE=Trace, INFO=Info} -.... - -Alternatively, for the compact-minded: - -.... -%level{WARN=W, DEBUG=D, ERROR=E, TRACE=T, INFO=I} -.... - -More succinctly, for the same result as above, you can define the length -of the level label: - -.... -%level{length=1} -.... - -If the length is greater than a level name length, the layout uses the -normal level name. - -You can combine the two kinds of options: - -.... -%level{ERROR=Error, length=2} -.... - -This gives you the `Error` level name and all other level names of length -2. - -Finally, you can output lower-case level names (the default is upper-case): - -.... -%level{lowerCase=true} -.... - -|[[PatternRelative]] *r* + -*relative* -|Outputs the number of milliseconds elapsed since the JVM was -started until the creation of the logging event. - -|[[PatternRepeat]] *R*\{string}\{count} + -*repeat*\{string}\{count} -|Produces a string containing the requested number of instances of the specified string. -For example, "%repeat{\*}\{2}" will result in the string "**". - -|[[PatternReplace]] *replace*\{pattern}\{regex}\{substitution} -|Replaces occurrences of 'regex', a regular expression, with its -replacement 'substitution' in the string resulting from the evaluation of -the pattern. For example, "%replace{%msg}{\s}\{}" will remove all -spaces contained in the event message. - -The pattern can be arbitrarily complex and in particular, can contain -multiple conversion keywords. For instance, "%replace{%logger -%msg}{\.}{/}" will replace all dots in the logger or the message of -the event with a forward slash. - -|[[PatternException]] **rEx**\|**rException**\|*rThrowable* + -  { + -    ["none" \| "short" \| "full" \| depth] + -    [,filters(package,package,...)] + -    [,separator(_separator_)] + -  } + -  {ansi( + -    Key=Value,Value,... + -    Key=Value,Value,... + -    ...) + -  } + -  {suffix(_pattern_)} + -|The same as the %throwable conversion word but the stack trace is -printed starting with the first exception that was thrown followed by -each subsequent wrapping exception. - -The throwable conversion word can be followed by an option in the form -`%rEx\{short}` which will only output the first line of the Throwable or -`%rEx\{n}` where the first n lines of the stack trace will be printed. - -Specifying `%rEx\{none}` or `%rEx\{0}` will suppress printing of the -exception. - -Use `filters(packages)` where _packages_ is a list of package names to -suppress matching stack frames from stack traces. - -Use a `separator` string to separate the lines of a stack trace. For -example: `separator(\|)`. The default value is the `line.separator` -system property, which is operating system dependent. - -Use `rEx{suffix(pattern)` to add the output of _pattern_ to the output -only when there is a throwable to print. - -|[[PatternSequenceNumber]] *sn* + -*sequenceNumber* -|Includes a sequence number that will be incremented in -every event. The counter is a static variable so will only be unique -within applications that share the same converter Class object. - -|[[PatternStyle]] *style*\{pattern}{ANSI style} -|Uses ANSI escape sequences to style the result of the enclosed pattern. -The style can consist of a comma-separated list of style names from the -following table. (See Jansi link:#enable-jansi[configuration].) - -!=== -!Style Name !Description - -!Normal -!Normal display - -!Bright -!Bold - -!Dim -!Dimmed or faint characters - -!Underline -!Underlined characters - -!Blink -!Blinking characters - -!Reverse -!Reverse video - -!Hidden -! - -!Black or FG_Black -!Set the foreground color to black - -!Red or FG_Red -!Set the foreground color to red - -!Green or FG_Green -!Set the foreground color to green - -!Yellow or FG_Yellow -!Set the foreground color to yellow - -!Blue or FG_Blue -!Set the foreground color to blue - -!Magenta or FG_Magenta -!Set the foreground color to magenta - -!Cyan or FG_Cyan -!Set the foreground color to cyan - -!White or FG_White -!Set the foreground color to white - -!Default or FG_Default -!Set the foreground color to default (white) - -!BG_Black -!Set the background color to black - -!BG_Red -!Set the background color to red - -!BG_Green -!Set the background color to green - -!BG_Yellow -!Set the background color to yellow - -!BG_Blue -!Set the background color to blue - -!BG_Magenta -!Set the background color to magenta - -!BG_Cyan -!Set the background color to cyan - -!BG_White -!Set the background color to white -!=== - -For example: - -.... -%style{%d{ISO8601}}{black} %style{[%t]}{blue} %style{%-5level:}{yellow} %style{%msg%n%throwable}{green} -.... - -You can also combine styles: - -.... -%d %highlight{%p} %style{%logger}{bright,cyan} %C{1.} %msg%n -.... - -You can also use `%` with a color like `%black`, `%blue`, `%cyan`, and -so on. For example: - -.... -%black{%d{ISO8601}} %blue{[%t]} %yellow{%-5level:} %green{%msg%n%throwable} -.... - -|[[PatternThreadId]] *T* + -*tid* + -*threadId* -|Outputs the ID of the thread that generated the logging event. - -|[[PatternThreadName]] *t* + -*tn* + -*thread* + -*threadName* -|Outputs the name of the thread that generated the logging event. - -|[[PatternThreadPriority]] *tp* + -*threadPriority* -|Outputs the priority of the thread that generated the logging event. - -|[[PatternLoggerFqcn]] *fqcn* -|Outputs the fully qualified class name of the logger. - -|[[EndOfBatch]] *endOfBatch* -|Outputs the EndOfBatch status of the logging event, as "true" or "false". - -|[[PatternNDC]] *x* + -*NDC* -|Outputs the Thread Context Stack (also known as the Nested -Diagnostic Context or NDC) associated with the thread that generated the -logging event. - -|[[PatternMDC]] *X*{key[,key2...]} + -*mdc*{key[,key2...]} + -*MDC*{key[,key2...]} -|Outputs the Thread Context Map (also known as the Mapped Diagnostic -Context or MDC) associated with the thread that generated the logging -event. The *X* conversion character can be followed by one or more keys -for the map placed between braces, as in *%X\{clientNumber}* where -`clientNumber` is the key. The value in the MDC corresponding to the key -will be output. - -If a list of keys is provided, such as *%X{name, number}*, then each -key that is present in the ThreadContext will be output using the format -{name=val1, number=val2}. The key/value pairs will be printed in the -order they appear in the list. - -If no sub-options are specified then the entire contents of the MDC key-value pair -set is output using a format {key1=val1, key2=val2}. -The key/value pairs will be printed in sorted order. - -See the -link:../javadoc/log4j-api/org/apache/logging/log4j/ThreadContext.html[ThreadContext] -class for more details. - -|[[PatternUUID]] *u*{"RANDOM" \| "TIME"} + -*uuid* -|Includes either a random or a time-based UUID. The time-based -UUID is a Type 1 UUID that can generate up to 10,000 unique IDs per -millisecond will use the MAC address of each host, and to try to insure -uniqueness across multiple JVMs and/or ClassLoaders on the same host -a random number between 0 and 16,384 will be associated with each instance -of the UUID generator Class and included in each time-based UUID -generated. Because time-based UUIDs contain the MAC address and -timestamp they should be used with care as they can cause a security -vulnerability. - -|[[PatternExtendedException]] **xEx**\|**xException**\|*xThrowable* + -  { + -    ["none" \| "short" \| "full" \| depth] + -    [,filters(package,package,...)] + -    [,separator(_separator_)] + -  } + -  {ansi( + -    Key=Value,Value,... + -    Key=Value,Value,... + -    ...) + -  } + -  {suffix(_pattern_)} + -|The same as the %throwable conversion word but also includes class -packaging information. - -At the end of each stack element of the exception, a string containing -the name of the jar file that contains the class or the directory the -class is located in and the "Implementation-Version" as found in that -jar's manifest will be added. If the information is uncertain, then the -class packaging data will be preceded by a tilde, i.e. the '~' -character. - -The throwable conversion word can be followed by an option in the form -`%xEx\{short}` which will only output the first line of the Throwable or -`%xEx\{n}` where the first n lines of the stack trace will be printed. -Specifying `%xEx\{none}` or `%xEx\{0}` will suppress printing of the -exception. - -Use `filters(packages)` where _packages_ is a list of package names to -suppress matching stack frames from stack traces. - -Use a `separator` string to separate the lines of a stack trace. For -example: `separator(\|)`. The default value is the `line.separator` -system property, which is operating system dependent. - -The `ansi` option renders stack traces with ANSI escape code using the -JAnsi library. (See link:#enable-jansi[configuration].) Use `\{ansi}` to -use the default color mapping. You can specify your mappings with -`key=value` pairs. The keys are: - -* Prefix -* Name -* NameMessageSeparator -* Message -* At -* CauseLabel -* Text -* More -* Suppressed -* StackTraceElement.ClassName -* StackTraceElement.ClassMethodSeparator -* StackTraceElement.MethodName -* StackTraceElement.NativeMethod -* StackTraceElement.FileName -* StackTraceElement.LineNumber -* StackTraceElement.Container -* StackTraceElement.ContainerSeparator -* StackTraceElement.UnknownSource -* ExtraClassInfo.Inexact -* ExtraClassInfo.Container -* ExtraClassInfo.ContainerSeparator -* ExtraClassInfo.Location -* ExtraClassInfo.Version - -The values are names from JAnsi's -https://fusesource.github.io/jansi/documentation/api/org/fusesource/jansi/AnsiRenderer.Code.html[Code] -class like `blue`, `bg_red`, and so on (Log4j ignores case.) - -The special key `StyleMapName` can be set to one of the following -predefined maps: `Spock`, `Kirk`. - -As with %throwable, the *%xEx{suffix(_pattern_)* conversion will add -the output of _pattern_ to the output only if there is a throwable to -print. - -|[[PatternPercentLiteral]] *%* -|The sequence %% outputs a single percent sign. -|=== - -By default, the relevant information is output as is. -However, with the aid of format modifiers it is possible to change the minimum field width, the maximum field width and justification. - -The optional format modifier is placed between the percent sign and the conversion character. - -The first optional format modifier is the _left justification flag_ -which is just the minus (-) character. -Then comes the optional _minimum field width_ modifier. -This is a decimal constant that represents the minimum number of characters to output. -If the data item requires fewer characters, it is padded on either the left or the right until the minimum width is reached. -The default is to pad on the left (right justify) but you can specify right padding with the left justification flag. -The padding character is space. -If the data item is larger than the minimum field width, the field is expanded to accommodate the data. -The value is never truncated. -To use zeros as the padding character prepend the _minimum field width_ with a zero. - -This behavior can be changed using the _maximum field width_ modifier which is designated by a period followed by a decimal constant. -If the data item is longer than the maximum field, then the extra characters are removed from the _beginning_ of the data item and not from the end. -For example, if the maximum field width is eight and the data item is ten characters long, then the first two characters of the data item are dropped. -This behavior deviates from the `printf()` function in C where truncation is done from the end. - -Truncation from the end is possible by appending a minus character right after the period. -In that case, if the maximum field width is eight and the data item is ten characters long, then the last two characters of the data item are dropped. - -Below are various format modifier examples for the category conversion specifier. - -.Pattern Converters -|=== -|Format modifier |left justify |minimum width |maximum width |comment - -|%20c -|false -|20 -|none -|Left pad with spaces if the category name is -less than 20 characters long. - -|%-20c -|true -|20 -|none -|Right pad with spaces if the category name is -less than 20 characters long. - -|%.30c -|NA -|none -|30 -|Truncate from the beginning if the category name is longer than 30 characters. - -|%20.30c -|false -|20 -|30 -|Left pad with spaces if the category name is -shorter than 20 characters. However, if the category name is longer than 30 -characters, then truncate from the beginning. - -|%-20.30c -|true -|20 -|30 -|Right pad with spaces if the category name is -shorter than 20 characters. However, if the category name is longer than 30 -characters, then truncate from the beginning. - -|%-20.-30c -|true -|20 -|30 -|Right pad with spaces if the category name is -shorter than 20 characters. However, if the category name is longer than 30 -characters, then truncate from the end. -|=== - -[#enable-jansi] -=== ANSI Styling on Windows - -ANSI escape sequences are supported natively on many platforms but are not by default on Windows. -To enable ANSI support add the -http://jansi.fusesource.org/[Jansi] jar to your application and set property `log4j.skipJansi` to `false`. -This allows Log4j to use Jansi to add ANSI escape codes when writing to the console. - -NOTE: Before Log4j 2.10, Jansi was enabled by default. -The fact that Jansi requires native code means that Jansi can only be loaded by a single class loader. -For web applications, this means the Jansi jar has to be in the web container's classpath. -To avoid causing problems for web applications, Log4j will no longer automatically try to load Jansi without explicit configuration from Log4j 2.10 onward. - -=== Example Patterns - -==== Filtered Throwables - -This example shows how to filter out classes from unimportant packages in stack traces. - -[source,xml] ----- - - - org.junit,org.apache.maven,sun.reflect,java.lang.reflect - - - - ----- - -The result printed to the console will appear similar to: - -.... -Exception java.lang.IllegalArgumentException: IllegalArgument -at org.apache.logging.log4j.core.pattern.ExtendedThrowableTest.testException(ExtendedThrowableTest.java:72) [test-classes/:?] -... suppressed 26 lines -at $Proxy0.invoke(Unknown Source)} [?:?] -... suppressed 3 lines -Caused by: java.lang.NullPointerException: null pointer -at org.apache.logging.log4j.core.pattern.ExtendedThrowableTest.testException(ExtendedThrowableTest.java:71) ~[test-classes/:?] -... 30 more -.... - -==== ANSI Styled - -The log level will be highlighted according to the event's log level. -All the content that follows the level will be bright green. - -[source,xml] ----- - - %d %highlight{%p} %style{%C{1.} [%t] %m}{bold,green}%n - ----- - -[#PatternSelectors] -=== Pattern Selectors - -The PatternLayout can be configured with a PatternSelector to allow it to choose a pattern to use based on attributes of the log event or other factors. -A PatternSelector will normally be configured with a defaultPattern attribute, which is used when other criteria don't match, and a set of PatternMatch elements that identify the various patterns that can be selected. - -[#LevelPatternSelector] -==== LevelPatternSelector - -The LevelPatternSelector selects patterns based on the log level of the log event. -If the Level in the log event is equal to (ignoring case) the name specified on the PatternMatch key attribute, then the pattern specified on that PatternMatch element will be used. - -[source,xml] ----- - - - - - ----- - -[#MarkerPatternSelector] -==== MarkerPatternSelector +[IMPORTANT] +==== +Pattern Layout is not intended for _structural logging_ purposes. +For production environments, you are strongly advised to use xref:manual/json-template-layout.adoc[] producing JSON output ready to be delivered to log ingestion systems such as Elasticsearch or Google Cloud Logging. +==== -The MarkerPatternSelector selects patterns based on the Marker included in the log event. -If the Marker in the log event is equal to or is an ancestor of the name specified on the PatternMatch key attribute, then the pattern specified on that PatternMatch element will be used. +A conversion pattern is composed of literal text and format control expressions. +For instance, given the `%-5p [%t]: %m%n` pattern, following statements -[source,xml] +[source,java] ---- - - - - - +LOGGER.debug("Message 1"); +LOGGER.warn("Message 2"); ---- -[#ScriptPatternSelector] -==== ScriptPatternSelector - -The ScriptPatternSelector executes a script as described in the -xref:manual/scripts.adoc[Scripts] section of the Configuration chapter. -The script is passed all the properties configured in the Properties section of the configuration, the StrSubstitutor used by the Configuration in the "substitutor" variables, and the log event in the "logEvent" variable, and is expected to return the value of the PatternMatch key that should be used, or null if the default pattern should be used. +will yield the output -[source,xml] +[source,text] ---- - - - - - - - +DEBUG [main]: Message 1 +WARN [main]: Message 2 ---- -[#PatternLayout-gcfree] -=== Garbage-free configuration - -PatternLayout with the following limited set of conversion patterns is -garbage-free. Format modifiers to control such things as field width, -padding, left and right justification will not generate garbage. - -[cols="1m,2"] -|=== -|Conversion Pattern |Description - -|%c\{precision}, %logger\{precision} -|Logger name - -|%d, %date -a| -Note: Only the predefined date formats are garbage-free: (millisecond -separator may be either a comma ',' or a period '.') - -[cols="1m,1"] -!=== -!Pattern !Example - -!%d\{DEFAULT} -!2012-11-02 14:34:02,781 - -!%d\{ISO8601} -!2012-11-02T14:34:02,781 - -!%d\{ISO8601_BASIC} -!20121102T143402,781 - -!%d\{ABSOLUTE} -!14:34:02,781 - -!%d\{DATE} -!02 Nov 2012 14:34:02,781 - -!%d\{COMPACT} -!20121102143402781 - -!%d{HH:mm:ss,SSS} -!14:34:02,781 - -!%d{dd MMM yyyy HH:mm:ss,SSS} -!02 Nov 2012 14:34:02,781 - -!%d{HH:mm:ss}{GMT+0} -!18:34:02 - -!%d\{UNIX} -!1351866842 - -!%d\{UNIX_MILLIS} -!1351866842781 -!=== - -|%enc\{pattern}, %encode\{pattern} -|Encodes special characters such as -'\n' and HTML characters to help prevent log forging and some XSS -attacks that could occur when displaying logs in a web browser - -garbage-free since 2.8 - -|%equals\{pattern}\{test}\{substitution}, -%equalsIgnoreCase\{pattern}\{test}\{substitution} -|Replaces occurrences -of 'test', a string, with its replacement 'substitution' in the string -resulting from evaluation of the pattern - garbage-free since 2.8 - -|%highlight\{pattern}\{style} -|Adds ANSI colors - garbage-free since 2.7 -(unless nested pattern is not garbage free) - -|%K\{key}, %map\{key}, %MAP\{key} -|Outputs the entries in a -link:../javadoc/log4j-api/org/apache/logging/log4j/message/MapMessage.html[MapMessage], -if one is present in the event - garbage-free since 2.8. - -|%m, %msg, %message -|Log message (garbage-free unless message text -contains '${') - -|%marker -|The full name of the marker (including parents) - garbage-free -since 2.8 - -|%markerSimpleName -|The simple name of the marker (not including -parents) - -|%maxLen, %maxLength -|Truncates another pattern to some max number of -characters - garbage-free since 2.8 - -|%n -|The platform dependent line separator - -|%N, %nano -|System.nanoTime() when the event was logged - -|%notEmpty\{pattern}, %varsNotEmpty\{pattern}, -%variablesNotEmpty\{pattern} -|Outputs the result of evaluating the -pattern if and only if all variables in the pattern are not empty - -garbage-free since 2.8 - -|%p, %level -|The level of the logging event - -|%r, %relative -|The number of milliseconds elapsed since the JVM was -started until the creation of the logging event - garbage-free since 2.8 - -|%sn, %sequenceNumber -|A sequence number that will be incremented in -every event - garbage-free since 2.8 - -|%style\{pattern}{ANSI style} -|Style the message - garbage-free since -2.7 (unless nested pattern is not garbage free) - -|%T, %tid, %threadId -|The ID of the thread that generated the logging -event - -|%t, %tn, %thread, %threadName -|The name of the thread that generated -the logging event - -|%tp -|The priority of the thread that generated the logging event - -|%X{key[,key2...]}, %mdc{key[,key2...]}, %MDC{key[,key2...]} -|Outputs -the Thread Context Map (also known as the Mapped Diagnostic Context or -MDC) associated with the thread that generated the logging event - -garbage-free since 2.8 - -|literal text -|Garbage-free unless literal contains '${' (variable -substitution) -|=== - -Other PatternLayout conversion patterns, and other Layouts may be -updated to avoid creating temporary objects in future releases. (Patches -welcome!) - -NOTE: Logging exceptions and stack traces will create temporary -objects with any layout. (However, Layouts will only create these -temporary objects when an exception actually occurs.) We haven't figured -out a way to log exceptions and stack traces without creating temporary -objects. That is unfortunate, but you probably still want to log them -when they happen. - -[NOTE] -==== -patterns containing regular expressions and lookups for property -substitution will result in temporary objects being created during -steady-state logging. - -Including location information is done by walking the stacktrace of an -exception, which creates temporary objects, so the following patterns -are not garbage-free: - -* %C, %class - Class Name -* %F, %file - File Location -* %l, %location - Location -* %L, %line - Line Location -* %M, %method - Method Location - -Also, the pattern converters for formatting Throwables are not -garbage-free: - -* %ex, %exception, %throwable - The Throwable trace bound to the -LoggingEvent -* %rEx, %rException %rThrowable - Same as %ex but with wrapping -exceptions -* %xEx, %xException, %xThrowable - Same as %ex but with class packaging -information -* %u, %uuid - Creates a new random or time-based UUID while formatting -==== - -[#PatternLayout-performance] -=== Performance - -Great effort has been put into the efficiency of Pattern Layout. -To get the most out of it, mind the following checklist: - -* Enable xref:manual/garbagefree.adoc[garbage-free logging] -* Mind xref:#PatternLayout-gcfree[the garbage footprint of features you use] -* Don't give too much slack to xref:manual/systemproperties.adoc#log4j2.layoutStringBuilderMaxSize[`log4j2 -.layoutStringBuilderMaxSize`] and try to keep it relatively tight +Read more on xref:manual/pattern-layout.adoc[]... [#RFC5424Layout] == RFC5424 Layout @@ -1954,11 +574,8 @@ http://tools.ietf.org/html/rfc5424#section-7.2.2[RFC 5424] |exceptionPattern |String -|One of the conversion specifiers from -PatternLayout that defines which ThrowablePatternConverter to use to -format exceptions. Any of the options that are valid for those -specifiers may be included. The default is to not include the Throwable -from the event, if any, in the output. +|An xref:manual/pattern-layout.adoc#converter-exception[`exception` conversion specifier of Pattern Layout]. +The default is to not include the `Throwable` from the event, if any, in the output. |facility |String @@ -1990,14 +607,11 @@ the id from the Message will be used instead of this value. will be included in the RFC 5424 Syslog record. Defaults to true. |loggerFields -|List of KeyValuePairs -|Allows arbitrary PatternLayout -patterns to be included as specified ThreadContext fields; no default -specified. To use, include a nested element, containing -one or more elements. Each must have a key -attribute, which specifies the key name which will be used to identify -the field within the MDC Structured Data element, and a value attribute, -which specifies the PatternLayout pattern to use as the value. +|`KeyValuePair[]` +|Allows arbitrary xref:manual/thread-context.adoc[] map entries. +To use, include a `LoggerFields` nested element, containing one or more `KeyValuePair` elements. +Each `KeyValuePair` must have `key` and `value` attributes associating them with a thread context map entry. +The `value` attribute can be an arbitrary xref:manual/pattern-layout.adoc[] pattern. |mdcExcludes |String diff --git a/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc b/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc new file mode 100644 index 00000000000..fc187be95ce --- /dev/null +++ b/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc @@ -0,0 +1,1705 @@ +//// + 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. +//// + += Pattern Layout + +Pattern Layout is a customizable, xref:#performance[efficient], xref:#garbage-free[garbage-free], and human-readable string generating layout using a user-provided pattern. +It is analogous to `String#format()` with specialized directives on injecting certain properties of a `LogEvent`. + +[IMPORTANT] +==== +Pattern Layout is not intended for _structural logging_ purposes. +For production environments, you are strongly advised to use xref:manual/json-template-layout.adoc[] producing JSON output ready to be delivered to log ingestion systems such as Elasticsearch or Google Cloud Logging. +==== + +[#usage] +== Usage + +Pattern Layout is primarily configured using a *conversion pattern* referring to certain properties of a `LogEvent`. +A conversion pattern is composed of literal text and format control expressions called *conversion specifiers*. +For instance, given the following layout configuration + +[tabs] +==== +XML:: ++ +.Snippet from an example {antora-examples-url}/manual/pattern-layout/usage/log4j2.xml[`log4j2.xml`] +[source,xml] +---- +include::example$manual/pattern-layout/usage/log4j2.xml[lines=26..26,indent=0] +---- + +JSON:: ++ +.Snippet from an example {antora-examples-url}/manual/pattern-layout/usage/log4j2.json[`log4j2.json`] +[source,json] +---- +include::example$manual/pattern-layout/usage/log4j2.json[lines=6..8,indent=0] +---- + +YAML:: ++ +.Snippet from an example {antora-examples-url}/manual/pattern-layout/usage/log4j2.yaml[`log4j2.yaml`] +[source,xml] +---- +include::example$manual/pattern-layout/usage/log4j2.yaml[lines=22..23,indent=0] +---- + +Properties:: ++ +.Snippet from an example {antora-examples-url}/manual/pattern-layout/usage/log4j2.properties[`log4j2.properties`] +[source,xml] +---- +include::example$manual/pattern-layout/usage/log4j2.properties[lines=19..20,indent=0] +---- +==== + +then the following statements + +[source,java] +---- +LOGGER.debug("Message 1"); +LOGGER.warn("Message 2"); +---- + +will yield the output + +[source,text] +---- +DEBUG [main]: Message 1 +WARN [main]: Message 2 +---- + +Any literal text, including `\t`, `\n`, `\r\`, and `\f` special characters, may be included in the conversion pattern. +Use `\\` to insert a single backslash into the output. + +Each conversion specifier starts with a `%` character, and is followed by optional *format modifiers* and a *conversion character*. +The conversion character specifies the type of data, e.g., category, priority, date, thread name. +The format modifiers control such things as field width, padding, and left and right justification. +Use `%%` to insert a single `%` into the output. +Use `%n` to insert the line separator of the platform. + +There is no explicit separator between text and conversion specifiers. +The pattern parser knows when it has reached the end of a conversion specifier when it reads a conversion character. +In the example above the conversion specifier `%-5p` means the priority of the log event should be left justified to a width of five characters. + +If the pattern string does not contain a specifier to handle a `Throwable` being logged, parsing of the pattern will act as if the `%xEx` specifier had been added to the end of the string. +To suppress the formatting of the `Throwable` completely simply add `%ex\{0}` as a specifier in the pattern string. + +[#config] +== Configuration + +This section explains how to configure Pattern Layout plugin element in a Log4j configuration file. + +[#plugin-attrs] +=== Plugin attributes + +Pattern Layout plugin configuration accepts the following attributes: + +[#plugin-attr-charset] +==== `charset` + +[cols="2h,6"] +|=== +|Type |`Charset` +|Default value |The platform default +|=== + +`Charset` used for encoding the produced JSON into bytes + +[#plugin-attr-pattern] +==== `pattern` + +[cols="2h,6"] +|=== +|Type |`String` +|Default value |`%m%n` +|=== + +A composite pattern string of one or more xref:#converters[]. +`pattern` and xref:#plugin-attr-patternSelector[] are mutually exclusive, that is, only one can be specified. + +[#plugin-attr-patternSelector] +==== `patternSelector` + +[cols="2h,6"] +|=== +|Type |xref:#plugin-element-PatternSelector[`PatternSelector`] +|=== + +A component that analyzes information in the `LogEvent` and determines which pattern should be used to format the event. +`patternSelector` and xref:#plugin-attr-pattern[] are mutually exclusive, that is, only one can be specified. + +[#plugin-attr-replace] +==== `replace` + +[cols="2h,6"] +|=== +|Type |xref:#plugin-element-RegexReplacement[`RegexReplacement`] +|=== + +Allows portions of the resulting `String` to be replaced. +If configured, the `replace` element must specify the regular expression to match and the substitution. + +[#plugin-attr-alwaysWriteExceptions] +==== `alwaysWriteExceptions` + +[cols="2h,6"] +|=== +|Type |`boolean` +|Default value |`true` +|=== + +If `true`, exceptions are always written even if the pattern contains no exception conversions. +This means that if you do not include a way to output exceptions in your pattern, the default exception formatter will be added to the end of the pattern. +Setting this to `false` disables this behavior and allows you to exclude exceptions from your pattern output. + +[#plugin-attr-header] +==== `header` + +[cols="2h,6"] +|=== +|Type |`String` +|=== + +The optional header to include at the top of each log file + +[#plugin-attr-footer] +==== `footer` + +[cols="2h,6"] +|=== +|Type |`String` +|=== + +The optional footer to include at the bottom of each log file + +[#plugin-attr-disableAnsi] +==== `disableAnsi` + +[cols="2h,6"] +|=== +|Type |`boolean` +|Default value |`false` +|=== + +If `true`, do not output ANSI escape codes + +[#plugin-attr-noConsoleNoAnsi] +==== `noConsoleNoAnsi` + +[cols="2h,6"] +|=== +|Type |`boolean` +|Default value |`false` +|=== + +If `true` and `System.console()` is null, do not output ANSI escape codes + +[#plugin-elements] +=== Plugin elements + +Pattern Layout plugin configuration accepts the following elements: + +[#plugin-element-RegexReplacement] +==== `RegexReplacement` + +Allows portions of the resulting `String` to be replaced. +This performs a function similar to xref:#converter-RegexReplacement[the `RegexReplacement` converter] but applies to the whole message while the converter only applies to the `String` its pattern generates. + +It supports following attributes: + +`regex`:: A https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html[Java-compliant regular expression] to match the resulting string + +`replacement`:: The string to replace any matched substrings with + +[#plugin-element-PatternSelector] +==== `PatternSelector` + +Pattern Layout can be configured with a link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/PatternSelector.html[`PatternSelector`] to allow it to choose a pattern to use based on attributes of the log event or other factors. +A `PatternSelector` will normally be configured with a `defaultPattern` attribute, which is used when other criteria don't match, and a set of link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/PatternMatch.html[`PatternMatch`] elements that identify the various patterns that can be selected. + +Predefined ``PatternSelector``s are as follows: + +[#plugin-element-LevelPatternSelector] +===== `LevelPatternSelector` + +The link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/LevelPatternSelector.html[`LevelPatternSelector`] selects patterns based on the level of the log event. +Its configuration is similar to xref:#plugin-element-MarkerPatternSelector[], with the difference that the `key` attribute of the link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/PatternMatch.html[`PatternMatch`] element is matched against the log level associated with the log event. + +[#plugin-element-MarkerPatternSelector] +===== `MarkerPatternSelector` + +The link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/MarkerPatternSelector.html[`MarkerPatternSelector`] selects patterns based on the xref:manual/markers.adoc[marker] included in the log event. +If the marker in the log event is equal to or is an ancestor of the name specified on the `key` attribute of the link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/PatternMatch.html[`PatternMatch`] element, then the `pattern` specified on that `PatternMatch` element will be used. + +Below is a `MarkerPatternSelector` example switching from the `[%-5level] %c{1.} %msg%n` default pattern to `[%-5level] %c{1.} ====== %C{1.}.%M:%L %msg ======%n`, if the marker matches to `FLOW`: + +[tabs] +==== +XML:: ++ +.Snippet from an example {antora-examples-url}/manual/pattern-layout/marker-pattern-selector/log4j2.xml[`log4j2.xml`] +[source,xml] +---- +include::example$manual/pattern-layout/marker-pattern-selector/log4j2.xml[lines=26..30,indent=0] +---- + +JSON:: ++ +.Snippet from an example {antora-examples-url}/manual/pattern-layout/marker-pattern-selector/log4j2.json[`log4j2.json`] +[source,json] +---- +include::example$manual/pattern-layout/marker-pattern-selector/log4j2.json[lines=6..16,indent=0] +---- + +YAML:: ++ +.Snippet from an example {antora-examples-url}/manual/pattern-layout/marker-pattern-selector/log4j2.yaml[`log4j2.yaml`] +[source,xml] +---- +include::example$manual/pattern-layout/marker-pattern-selector/log4j2.yaml[lines=22..27,indent=0] +---- + +Properties:: ++ +.Snippet from an example {antora-examples-url}/manual/pattern-layout/marker-pattern-selector/log4j2.properties[`log4j2.properties`] +[source,xml] +---- +include::example$manual/pattern-layout/marker-pattern-selector/log4j2.properties[lines=19..24,indent=0] +---- +==== + +[#plugin-element-ScriptPatternSelector] +===== `ScriptPatternSelector` + +The link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/ScriptPatternSelector.html[`ScriptPatternSelector`] selects patterns by matching the output of a xref:manual/scripts.adoc[script] execution against given link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/PatternMatch.html[`PatternMatch`] elements. + +Below is an example using a script determining `NoLocation` or `Flow` keywords from a log event and matching it against two ``PatternMatch``es to configure the effective pattern: + +[tabs] +==== +XML:: ++ +.Snippet from an example {antora-examples-url}/manual/pattern-layout/script-pattern-selector/log4j2.xml[`log4j2.xml`] +[source,xml] +---- +include::example$manual/pattern-layout/script-pattern-selector/log4j2.xml[lines=26..40,indent=0] +---- + +JSON:: ++ +.Snippet from an example {antora-examples-url}/manual/pattern-layout/script-pattern-selector/log4j2.json[`log4j2.json`] +[source,json] +---- +include::example$manual/pattern-layout/script-pattern-selector/log4j2.json[lines=6..25,indent=0] +---- + +YAML:: ++ +.Snippet from an example {antora-examples-url}/manual/pattern-layout/script-pattern-selector/log4j2.yaml[`log4j2.yaml`] +[source,xml] +---- +include::example$manual/pattern-layout/script-pattern-selector/log4j2.yaml[lines=22..40,indent=0] +---- + +Properties:: ++ +.Snippet from an example {antora-examples-url}/manual/pattern-layout/script-pattern-selector/log4j2.properties[`log4j2.properties`] +[source,xml] +---- +include::example$manual/pattern-layout/script-pattern-selector/log4j2.properties[lines=19..38,indent=0] +---- +==== + +[#converters] +=== Pattern converters + +The Pattern Layout _conversion pattern_ is composed of literal text and format control expressions called _conversion specifiers_ – refer to xref:#usage[] for details. +xref:manual/plugins.adoc[Plugins] implementing link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/PatternConverter.html[`PatternConverter`] are admitted to the *pattern converter* registry of Pattern Layout, and used to resolve the conversion specifiers. +The predefined set of pattern converters are shared below. + +[#converter-class] +==== Class + +Outputs the fully qualified class name of the caller issuing the logging request + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/ClassNamePatternConverter.html[`ClassNamePatternConverter`] specifier grammar +[source,text] +---- +C{precision} +class{precision} +---- + +This conversion specifier can be optionally followed by a _precision specifier_ that follows the same rules as xref:#converter-logger[the logger name converter]. + +[WARNING] +==== +Capturing the source location information to generate the class name of the caller is an expensive operation, and is not garbage-free. +xref:#converter-logger[The logger name converter] can generally be used as a zero-cost substitute. +See xref:manual/layouts.adoc#LocationInformation[this section of the layouts page] for details. +==== + +[#converter-date] +==== Date + +Outputs the date of the log event + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/DatePatternConverter.html[`DatePatternConverter`] specifier grammar +[source,text] +---- +d{pattern} +date{pattern} +---- + +The date conversion specifier may be followed by a set of braces containing a date and time pattern string per https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html[`SimpleDateFormat`]. +The predefined _named_ formats are: + +[%header,cols="2m,3m"] +|=== +|Pattern +|Example output + +|%d\{DEFAULT} +|2012-11-02 14:34:02,123 + +|%d\{DEFAULT_MICROS} +|2012-11-02 14:34:02,123456 + +|%d\{DEFAULT_NANOS} +|2012-11-02 14:34:02,123456789 + +|%d\{ISO8601} +|2012-11-02T14:34:02,781 + +|%d\{ISO8601_BASIC} +|20121102T143402,781 + +|%d\{ISO8601_OFFSET_DATE_TIME_HH} +|2012-11-02'T'14:34:02,781-07 + +|%d\{ISO8601_OFFSET_DATE_TIME_HHMM} +|2012-11-02'T'14:34:02,781-0700 + +|%d\{ISO8601_OFFSET_DATE_TIME_HHCMM} +|2012-11-02'T'14:34:02,781-07:00 + +|%d\{ABSOLUTE} +|14:34:02,781 + +|%d\{ABSOLUTE_MICROS} +|14:34:02,123456 + +|%d\{ABSOLUTE_NANOS} +|14:34:02,123456789 + +|%d\{DATE} +|02 Nov 2012 14:34:02,781 + +|%d\{COMPACT} +|20121102143402781 + +|%d\{UNIX} +|1351866842 + +|%d\{UNIX_MILLIS} +|1351866842781 +|=== + +You can also use a set of braces containing a time zone id per https://docs.oracle.com/javase/8/docs/api/java/util/TimeZone.html#getTimeZone(java.lang.String)[`java.util.TimeZone#getTimeZone(String)`]. +If no date format specifier is given then the `DEFAULT` format is used. + +You can also define custom date formats, see following examples: + +[%header,cols="2m,3m"] +|=== +|Pattern +|Example output + +|%d{HH:mm:ss,SSS} +|14:34:02,123 + +|%d{HH:mm:ss,nnnn} to %d{HH:mm:ss,nnnnnnnnn} +|14:34:02,1234 to 14:34:02,123456789 + +|%d{dd MMM yyyy HH:mm:ss,SSS} +|02 Nov 2012 14:34:02,123 + +|%d{dd MMM yyyy HH:mm:ss,nnnn} to %d{dd MMM yyyy HH:mm:ss,nnnnnnnnn} +|02 Nov 2012 14:34:02,1234 to 02 Nov 2012 14:34:02,123456789 + +|%d{HH:mm:ss}{GMT+0} +|18:34:02 +|=== + +`%d\{UNIX}` outputs the UNIX time in seconds. +`%d\{UNIX_MILLIS}` outputs the UNIX time in milliseconds. +The `UNIX` time is the difference – in seconds for `UNIX` and in milliseconds for `UNIX_MILLIS` – between the current time and 1970-01-01 00:00:00 (UTC). +While the time unit is milliseconds, the granularity depends on the platform. +This is an efficient way to output the event time because only a conversion from `long` to `String` takes place, there is no `Date` formatting involved. + +There is also limited support for timestamps more precise than milliseconds when running on Java 9 or later. +Note that not all https://docs.oracle.com/javase/9/docs/api/java/time/format/DateTimeFormatter.html[`DateTimeFormatter`] formats are supported. +Only timestamps in the formats mentioned in the table above may use the _nano-of-second_ pattern letter `n` instead of the _fraction-of-second_ pattern letter `S`. + +Users may revert to a millisecond-precision clock when running on Java 9 by setting xref:manual/systemproperties.adoc#log4j2.clock[the `log4j2.clock` system property] to `SystemMillisClock`. + +[WARNING] +==== +Only named date formats (`DEFAULT`, `ISO8601`, `UNIX`, `UNIX_MILLIS`, etc.) are garbage-free. +==== + +[#converter-encode] +==== Encode + +Encodes and escapes special characters suitable for output in specific markup languages + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/EncodingPatternConverter.html[`EncodingPatternConverter`] specifier grammar +[source,text] +---- +enc{pattern}{[HTML|XML|JSON|CRLF]} +encode{pattern}{[HTML|XML|JSON|CRLF]} +---- + +By default, this encodes for HTML if only one option is specified. +The second option is used to specify which encoding format should be used. + +A typical usage would encode the message (i.e., `%enc{%m}`), but the input could come from other locations as well (e.g., from a xref:manual/thread-context.adoc[] entry: `%enc{%mdc\{key}}`). + +Using the HTML encoding format, the following characters are replaced: + +[%header,cols="2,4"] +|=== +|Characters +|Replacement + +|`\r` and `\n` +|Converted into string literals `\r` and `\n`, respectively + +|`&<>"'/` +|Replaced with the corresponding HTML entity +|=== + +Using the XML encoding format, this follows the escaping rules specified by https://www.w3.org/TR/xml/[the XML specification]: + +[%header,cols="2,4"] +|=== +|Characters +|Replacement + +|`&<>"'` +|The corresponding XML entity +|=== + +Using the JSON encoding format, this follows the escaping rules specified by https://www.ietf.org/rfc/rfc4627.txt[RFC 4627 section 2.5]: + +[%header,cols="2,4"] +|=== +|Characters +|Replacement + +|`U+0000` - `U+001F` +|`\u0000` - `\u001F` + +|Any other control characters +|Encoded into its `\uABCD` equivalent escaped code point + +|`"` +|`\"` + +|`\` +|`\\` +|=== + +[WARNING] +==== +If you are using JSON encoder in your conversion pattern, it is a strong indicator that you are trying to implement _structured logging_ using Pattern Layout – please, don't! +*Use xref:manual/json-template-layout.adoc[] instead.* +==== + +Using the CRLF encoding format, the following characters are replaced: + +[%header,cols="2,4"] +|=== +|Characters +|Replacement + +|`\r` and `\n` +|Converted into literal strings `\r` and `\n`, respectively +|=== + +[#converter-end-of-batch] +==== End-of-batch + +Outputs the `EndOfBatch` status of the log event as `true` or `false` + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/EndOfBatchPatternConverter.html[`EndOfBatchPatternConverter`] specifier grammar +[source,text] +---- +endOfBatch +---- + +[#converter-equals] +==== Equals + +Replaces occurrences of a string (`test`) with its replacement (`substitution`) in the string resulting from the evaluation of the pattern: + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/EqualsReplacementConverter.html[`EqualsReplacementConverter`] and link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/EqualsIgnoreCaseReplacementConverter.html[`EqualsReplacementConverter`] specifiers' grammar +[source,text] +---- +equals{pattern}{test}{substitution} + +equalsIgnoreCase{pattern}{test}{substitution} +---- + +For example, `%equals{[%marker]}{[]}\{}` will replace `[]` strings produced by events without markers with an empty string. + +The pattern can be arbitrarily complex and in particular can contain multiple conversion keywords. + +[#converter-exception] +==== Exception + +Outputs the `Throwable` attached to the log event + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/ThrowablePatternConverter.html[`ThrowablePatternConverter`] specifier grammar +[source,text] +---- +ex|exception|throwable + { "none" + | "full" + | depth + | "short" + | "short.className" + | "short.fileName" + | "short.lineNumber" + | "short.methodName" + | "short.message" + | "short.localizedMessage" + } + {filters(package,package,...)} + {suffix(pattern)} + {separator(separator)} +---- + +By default this will output the full trace as one would normally find with a call to `Throwable#printStackTrace()`. + +You can follow the throwable conversion word with an option in the form `%throwable\{option}`. + +`%throwable\{short}` outputs the first line of the `Throwable`. + +`%throwable{short.className}` outputs the name of the class where the exception occurred. + +`%throwable{short.methodName}` outputs the method name where the exception occurred. + +`%throwable{short.fileName}` outputs the name of the class where the exception occurred. + +`%throwable{short.lineNumber}` outputs the line number where the exception occurred. + +`%throwable{short.message}` outputs the message. + +`%throwable{short.localizedMessage}` outputs the localized message. + +`%throwable\{n}` outputs the first `n` lines of the stack trace. + +Specifying `%throwable\{none}` or `%throwable\{0}` suppresses output of the exception. + +Use `{filters(packages)}` where _packages_ is a list of package names to suppress matching stack frames from stack traces. + +Use `{suffix(pattern)}` to add the output of _pattern_ at the end of each stack frame. + +Use a `{separator(...)}` as the end-of-line string, e.g., `separator(\|)`. +The default value is the `line.separator` system property, which is operating system dependent. + +[WARNING] +==== +Exception converter is not garbage-free. +==== + +[#converter-exception-extended] +==== Exception (Extended) + +The same as xref:#converter-exception[the `%throwable` conversion], but also includes class packaging information + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/ThrowablePatternConverter.html[`ThrowablePatternConverter`] specifier grammar +[source,text] +---- +xEx|xException|xThrowable + { "none" + | "full" + | depth + | "short" + | "short.className" + | "short.fileName" + | "short.lineNumber" + | "short.methodName" + | "short.message" + | "short.localizedMessage" + } + {filters(package,package,...)} + {suffix(pattern)} + {separator(separator)} +---- + +Different from xref:#converter-exception[the `%throwable` conversion], at the end of each stack element of the exception, a string containing the name of the JAR file that contains the class or the directory the class is located in and the `Implementation-Version` as found in that JAR's manifest will be added. +If the information is uncertain, then the class packaging data will be preceded by a `~` (tilde) character. + +[#converter-file] +==== File + +Outputs the file name where the logging request was issued + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/FileLocationPatternConverter.html[`FileLocationPatternConverter`] specifier grammar +[source,text] +---- +F +file +---- + +[WARNING] +==== +Capturing the source location information to generate the file name of the caller is an expensive operation, and is not garbage-free. +See xref:manual/layouts.adoc#LocationInformation[this section of the layouts page] for details. +==== + +[#converter-fqcn] +==== FQCN + +Outputs the fully qualified class name of the logger + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/LoggerFqcnPatternConverter.html[`LoggerFqcnPatternConverter`] specifier grammar +[source,text] +---- +fqcn +---- + +[#converter-highlight] +==== Highlight + +Adds ANSI colors to the result of the enclosed pattern based on the current event's xref:manual/customloglevels.adoc[logging level]. +Windows users should refer to xref:#jansi[]. + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/HighlightConverter.html[`HighlightConverter`] specifier grammar +[source,text] +---- +highlight{pattern}{style} +---- + +The default colors for each level are: + +[%header,cols="1m,1"] +|=== +|Level +|ANSI color + +|FATAL +|Bright red + +|ERROR +|Bright red + +|WARN +|Yellow + +|INFO +|Green + +|DEBUG +|Cyan + +|TRACE +|Black (looks dark grey) +|=== + +The color names are ANSI names defined in link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/AnsiEscape.html[`AnsiEscape`]. + +The color and attribute names are standard, but the exact shade, hue, or value. + +.Color table +|=== +|Intensity |0 |1 |2 |3 |4 |5 |6 |7 + +|Normal +|{set:cellbgcolor:black}Black +|{set:cellbgcolor:maroon}Red +|{set:cellbgcolor:green}Green +|{set:cellbgcolor:olive}Yellow +|{set:cellbgcolor:navy}Blue +|{set:cellbgcolor:purple}Magenta +|{set:cellbgcolor:teal}Cyan +|{set:cellbgcolor:silver}White + +|{set:cellbgcolor:white}Bright +|{set:cellbgcolor:gray}Black +|{set:cellbgcolor:red}Red +|{set:cellbgcolor:lime}Green +|{set:cellbgcolor:yellow}Yellow +|{set:cellbgcolor:blue}Blue +|{set:cellbgcolor:fuchsia}Magenta +|{set:cellbgcolor:cyan}Cyan +|{set:cellbgcolor:white}White +|=== + +You can use the default colors with: + +[source,text] +---- +%highlight{%d [%t] %-5level: %msg%n%throwable} +---- + +You can override the default colors in the optional `\{style}` option. +For example: + +[source,text] +---- +%highlight{%d [%t] %-5level: %msg%n%throwable}{FATAL=white, ERROR=red, WARN=blue, INFO=black, DEBUG=green, TRACE=blue} +---- + +You can highlight only the portion of the log event: + +[source,text] +---- +%d [%t] %highlight{%-5level: %msg%n%throwable} +---- + +You can style one part of the message and highlight the rest of the log event: + +[source,text] +---- +%style{%d [%t]}{black} %highlight{%-5level: %msg%n%throwable} +---- + +You can also use the `STYLE` key to use a predefined group of colors: + +[source,text] +---- +%highlight{%d [%t] %-5level: %msg%n%throwable}{STYLE=Logback} +---- + +The `STYLE` value can be one of: + +`Default`:: See above +`Logback`:: ++ +[%header,cols="1m,1"] +|=== +|Level |ANSI color + +|FATAL |Blinking bright red + +|ERROR |Bright red + +|WARN |Red + +|INFO |Blue + +|DEBUG |Normal + +|TRACE |Normal +|=== + +[#converter-level] +==== Level + +Outputs the xref:manual/customloglevels.adoc[level] of the log event + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/LevelPatternConverter.html[`LevelPatternConverter`] specifier grammar +[source,text] +---- +p|level{level=label, level=label, ...} +p|level{length=n} +p|level{lowerCase=true|false} +---- + +You provide a level name map in the form `level=value, level=value`, where the level is the name of the `Level and value is the value that should be displayed instead of the name of the `Level`. +For example: + +[source,text] +---- +%level{WARN=Warning, DEBUG=Debug, ERROR=Error, TRACE=Trace, INFO=Info} +---- + +Alternatively, for the compact-minded: + +[source,text] +---- +%level{WARN=W, DEBUG=D, ERROR=E, TRACE=T, INFO=I} +---- + +More succinctly, for the same result as above, you can define the length of the level label: + +[source,text] +---- +%level{length=1} +---- + +If the length is greater than a level name length, the layout uses the normal level name. + +You can combine the two kinds of options: + +[source,text] +---- +%level{ERROR=Error, length=2} +---- + +This gives you the `Error` level name and all other level names of length 2. + +Finally, you can output lower-case level names (the default is upper-case): + +[source,text] +---- +%level{lowerCase=true} +---- + +[#converter-line] +==== Line + +Outputs the line number from where the log request was issued + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/LineLocationPatternConverter.html[`LineLocationPatternConverter`] specifier grammar +[source,text] +---- +L +line +---- + +[WARNING] +==== +Capturing the source location information to generate the line number of the caller is an expensive operation, and is not garbage-free. +See xref:manual/layouts.adoc#LocationInformation[this section of the layouts page] for details. +==== + +[#converter-location] +==== Location + +Outputs location information of the caller which generates the logging event + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/LocationPatternConverter.html[`LocationPatternConverter`] specifier grammar +[source,text] +---- +l +location +---- + +The location information depends on the JVM implementation, but it usually consists of the fully qualified name of the calling method followed by the callers' file name and line number. + +[WARNING] +==== +Capturing the source location information of the caller is an expensive operation, and is not garbage-free. +See xref:manual/layouts.adoc#LocationInformation[this section of the layouts page] for details. +==== + +[#converter-logger] +==== Logger + +Outputs the name of the logger that published the log event + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/LoggerPatternConverter.html[`LoggerPatternConverter`] specifier grammar +[source,text] +---- +c{precision} +logger{precision} +---- + +By default, the layout prints the logger name in full. + +A logger conversion specifier can be optionally followed by a _precision specifier_, which consists of a decimal integer, or a pattern starting with a decimal integer. + +* When the precision specifier is an integer value, it reduces the size of the logger name. +If the number is positive, the layout prints the corresponding number of the rightmost logger name components. +If negative, the layout removes the corresponding number of leftmost logger name components. +* If the precision contains periods then the number before the first period identifies the length to be printed from items that precede tokens in the rest of the pattern. +If the number after the first period is followed by an asterisk it indicates how many of the rightmost tokens will be printed in full. +* If the precision contains any non-integer characters, then the layout abbreviates the name based on the pattern. +If the precision integer is less than one, the layout still prints the right-most token in full. + +See the table below for abbreviation examples: + +[%header,cols="1m,3m,3m"] +|=== +|Pattern +|Logger name +|Output + +|%c\{1} +|org.apache.commons.Foo +|Foo + +|%c\{2} +|org.apache.commons.Foo +|commons.Foo + +|%c\{10} +|org.apache.commons.Foo +|org.apache.commons.Foo + +|%c{-1} +|org.apache.commons.Foo +|apache.commons.Foo + +|%c{-2} +|org.apache.commons.Foo +|commons.Foo + +|%c{-10} +|org.apache.commons.Foo +|org.apache.commons.Foo + +|%c{1.} +|org.apache.commons.Foo +|o.a.c.Foo + +|%c{1.1.\~.~} +|org.apache.commons.test.Foo +|o.a.~.~.Foo + +|%c{.} +|org.apache.commons.test.Foo +|....Foo + +|%c{1.1.1.*} +|org.apache.commons.test.Foo +|o.a.c.test.Foo + +|%c{1.2.*} +|org.apache.commons.test.Foo +|o.a.c.test.Foo + +|%c{1.3.*} +|org.apache.commons.test.Foo +|o.a.commons.test.Foo + +|%c{1.8.*} +|org.apache.commons.test.Foo +|org.apache.commons.test.Foo +|=== + +[#converter-marker] +==== Marker + +Outputs the marker, if one is present + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/MarkerPatternConverter.html[`MarkerPatternConverter`] and .link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/MarkerSimpleNamePatternConverter.html[`MarkerSimpleNamePatternConverter`] specifiers' grammar +[source,text] +---- +marker +markerSimpleName +---- + +`marker` outputs the full name of the marker, including its parents. +Whereas, `markerSimpleName` outputs the simple name of the marker without its parents. + +[#converter-map] +==== Map + +Outputs the entries in a xref:manual/messages.adoc#MapMessage[`MapMessage`], if one is present in the event + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/MapPatternConverter.html[`MapPatternConverter`] specifier grammar +[source,text] +---- +K{key} +map{key} +MAP{key} +---- + +The `K` conversion character can be followed by the key for the map placed between braces, as in `%K\{clientNumber}`, where `clientNumber` is the key. +The value of the map corresponding to the key will be output. +If no additional sub-option is specified, then all map entries are output using a `{{key1,val1},{key2,val2}}` format. + +[#converter-max-len] +==== Max. length + +Outputs the result of evaluating the given pattern and truncating the result + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/MaxLegthConverter.html[`MaxLengthConverter`] specifier grammar +[source,text] +---- +maxLen{pattern}{length} +maxLength{pattern}{length} +---- + +If the length is greater than 20, then the output will contain a trailing ellipsis. +If the provided length is invalid, a default value of 100 is used. + +For instance, `%maxLen{%p: %c\{1} - %m%notEmpty{ =>%ex\{short}}}\{160}` will be limited to 160 characters with a trailing ellipsis. +`%maxLen{%m}\{20}` will be limited to 20 characters and no trailing ellipsis. + +[#converter-message] +==== Message + +Outputs the message associated with the log event + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/MessagePatternConverter.html[`MessagePatternConverter`] specifier grammar +[source,text] +---- +m{lookups}{ansi} +msg{lookups}{ansi} +message{lookups}{ansi} +---- + +Add `\{ansi}` to render messages with ANSI escape codes. +Windows users should refer to xref:#jansi[]. + +The default syntax for embedded ANSI codes is: + +[source,text] +---- +@\|code(,code)* text\|@ +---- + +For example, to render the message `Hello` in green, use: + +[source,text] +---- +@\|green Hello\|@ +---- + +To render the message `Hello` in bold and red, use: + +[source,text] +---- +@\|bold,red Warning!\|@ +---- + +You can also define custom style names in the configuration with the syntax: + +[source,text] +---- +%message{ansi}{StyleName=value(,value)*( StyleName=value(,value)*)*}%n +---- + +For example: + +[source,text] +---- +%message{ansi}{WarningStyle=red,bold KeyStyle=white ValueStyle=blue}%n +---- + +The call site can look like this: + +[source,text] +---- +logger.info("@\|KeyStyle {}\|@ = @\|ValueStyle {}\|@", entry.getKey(), entry.getValue()); +---- + +You can enable xref:manual/lookups.adoc[] using `\{lookups}` and log messages like `${date:YYYY-MM-dd}` using lookups. +This will replace the date template `YYYY-MM-dd` with an actual date. +This can be confusing in many cases, and it's often both easier and more obvious to handle the lookup in code. +This feature is disabled by default and the message string is logged untouched. + +[WARNING] +==== +Users are strongly discouraged from using the lookups option. +Doing so may allow uncontrolled user input containing lookups to take unintended actions. +==== + +[#converter-method] +==== Method + +Outputs the method name where the logging request was issued + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/MethodLocationPatternConverter.html[`MethodLocationPatternConverter`] specifier grammar +[source,text] +---- +M +method +---- + +[WARNING] +==== +Capturing the source location information to generate the method name of the caller is an expensive operation, and is not garbage-free. +See xref:manual/layouts.adoc#LocationInformation[this section of the layouts page] for details. +==== + +[#converter-nano] +==== Nanoseconds + +Outputs the result of `System.nanoTime()` at the time the log event was created + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/NanoTimePatternConverter.html[`NanoTimePatternConverter`] specifier grammar +[source,text] +---- +N +nano +---- + +[#converter-not-empty] +==== Not empty + +Outputs the result of evaluating the pattern, if and only if all variables in the pattern are not empty + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/VariablesNotEmptyReplacementConverter.html[`VariablesNotEmptyReplacementConverter`] specifier grammar +[source,text] +---- +variablesNotEmpty{pattern} +varsNotEmpty{pattern} +notEmpty{pattern} +---- + +For example: + +[source,text] +---- +%notEmpty{[%marker]} +---- + +[#converter-pid] +==== Process ID + +Outputs the process ID, if supported by the underlying platform + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/ProcessIdPatternConverter.html[`ProcessIdPatternConverter`] specifier grammar +[source,text] +---- +pid{defaultValue} +processId{defaultValue} +---- + +An optional `defaultValue` may be specified to be shown, if the platform does not support process IDs. + +[#converter-relative] +==== Relative + +Outputs the number of milliseconds elapsed since the JVM was started until the creation of the log event + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/RelativeTimePatternConverter.html[`RelativeTimePatternConverter`] specifier grammar +[source,text] +---- +r +relative +---- + +[#converter-repeat] +==== Repeat + +Produces a string containing the requested number of instances of the specified string + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/RepeatPatternConverter.html[`RepeatPatternConverter`] specifier grammar +[source,text] +---- +R{string}{count} +repeat{string}{count} +---- + +For example, `%repeat{\*}\{2}` will result in the string `**`. + +[#converter-replace] +==== Replace + +Replaces occurrences of a regular expression (`regex`) with its replacement (`substitution`) in the string resulting from the evaluation of the pattern + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/RegexReplacementConverter.html[`RegexReplacementConverter`] specifier grammar +[source,text] +---- +replace{pattern}{regex}{substitution} +---- + +For example, `%replace{%msg}{\s}{}` will remove all spaces contained in the event message. + +The pattern can be arbitrarily complex and in particular, can contain multiple conversion keywords. +For instance, `%replace{%logger %msg}{\.}{/}` will replace all dots in the logger or the message of the event with a forward slash. + +[#converter-rootException] +==== Root exception + +The same as xref:#converter-exception[the `exception` converter], but the stack trace is printed starting with the first exception in the causal chain that was thrown followed by each subsequent wrapping exception + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/RootThrowablePatternConverter.html[`RootThrowablePatternConverter`] specifier grammar +[source,text] +---- +rEx|rException|rThrowable + { + ["none" | "short" | "full" | depth] + [,filters(package,package,...)] + [,separator(separator)] + } + {ansi( + Key=Value,Value,... + Key=Value,Value,... + ...) + } + {suffix(pattern)} +---- + +The throwable conversion specifier can be followed by an option in the form `%rEx\{short}`, which will only output the first line of the `Throwable`, or `%rEx\{n}`, where the first `n` lines of the stack trace will be printed. + +Specifying `%rEx\{none}` or `%rEx\{0}` will suppress printing of the exception. + +Use `filters(packages)`, where `packages` is a list of package names to suppress matching stack frames from stack traces. + +Use a `separator` string to separate the lines of a stack trace, e.g., `separator(|)`. +The default value is the `line.separator` system property, which is platform dependent. + +Use `rEx{suffix(pattern)}` to add the output of `pattern` to the output only when there is a `Throwable` to print. + +[#converter-seq] +==== Sequence number + +Includes a sequence number that will be incremented in every event + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/SequenceNumberPatternConverter.html[`SequenceNumberPatternConverter`] specifier grammar +[source,text] +---- +sn +sequenceNumber +---- + +The counter is a static variable, so will only be unique within applications that share the same converter class object. + +[#converter-style] +==== Style + +Use ANSI escape sequences to style the result of the enclosed pattern. +Windows users should refer to xref:#jansi[]. + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/StyleConverter.html[`StyleConverter`] specifier grammar +[source,text] +---- +style{pattern}{ANSI style} +---- + +The style can consist of a comma-separated list of style names from the following table: + +[%header,cols="2m,3"] +|=== +|Style Name +|Description + +|Normal +|Normal display + +|Bright +|Bold + +|Dim +|Dimmed or faint characters + +|Underline +|Underlined characters + +|Blink +|Blinking characters + +|Reverse +|Reverse video + +|Hidden +| + +|Black or FG_Black +|Set the foreground color to black + +|Red or FG_Red +|Set the foreground color to red + +|Green or FG_Green +|Set the foreground color to green + +|Yellow or FG_Yellow +|Set the foreground color to yellow + +|Blue or FG_Blue +|Set the foreground color to blue + +|Magenta or FG_Magenta +|Set the foreground color to magenta + +|Cyan or FG_Cyan +|Set the foreground color to cyan + +|White or FG_White +|Set the foreground color to white + +|Default or FG_Default +|Set the foreground color to default (white) + +|BG_Black +|Set the background color to black + +|BG_Red +|Set the background color to red + +|BG_Green +|Set the background color to green + +|BG_Yellow +|Set the background color to yellow + +|BG_Blue +|Set the background color to blue + +|BG_Magenta +|Set the background color to magenta + +|BG_Cyan +|Set the background color to cyan + +|BG_White +|Set the background color to white +|=== + +For example: + +[source,text] +---- +%style{%d{ISO8601}}{black} %style{[%t]}{blue} %style{%-5level:}{yellow} %style{%msg%n%throwable}{green} +---- + +You can also combine styles: + +[source,text] +---- +%d %highlight{%p} %style{%logger}{bright,cyan} %C{1.} %msg%n +---- + +You can also use `%` with a color like `%black`, `%blue`, `%cyan`, and so on. +For example: + +[source,text] +---- +%black{%d{ISO8601}} %blue{[%t]} %yellow{%-5level:} %green{%msg%n%throwable} +---- + +[#converter-thread-context-stack] +==== Thread context stack + +Outputs the xref:manual/thread-context.adoc[] stack (aka. Nested Diagnostic Context or NDC) associated with the thread that generated the log event + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/NdcPatternConverter.html[`NdcPatternConverter`] specifiers grammar +[source,text] +---- +x +NDC +---- + +[#converter-thread-context-map] +==== Thread context map + +Outputs the xref:manual/thread-context.adoc[] map (aka. Mapped Diagnostic Context or MDC) associated with the thread that generated the log event + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/MdcPatternConverter.html[`MdcPatternConverter`] specifiers grammar +[source,text] +---- +X{key[,key2...]} +mdc{key[,key2...]} +MDC{key[,key2...]} +---- + +The *X* conversion character can be followed by one or more keys for the map placed between braces, as in `%X\{clientNumber}`, where `clientNumber` is the key. +The value in the MDC corresponding to the key will be output. + +If a list of keys is provided, such as `%X{name, number}`, then each key that is present in the thread context will be output using the format `{name=val1, number=val2}`. +The key/value pairs will be printed in the order they appear in the list. + +If no sub-options are specified then the entire contents of the MDC key-value pair set is output using a format `{key1=val1, key2=val2}`. +The key/value pairs will be printed in sorted order. + +[#converter-thread-id] +==== Thread ID + +Outputs the ID of the thread that generated the log event + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/ThreadIdPatternConverter.html[`ThreadIdPatternConverter`] specifiers grammar +[source,text] +---- +T +tid +threadId +---- + +[#converter-thread-name] +==== Thread name + +Outputs the name of the thread that generated the log event + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/ThreadNamePatternConverter.html[`ThreadNamePatternConverter`] specifier grammar +[source,text] +---- +t +tn +thread +threadName +---- + +[#converter-thread-priority] +==== Thread priority + +Outputs the priority of the thread that generated the log event + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/ThreadPriorityPatternConverter.html[`ThreadPriorityPatternConverter`] specifier grammar +[source,text] +---- +tp +threadPriority +---- + +[#converter-uuid] +==== UUID + +Includes either a random or a time-based UUID + +.link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/UuidConverter.html[`UuidConverter`] specifier grammar +[source,text] +---- +u{RANDOM|TIME} +uuid{RANDOM|TIME} +---- + +The time-based UUID is a Type 1 UUID generated using the MAC address of each host +To ensure uniqueness across multiple JVMs and/or class loaders on the same host, a random number between 0 and 16,384 will be associated with each instance of the UUID generator class, and included in each time-based UUID generated. +Because time-based UUIDs contain the MAC address and timestamp, they should be used with care. + +[#format-modifiers] +=== Format modifiers + +By default, the relevant information is output as is. +However, with the aid of format modifiers it is possible to change the minimum field width, the maximum field width, and justification. + +The optional format modifier is placed between the percent sign and the conversion character. + +The first optional format modifier is the _left justification flag_ which is just the `-` (minus) character. +Then comes the optional _minimum field width_ modifier. +This is a decimal constant that represents the minimum number of characters to output. +If the data item requires fewer characters, it is padded on either the left or the right until the minimum width is reached. +The default is to pad on the left (right justify), but you can specify right padding with the left justification flag. +The padding character is space. +If the data item is larger than the minimum field width, the field is expanded to accommodate the data. +The value is never truncated. +To use zeros as the padding character prepend the _minimum field width_ with a zero. + +This behavior can be changed using the _maximum field width_ modifier which is designated by a period followed by a decimal constant. +If the data item is longer than the maximum field, then the extra characters are removed from the _beginning_ of the data item and not from the end. +For example, if the maximum field width is eight and the data item is ten characters long, then the first two characters of the data item are dropped. +This behavior deviates from the `String#format()`, where truncation is done from the end. + +Truncation from the end is possible by appending a minus character right after the period. +In that case, if the maximum field width is eight and the data item is ten characters long, then the last two characters of the data item are dropped. + +Below are various format modifier examples for the category conversion specifier. + +[%header,cols="2m,2m,2m,2m,5"] +|=== +|Pattern +|Left justify +|Min. width +|Max. width +|Comment + +|%20c +|false +|20 +|none +|Left pad with spaces if the category name is +less than 20 characters long. + +|%-20c +|true +|20 +|none +|Right pad with spaces if the category name is +less than 20 characters long. + +|%.30c +|N/A +|none +|30 +|Truncate from the beginning if the category name is longer than 30 characters. + +|%20.30c +|false +|20 +|30 +|Left pad with spaces if the category name is +shorter than 20 characters. However, if the category name is longer than 30 +characters, then truncate from the beginning. + +|%-20.30c +|true +|20 +|30 +|Right pad with spaces if the category name is +shorter than 20 characters. However, if the category name is longer than 30 +characters, then truncate from the beginning. + +|%-20.-30c +|true +|20 +|30 +|Right pad with spaces if the category name is +shorter than 20 characters. However, if the category name is longer than 30 +characters, then truncate from the end. +|=== + +[#jansi] +=== ANSI styling on Windows + +ANSI escape sequences are supported natively on many platforms, but not by default on Windows. +To enable ANSI support add the http://jansi.fusesource.org/[Jansi] dependency to your application, and set xref:manual/systemproperties.adoc#log4j.skipJansi[the `log4j.skipJansi` system property] to `false`. +This allows Log4j to use Jansi to add ANSI escape codes when writing to the console. + +[NOTE] +==== +Before Log4j 2.10, Jansi was enabled by default. +The fact that Jansi requires native code means that Jansi can only be loaded by a single class loader. +For web applications, this means the Jansi jar has to be in the web container's classpath. +To avoid causing problems for web applications, Log4j no longer automatically tries to load Jansi without explicit configuration from Log4j 2.10 onward. +==== + +[#garbage-free] +=== Garbage-free configuration + +Pattern Layout with the following limited set of conversion patterns is garbage-free. +Format modifiers to control such things as field width, padding, left, and right justification will not generate garbage. + +[%header,cols="2m,2"] +|=== +|Pattern +|Comment + +|xref:#converter-logger[%c\{precision} + +%logger\{precision}] +| + +|xref:#converter-date[%d + +%date] +|Only the predefined date formats (`DEFAULT`, `ISO8601`, `UNIX`, `UNIX_MILLIS`, etc.) are garbage-free + +|xref:#converter-encode[%enc\{pattern} + +%encode\{pattern}] +| + +|xref:#converter-equals[%equals\{pattern}\{test}\{substitution} + +%equalsIgnoreCase\{pattern}\{test}\{substitution}] +| + +|xref:#converter-highlight[%highlight\{pattern}\{style}] +|Granted nested pattern is garbage-free + +|xref:#converter-map[%K\{key} + +%map\{key} + +%MAP\{key}] +| + +|xref:#converter-message[%m + +%msg + +%message] +|Garbage-free unless message text contains `${` + +|xref:#converter-marker[%marker] +| + +|xref:#converter-marker[%markerSimpleName] +| + +|xref:#converter-max-len[%maxLen + +%maxLength] +| + +|%n +| + +|xref:#converter-nano[%N + +%nano] +| + +|xref:#converter-not-empty[%notEmpty\{pattern} + +%varsNotEmpty\{pattern} + +%variablesNotEmpty\{pattern}] +| + +|xref:#converter-level[%p + +%level] +| + +|xref:#converter-relative[%r + +%relative] +| + +|xref:#converter-seq[%sn + +%sequenceNumber] +| + +|xref:#converter-style[%style\{pattern}{ANSI style}] +| + +|xref:#converter-thread-id[%T + +%tid + +%threadId] +| + +|xref:#converter-thread-name[%t + +%tn + +%thread + +%threadName] +| + +|xref:#converter-thread-priority[%tp] +| + +|xref:#converter-thread-context-map[%X{key[,key2...\]} + +%mdc{key[,key2...\]} + +%MDC{key[,key2...\]}] +| + +|literal text +|Garbage-free unless it contains `${` +|=== + +[NOTE] +==== +Patterns containing + +* regular expressions +* lookups +* xref:manual/layouts.adoc#LocationInformation[location information] + +are not garbage-free. +==== + +:plugin-extension-intro-component-name: Pattern Layout +include::partial$manual/plugin-extension-intro.adoc[tag=part1] +As of this moment, only xref:#extending-converters[extending pattern converters] is supported. +include::partial$manual/plugin-extension-intro.adoc[tag=part2] + +[#extending-converters] +=== Pattern converters + +xref:manual/plugins.adoc[Plugins] implementing link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/PatternConverter.html[`PatternConverter`] are admitted to the *pattern converter* registry of Pattern Layout, and used to resolve the conversion specifiers. +You can leverage this mechanism to introduce your custom pattern converters next to xref:#converters[the predefined ones]. + +A `PatternConverter` must first declare itself as a xref:manual/plugins.adoc[plugin] using the standard `@Plugin` annotation, and the `@Namespace` annotation with the value `Converter`. +Furthermore, the converter must also specify the `@ConverterKeys` annotation to define the conversion specifiers, which will preceded by a `%` character when used in a pattern. + +[IMPORTANT] +==== +Unlike most other plugins, **pattern converters do not use a `@PluginFactory`**. +Instead, each converter is required to provide a static `newInstance(String[])` factory method. +The `String[]` argument denots the the values that are specified within the curly braces that following the conversion specifier. +==== + +Refer to following sources for simple examples: + +* {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/EndOfBatchPatternConverter.java[`EndOfBatchPatternConverter.java`] +* {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/UuidPatternConverter.java[`UuidPatternConverter.java`] + +[#performance] +== Performance + +Great effort has been put into the efficiency of Pattern Layout. +To get the most out of it, mind the following checklist: + +* Enable xref:manual/garbagefree.adoc[garbage-free logging] +* Mind xref:#garbage-free[the garbage footprint of features you use] +* Don't give too much slack to xref:manual/systemproperties.adoc#log4j2.layoutStringBuilderMaxSize[`log4j2.layoutStringBuilderMaxSize`] and try to keep it relatively tight diff --git a/src/site/antora/modules/ROOT/pages/manual/performance.adoc b/src/site/antora/modules/ROOT/pages/manual/performance.adoc index c9234574d56..e3777fc56fb 100644 --- a/src/site/antora/modules/ROOT/pages/manual/performance.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/performance.adoc @@ -134,7 +134,7 @@ xref:manual/json-template-layout.adoc#performance[JSON Template Layout]:: It encodes log events into JSON according to the structure described by the template provided. Its output can safely be ingested into several log storage solutions: Elasticsearch, Google Cloud Logging, Graylog, Logstash, etc. -xref:manual/layouts.adoc#PatternLayout-performance[Pattern Layout]:: +xref:manual/pattern-layout.adoc#performance[Pattern Layout]:: It encodes log events into human-readable text according to the pattern provided. [#async] diff --git a/src/site/antora/modules/ROOT/partials/manual/plugin-extension-intro.adoc b/src/site/antora/modules/ROOT/partials/manual/plugin-extension-intro.adoc new file mode 100644 index 00000000000..549f0a4a867 --- /dev/null +++ b/src/site/antora/modules/ROOT/partials/manual/plugin-extension-intro.adoc @@ -0,0 +1,39 @@ +//// + 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. +//// + +// tag::part1[] +[#extending] +== Extending + +{plugin-extension-intro-component-name} relies on xref:manual/plugins.adoc[the Log4j plugin system] to compose the features it provides. +This makes it possible for users to extend the plugin-based feature set as they see fit. +// end::part1[] +// tag::part2[] +Following sections cover these in detail. + +[#extending-plugins] +=== Plugin preliminaries + +Log4j plugin system is the de facto extension mechanism embraced by various Log4j components, including {plugin-extension-intro-component-name}. +Plugins make it possible for extensible components to _receive_ feature implementations without any explicit links in between. +It is analogous to a https://en.wikipedia.org/wiki/Dependency_injection[dependency injection] framework, but curated for Log4j-specific needs. + +In a nutshell, you annotate your classes with `@Plugin` and their (`static`) factory methods with `@PluginFactory`. +Last, you inform the Log4j plugin system to discover these custom classes. +This is done using running the `org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor` annotation processor while building your project. +Refer to xref:manual/plugins.adoc[] for details. +// end::part2[] diff --git a/src/site/resources/.htaccess b/src/site/resources/.htaccess index 8194fd29b55..4490f1f6797 100644 --- a/src/site/resources/.htaccess +++ b/src/site/resources/.htaccess @@ -54,6 +54,45 @@ RewriteRule "^log4j-slf4j-impl\.html$" "manual/installation.html#impl-core-bridg RewriteRule "^log4j-slf4j2-impl/index\.html$" "manual/installation.html#impl-core-bridge-slf4j" [R=permanent,NE] RewriteRule "^log4j-slf4j2-impl\.html$" "manual/installation.html#impl-core-bridge-slf4j" [R=permanent,NE] RewriteRule "^manual/api-separation\.html$" "manual/api.html" [R=permanent] +RewriteRule "^manual/extending\.html#PatternConverters$" "manual/pattern-layout.html#extending-converters" [R=permanent] +RewriteRule "^manual/layouts\.html#enable-jansi$" "manual/pattern-layout.html#jansi" [R=permanent] +RewriteRule "^manual/layouts\.html#EndOfBatch$" "manual/pattern-layout.html#converter-end-of-batch" [R=permanent] +RewriteRule "^manual/layouts\.html#LevelPatternSelector$" "manual/pattern-layout.html#plugin-element-LevelPatternSelector" [R=permanent] +RewriteRule "^manual/layouts\.html#MarkerPatternSelector$" "manual/pattern-layout.html#plugin-element-MarkerPatternSelector" [R=permanent] +RewriteRule "^manual/layouts\.html#(PatternLayout|pattern-layout)$" "manual/pattern-layout.html" [R=permanent] +RewriteRule "^manual/layouts\.html#Patterns$" "manual/pattern-layout.html#converters" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternClass$" "manual/pattern-layout.html#converter-class" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternDate$" "manual/pattern-layout.html#converter-date" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternException$" "manual/pattern-layout.html#converter-exception" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternExtendedException$" "manual/pattern-layout.html#converter-exception-extended" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternFile$" "manual/pattern-layout.html#converter-file" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternLevel$" "manual/pattern-layout.html#converter-level" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternLine$" "manual/pattern-layout.html#converter-line" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternLocation$" "manual/pattern-layout.html#converter-location" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternLoggerFqcn$" "manual/pattern-layout.html#converter-fqcn" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternMap$" "manual/pattern-layout.html#converter-map" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternMarker(SimpleName)?$" "manual/pattern-layout.html#converter-marker" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternMaxLength$" "manual/pattern-layout.html#converter-max-len" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternMDC$" "manual/pattern-layout.html#converter-thread-context-map" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternMessage$" "manual/pattern-layout.html#converter-message" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternMethod$" "manual/pattern-layout.html#converter-method" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternNDC$" "manual/pattern-layout.html#converter-thread-context-stack" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternPercentLiteral$" "manual/pattern-layout.html#usage" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternRelative$" "manual/pattern-layout.html#converter-relative" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternRepeat$" "manual/pattern-layout.html#converter-repeat" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternReplace$" "manual/pattern-layout.html#converter-replace" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternSelectors$" "manual/pattern-layout.html#plugin-element-PatternSelector" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternSequenceNumber$" "manual/pattern-layout.html#converter-seq" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternStyle$" "manual/pattern-layout.html#converter-style" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternThreadId$" "manual/pattern-layout.html#converter-thread-id" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternThreadName$" "manual/pattern-layout.html#converter-thread-name" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternThreadPriority$" "manual/pattern-layout.html#converter-thread-priority" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternUUID$" "manual/pattern-layout.html#converter-uuid" [R=permanent] +RewriteRule "^manual/layouts\.html#NanoTime$" "manual/pattern-layout.html#converter-nano" [R=permanent] +RewriteRule "^manual/layouts\.html#PatternNewLine$" "manual/pattern-layout.html#usage" [R=permanent] +RewriteRule "^manual/layouts\.html#Process_ID$" "manual/pattern-layout.html#converter-pid" [R=permanent] +RewriteRule "^manual/layouts\.html#ScriptPatternSelector$" "manual/pattern-layout.html#plugin-element-ScriptPatternSelector" [R=permanent] +RewriteRule "^manual/layouts\.html#VariablesNotEmpty$" "manual/pattern-layout.html#converter-not-empty" [R=permanent] RewriteRule "^manual/scala-api\.html$" "/log4j/scala/latest/index.html" [R=permanent] RewriteRule "^manual/usage\.html$" "manual/api.html" [R=permanent] RewriteRule "^runtime-dependencies\.html$" "manual/installation.html" [R=permanent] From f3879ab0c75464069feea98f88fe314725e0ca2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Thu, 6 Jun 2024 13:41:34 +0200 Subject: [PATCH 04/53] Rewrite layouts in `layouts.adoc` --- .../examples/manual/gelf-layout/log4j2.json | 68 ++ .../manual/gelf-layout/log4j2.properties | 53 + .../examples/manual/gelf-layout/log4j2.xml | 53 + .../examples/manual/gelf-layout/log4j2.yaml | 61 ++ .../pages/manual/json-template-layout.adoc | 21 +- .../modules/ROOT/pages/manual/layouts.adoc | 924 +++++++++++------- .../ROOT/pages/manual/pattern-layout.adoc | 20 +- .../manual/dependencies-log4j-csv.adoc | 38 - .../dependencies-log4j-layout-json.adoc | 38 - ...pendencies-log4j-layout-template-json.adoc | 38 - .../manual/dependencies-log4j-layout-xml.adoc | 38 - .../dependencies-log4j-layout-yaml.adoc | 38 - .../manual/layouts-location-information.adoc | 2 +- ...n-intro.adoc => plugin-preliminaries.adoc} | 20 +- 14 files changed, 823 insertions(+), 589 deletions(-) create mode 100644 src/site/antora/modules/ROOT/examples/manual/gelf-layout/log4j2.json create mode 100644 src/site/antora/modules/ROOT/examples/manual/gelf-layout/log4j2.properties create mode 100644 src/site/antora/modules/ROOT/examples/manual/gelf-layout/log4j2.xml create mode 100644 src/site/antora/modules/ROOT/examples/manual/gelf-layout/log4j2.yaml delete mode 100644 src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-csv.adoc delete mode 100644 src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-layout-json.adoc delete mode 100644 src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-layout-template-json.adoc delete mode 100644 src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-layout-xml.adoc delete mode 100644 src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-layout-yaml.adoc rename src/site/antora/modules/ROOT/partials/manual/{plugin-extension-intro.adoc => plugin-preliminaries.adoc} (62%) diff --git a/src/site/antora/modules/ROOT/examples/manual/gelf-layout/log4j2.json b/src/site/antora/modules/ROOT/examples/manual/gelf-layout/log4j2.json new file mode 100644 index 00000000000..06f037f77a1 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/gelf-layout/log4j2.json @@ -0,0 +1,68 @@ +{ + "Configuration": { + "Appenders": { + + "Console": { + "name": "CONSOLE", + "GelfLayout": { //<1> + "messagePattern": "%d %5p [%t] %c{1} %X{loginId, requestId} - %m%n", + "includeThreadContext": "true", + "threadContextIncludes": "loginId,requestId", + "KeyValuePair": [ + { + "key": "additionalField1", + "value": "constant value" + }, + { + "key": "additionalField2", + "value": "$${ctx:key}" + } + ] + } + }, + + "SocketTcp": { //<2> + "type": "Socket", + "name": "GRAYLOG_TCP", + "protocol": "TCP", + "host": "graylog.domain.com", + "port": 12201, + "GelfLayout": { + "host": "someserver", + "compressionType": "OFF", + "includeNullDelimiter": "true" + } + }, + + "SocketUdp": { //<3> + "type": "Socket", + "name": "GRAYLOG_UDP", + "protocol": "UDP", + "host": "graylog.domain.com", + "port": 12201, + "GelfLayout": { + "host": "someserver", + "compressionType": "ZLIB", + "compressionThreshold": 1024 + } + } + + }, + "Loggers": { + "Root": { + "level": "WARN", + "AppenderRef": [ + { + "ref": "CONSOLE" + }, + { + "ref": "GRAYLOG_TCP" + }, + { + "ref": "GRAYLOG_UDP" + } + ] + } + } + } +} diff --git a/src/site/antora/modules/ROOT/examples/manual/gelf-layout/log4j2.properties b/src/site/antora/modules/ROOT/examples/manual/gelf-layout/log4j2.properties new file mode 100644 index 00000000000..42ea05fd01f --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/gelf-layout/log4j2.properties @@ -0,0 +1,53 @@ +# +# 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. +# +appender.0.type = Console #<1> +appender.0.name = CONSOLE +appender.0.layout.type = GelfLayout +appender.0.layout.messagePattern = %d %5p [%t] %c{1} %X{loginId, requestId} - %m%n +appender.0.layout.includeThreadContext = true +appender.0.layout.threadContextIncludes = true +appender.0.layout.KeyValuePair.0.type = KeyValuePair +appender.0.layout.KeyValuePair.0.key = additionalField1 +appender.0.layout.KeyValuePair.0.value = constant value +appender.0.layout.KeyValuePair.1.type = KeyValuePair +appender.0.layout.KeyValuePair.1.key = additionalField2 +appender.0.layout.KeyValuePair.1.value = $${ctx:key} + +appender.1.type = Socket #<2> +appender.1.name = GRAYLOG_TCP +appender.1.protocol = TCP +appender.1.host = graylog.domain.com +appender.1.port = 12201 +appender.1.layout.type = GelfLayout +appender.1.layout.host = someserver +appender.1.layout.compressionType = OFF +appender.1.layout.includeNullDelimiter = true + +appender.2.type = Socket #<3> +appender.2.name = GRAYLOG_UDP +appender.2.protocol = UDP +appender.2.host = graylog.domain.com +appender.2.port = 12201 +appender.2.layout.type = GelfLayout +appender.2.layout.host = someserver +appender.2.layout.compressionType = ZLIB +appender.2.layout.compressionThreshold = 1024 + +rootLogger.level = WARN +rootLogger.appenderRef.0.ref = CONSOLE +rootLogger.appenderRef.1.ref = GRAYLOG_TCP +rootLogger.appenderRef.2.ref = GRAYLOG_UDP diff --git a/src/site/antora/modules/ROOT/examples/manual/gelf-layout/log4j2.xml b/src/site/antora/modules/ROOT/examples/manual/gelf-layout/log4j2.xml new file mode 100644 index 00000000000..e0fa0cf85b8 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/gelf-layout/log4j2.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/site/antora/modules/ROOT/examples/manual/gelf-layout/log4j2.yaml b/src/site/antora/modules/ROOT/examples/manual/gelf-layout/log4j2.yaml new file mode 100644 index 00000000000..01ca630368b --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/gelf-layout/log4j2.yaml @@ -0,0 +1,61 @@ +# +# 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. +# +Configuration: + + Appenders: + + Console: + name: "CONSOLE" + GelfLayout: #<1> + messagePattern: "%d %5p [%t] %c{1} %X{loginId, requestId} - %m%n" + includeThreadContext: "true" + threadContextIncludes: "loginId,requestId" + KeyValuePair: + - key: "additionalField1" + value: "constant value" + - key: "additionalField2" + value: "$${ctx:key}" + + SocketTcp: #<2> + type: "Socket" + name: "GRAYLOG_TCP" + protocol: "TCP" + host: "graylog.domain.com" + port: 12201 + GelfLayout: + host: "someserver" + compressionType: "OFF" + includeNullDelimiter: "true" + + SocketUdp: #<3> + type: "Socket" + name: "GRAYLOG_UDP" + protocol: "UDP" + host: "graylog.domain.com" + port: 12201 + GelfLayout: + host: "someserver" + compressionType: "ZLIB" + compressionThreshold: 1024 + + Loggers: + Root: + level: "WARN" + AppenderRef: + - ref: "CONSOLE" + - ref: "GRAYLOG_TCP" + - ref: "GRAYLOG_UDP" diff --git a/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc b/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc index d1ad77494a1..00e746b962f 100644 --- a/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc @@ -1675,14 +1675,29 @@ The effective recycler factory can be configured using the xref:#plugin-attr-rec Next to predefined ones, you can introduce custom `RecyclerFactory` implementations too. See <> for details. -:plugin-extension-intro-component-name: JSON Template Layout -include::partial$manual/plugin-extension-intro.adoc[tag=part1] +[#extending] +== Extending + +JSON Template Layout relies on xref:manual/plugins.adoc[the Log4j plugin system] to compose the features it provides. +This makes it possible for users to extend the plugin-based feature set as they see fit. As of this moment, following features are implemented by means of plugins: * Event template resolvers (e.g., `exception`, `message`, `level` event template resolvers) * Event template interceptors (e.g., injection of `eventTemplateAdditionalField`) * Recycler factories -include::partial$manual/plugin-extension-intro.adoc[tag=part2] + +Following sections cover how to extend these in detail. + +[NOTE] +==== +While existing features should address most common use cases, you might find yourself needing to implement a custom one. +If this is the case, we really appreciate it if you can *share your use case in a {logging-services-url}/support.html[user support channel]*. +==== + +[#extending-plugins] +=== Plugin preliminaries + +include::partial$manual/plugin-preliminaries.adoc[] [#extending-event-resolvers] === Extending event template resolvers diff --git a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc index 66be951a8be..5dffde3add4 100644 --- a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc @@ -14,157 +14,200 @@ See the License for the specific language governing permissions and limitations under the License. //// -= Layouts -An Appender uses a Layout to format a LogEvent into a form that meets the needs of whatever will be consuming the log event. -In Log4j 1.x and Logback Layouts were expected to transform an event into a String. -In Log4j 2 Layouts return a byte array. -This allows the result of the Layout to be useful in many more types of Appenders. -However, this means you need to configure most Layouts with a -https://docs.oracle.com/javase/6/docs/api/java/nio/charset/Charset.html[`Charset`] -to ensure the byte array contains correct values. += Layouts -The root class for layouts that use a Charset is -`org.apache.logging.log4j.core.layout.AbstractStringLayout` where the default is UTF-8. Each layout that extends `AbstractStringLayout` can provide its own default. -See each layout below. +An xref:manual/appenders.adoc[appender] uses a *layout* to encode a link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html[`LogEvent`] into a form that meets the needs of whatever will be consuming the log event. +This page will try to answer following questions: -A custom character encoder was added to Log4j 2.4.1 for the ISO-8859-1 and US-ASCII charsets, to bring some of the performance improvements built-in to Java 8 to Log4j for use on Java 7. For applications that log only ISO-8859-1 characters, specifying this charset will improve performance significantly. +* xref:#concerns[What are some common concerns shared by all predefined layouts?] +* xref:#collection[Which layouts does Log4j provide?] +* xref:#extending[How can you create custom layouts?] -[#common-concerns] +[#concerns] == Common concerns +This section introduces you to some common concerns shared by almost all xref:#collection[predefined layouts] that you need to be aware of while using them. + +[#charset] +=== Character encoding + +All xref:#collection[predefined layouts] produce `String` that eventually get converted into a `byte[]` using the https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html[`Charset`] configured. +While doing so, they use `UTF-8` by default. +If you want all your log events to be formatted in a certain character encoding that is different than what the employed layout defaults to, make sure to configure the layout's character encoding as needed. + [#LocationInformation] === Location information include::partial$manual/layouts-location-information.adoc[] -[#CSVLayouts] -== CSV Layouts +[#collection] +== Collection + +Log4j bundles predefined layouts to assist in several common deployment use cases. +Let's start with shortcuts to most used ones: + +* Are you looking for a production-grade *JSON layout ready to be deployed to a log ingestion system* such as Elasticsearch or Google Cloud? +Refer to xref:manual/json-template-layout.adoc[]. + +* Are you looking for a layout that encodes log events in a *human-readable format suitable for tests and local development*? +Refer to xref:manual/pattern-layout.adoc[]. -As of Log4j 2.11.0, CSV support has moved from the existing module -`log4j-core` to the new module `log4j-csv`. +Following sections explain all predefined layouts in detail. -This layout creates -https://en.wikipedia.org/wiki/Comma-separated_values[Comma Separated -Value (CSV)] records and requires -https://commons.apache.org/proper/commons-csv/[Apache Commons CSV] 1.4. +[#csv-layouts] +=== [[CSVLayouts]] CSV Layouts -The CSV layout can be used in two ways: First, using -`CsvParameterLayout` to log event parameters to create a custom database, usually to a logger and file appender uniquely configured for this purpose. -Second, using `CsvLogEventLayout` to log events to create a database, as an alternative to using a full DBMS or using a JDBC driver that supports the CSV format. +There are two layouts performing https://en.wikipedia.org/wiki/Comma-separated_values[Comma Separated Value (CSV)] encoding: -The `CsvParameterLayout` converts an event's parameters into a CSV record, ignoring the message. -To log CSV records, you can use the usual Logger methods `info()`, `debug()`, and so on: +link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/CsvParameterLayout.html[`CsvParameterLayout`]:: +It encodes *only* the parameters of the message of a log event. +Generated CSV records will be composed of fields denoting the message parameters. ++ +.Click here for examples +[%collapsible] +==== +Given the following statement [source,java] ---- -logger.info("Ignored", value1, value2, value3); +LOGGER.info("Record1 {} {}", "arg1", "arg2"); +LOGGER.error("Record2 {} {} {}", "arg3", "arg4", "arg5", throwable); +---- + +`CsvParameterLayout` will output + +[source,text] +---- +arg1,arg2 +arg3,arg4,arg5 ---- -Which will create the CSV record: +The same can be achieved using link:../javadoc/log4j-api/org/apache/logging/log4j/message/ObjectArrayMessage.html[`ObjectArrayMessage`] as follows: + +[source,java] +---- +LOGGER.info(new ObjectArrayMessage("arg1", "arg2")); +LOGGER.info(new ObjectArrayMessage("arg3", "arg4", "arg5")); +---- +==== -.... -value1, value2, value3 -.... +link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/CsvLogEventLayout.html[`CsvLogEventLayout`]:: +It encodes the complete log event, including the formatted message. +Generated CSV records will be composed of following fields in the given order: ++ +. Time (in nanoseconds) +. Time (in milliseconds) +. Level +. Thread ID +. Thread name +. Thread priority +. Message (formatted, hence including parameters) +. Logger FQCN +. Logger name +. Marker +. Throwable +. Source +. Thread context map +. Thread context stack -Alternatively, you can use an `ObjectArrayMessage`, which only carries parameters: ++ +.Click here for examples +[%collapsible] +==== +Given the following statement [source,java] ---- -logger.info(new ObjectArrayMessage(value1, value2, value3)); +LOGGER.debug("one={}, two={}, three={}", 1, 2, 3); +---- + +`CsvLogEventLayout` will output + +[source,text] +---- +0,1441617184044,DEBUG,main,"one=1, two=2, three=3",org.apache.logging.log4j.spi.AbstractLogger,,,,org.apache.logging.log4j.core.layout.CsvLogEventLayoutTest.testLayout(CsvLogEventLayoutTest.java:98),{},[] ---- +==== -The layouts `CsvParameterLayout` and `CsvLogEventLayout` are configured with the following parameters: +Both `CsvParameterLayout` and `CsvLogEventLayout` are configured with the following parameters: -.CsvParameterLayout and CsvLogEventLayout -[cols="1m,1,4"] +[%header,cols="1m,1m,4"] |=== -|Parameter Name |Type |Description +|Parameter +|Type +|Description |format |String -|One of the predefined formats: `Default`, `Excel`, -`MySQL`, `RFC4180`, `TDF`. See -https://commons.apache.org/proper/commons-csv/archives/1.4/apidocs/org/apache/commons/csv/CSVFormat.Predefined.html[CSVFormat.Predefined]. +|A predefined format name (`Default`, `Excel`, `MySQL`, `RFC4180`, `TDF`, etc.) accepted by https://commons.apache.org/proper/commons-csv/apidocs/org/apache/commons/csv/CSVFormat.html[`CSVFormat`] |delimiter |Character -|Sets the delimiter of the format to the specified character. +|The field delimiter character |escape |Character -|Sets the escape character of the format to the specified character. +|The escape character |quote |Character -|Sets the quoteChar of the format to the specified -character. +|The quote character |quoteMode |String -|Sets the output quote policy of the format to the -specified value. One of: `ALL`, `MINIMAL`, `NON_NUMERIC`, `NONE`. +|A quote mode name (`ALL`, `ALL_NONE_NULL`, `MINIMAL`, `NON_NUMERIC`, `NONE`, etc.) accepted by https://commons.apache.org/proper/commons-csv/apidocs/org/apache/commons/csv/QuoteMode.html[`QuoteMode`] |nullString |String -|Writes null as the given nullString when writing records. +|The string to denote `null` values |recordSeparator |String -|Sets the record separator of the format to the specified String. +|The record separator string |charset |Charset -|The output Charset. +|The character encoding |header -|Sets the header to include when the stream is opened. -|Desc. +|String +|The header to include when the stream is opened |footer -|Sets the footer to include when the stream is closed. -|Desc. +|String +|The footer to include when the stream is closed |=== -Logging as a CSV event looks like this: +There are additional runtime dependencies are required for using CSV layouts: -[source,java] +[tabs] +==== +Maven:: ++ +[source,xml,subs="+attributes"] ---- -logger.debug("one={}, two={}, three={}", 1, 2, 3); + + org.apache.commons + commons-csv + {commons-csv-version} + runtime + ---- -Produces a CSV record with the following fields: - -1. Time Nanos -2. Time Millis -3. Level -4. Thread ID -5. Thread Name -6. Thread Priority -7. Formatted Message -8. Logger FQCN -9. Logger Name -10. Marker -11. Thrown Proxy -12. Source -13. Context Map -14. Context Stack - -.... -0,1441617184044,DEBUG,main,"one=1, two=2, three=3",org.apache.logging.log4j.spi.AbstractLogger,,,,org.apache.logging.log4j.core.layout.CsvLogEventLayoutTest.testLayout(CsvLogEventLayoutTest.java:98),{},[] -.... - -.Additional runtime dependencies are required for using CSV layouts -[%collapsible,id=log4j-csv-deps] -===== -include::partial$manual/dependencies-log4j-csv.adoc[] -===== +Gradle:: ++ +[source,groovy,subs="+attributes"] +---- +runtimeOnly 'org.apache.commons:commons-csv:{commons-csv-version}' +---- +==== [#GELFLayout] -== GELF Layout +=== GELF Layout -Encodes log events in https://go2docs.graylog.org/current/getting_in_log_data/gelf.html#GELFPayloadSpecification[the GELF specification] version `1.1`. -Compresses JSON to GZIP or ZLIB (`compressionType`) if log event data is larger than 1024 bytes (`compressionThreshold`). +GELF Layout encodes log events in https://go2docs.graylog.org/current/getting_in_log_data/gelf.html#GELFPayloadSpecification[the GELF specification] version `1.1`. +It can compress the output when it exceeds a certain threshold. This layout does not implement chunking. [TIP] @@ -172,127 +215,193 @@ This layout does not implement chunking. Unless compression is needed, we advise you to use xref:manual/json-template-layout.adoc[JSON Template Layout] instead, which provides GELF layout support out of the box and offers a far richer set of features. ==== -Configure as follows to send to a Graylog 2.x server with UDP: +GELF Layout is configured with the following parameters: -[source,xml] ----- - - - - - ----- +[%header,cols="1m,1m,4"] +|=== +|Parameter +|Type +|Description -Configure as follows to send to a Graylog 2.x server with TCP: +|additionalFields +|link:../javadoc/log4j-core/org/apache/logging/log4j/core/util/KeyValuePair.html[KeyValuePair[\]] +|link:../javadoc/log4j-core/org/apache/logging/log4j/core/util/KeyValuePair.html[`KeyValuePair`] elements denoting additional entries. +Entry values can contain xref:manual/lookups.adoc[] using `${` syntax. -[source,xml] ----- - - - - - ----- +|compressionThreshold +|int +|Triggers compression if the output is larger than this number of bytes (optional, defaults to 1024) -[options="header"] -|=== -| Parameter Name | Type | Description -| host | String | The value of the `host` property (optional, defaults to local host name). -| compressionType | `GZIP`, `ZLIB` or `OFF` | Compression to use (optional, defaults to `GZIP`). -| compressionThreshold | int | Compress if data is larger than this number of bytes (optional, defaults to 1024). -| includeMapMessage | boolean | Whether to include fields from MapMessages as additional fields (optional, default to true). -| includeNullDelimiter | boolean | Whether to include NULL byte as delimiter after each event (optional, default to false). Useful for Graylog GELF TCP input. Cannot be used with compression. -| includeStacktrace | boolean | Whether to include full stacktrace of logged Throwables (optional, default to true). If set to false, only the class name and message of the Throwable will be included. -| includeThreadContext | boolean | Whether to include thread context as additional fields (optional, default to true). -| mapMessageExcludes | String | A comma separated list of attributes from the MapMessage to exclude when formatting the event. This attribute only applies when includeMapMessage="true" is specified. If mapMessageIncludes are also specified this attribute will be ignored. -| mapMessageIncludes | String | A comma separated list of attributes from the MapMessage to include when formatting the event. This attribute only applies when includeMapMessage="true" is specified. If mapMessageExcludes are also specified this attribute will override them. MapMessage fields specified here that have no value will be omitted. -| mapPrefix | String | A String to prepend to all elements of the MapMessage when rendered as a field. Defaults to an empty String. -| [[GELFLayout-messagePattern]] messagePattern -| String -| The pattern to use to format the `String`. +|compressionType +|String +|The compression to use. +It is optional, and defaults to `GZIP`. +Accepted values are `GZIP`, `ZLIB`, or `OFF`. + +|host +|String +|The value of the `host` property (optional, defaults to the local host name) + +|includeMapMessage +|boolean +|Whether to include fields from xref:manual/messages.adoc#MapMessage[`MapMessage`]s as additional fields (optional, defaults to `true`) + +|includeNullDelimiter +|boolean +|Whether to include `NULL` byte as delimiter after each event. +Optional and defaults to `false`. +Useful for Graylog GELF TCP input. +Cannot be used with compression. + +|includeStacktrace +|boolean +|Whether to include full stacktrace of logged ``Throwable``s. +Optional and defaults to `true`. +If set to `false`, only the class name and message of the `Throwable` will be included. + +|includeThreadContext +|boolean +|Whether to include thread context as additional fields (optional, defaults to `true`) + +|mapMessageExcludes +|String +|A comma separated list of attributes from the xref:manual/messages.adoc#MapMessage[`MapMessage`] to exclude when formatting the event. +This attribute only applies when `includeMapMessage` is `true`. +If `mapMessageIncludes` are also specified, this attribute will be ignored. + +|mapMessageIncludes +|String +|A comma separated list of attributes from the xref:manual/messages.adoc#MapMessage[`MapMessage`] to include when formatting the event. +This attribute only applies when `includeMapMessage` is `true`. +If `mapMessageExcludes` are also specified, this attribute will override them. +`MapMessage` fields specified here that have no value will be omitted. + +|mapPrefix +|String +|A string to prepend to all elements of the xref:manual/messages.adoc#MapMessage[`MapMessage`] when rendered as a field (optional, defaults to an empty string) + +|[[GELFLayout-messagePattern]] messagePattern +|String +|The pattern to use to format the `String`. A `messagePattern` and xref:#GELFLayout-patternSelector[`patternSelector`] cannot both be specified. If both are present, the message pattern will be ignored and an error will be logged. If not supplied, only the text derived from the log message will be used. See xref:manual/pattern-layout.adoc[] for information on the pattern strings. -| omitEmptyFields | boolean | If true fields which are null or are zero-length strings will not be included as a field in the Gelf JSON. This setting will not affect whether those fields appear in the message fields. The default value is false. -| [[GELFLayout-patternSelector]] patternSelector -| xref:manual/pattern-layout.adoc#plugin-element-PatternSelector[PatternSelector] -| The `PatternSelector` to use to format the `String`. + +|omitEmptyFields +|boolean +|If `true`, fields which are null or are zero-length strings will not be included as a field in the produced JSON. +This setting will not affect if those fields appear in the message fields. +The default value is `false`. + +|[[GELFLayout-patternSelector]] patternSelector +|xref:manual/pattern-layout.adoc#plugin-element-PatternSelector[PatternSelector] +|The `PatternSelector` to use to format the `String`. A xref:#GELFLayout-messagePattern[`messagePattern`] and `patternSelector` cannot both be specified. If both are present, the message pattern will be ignored and an error will be logged. If not supplied, only the text derived from the logging message will be used. See xref:manual/pattern-layout.adoc#plugin-element-PatternSelector[`PatternSelector`] for information on how to specify a `PatternSelector`. See xref:manual/pattern-layout.adoc[] for information on the pattern strings. -| threadContextExcludes | String | A comma separated list of ThreadContext attributes to exclude when formatting the event. This attribute only applies when includeThreadContext="true" is specified. If threadContextIncludes are also specified this attribute will be ignored. -| threadContextIncludes | String | A comma separated list of ThreadContext attributes to include when formatting the event. This attribute only applies when includeThreadContext="true" is specified. If threadContextExcludes are also specified this attribute will override them. ThreadContext fields specified here that have no value will be omitted. -| ThreadContextPrefix | String | A String to prepend to all elements of the ThreadContextMap when rendered as a field. Defaults to an empty String. + +|threadContextExcludes +|String +|A comma separated list of xref:manual/thread-context.adoc[] map keys to exclude when formatting the event. +This attribute only applies when `includeThreadContext` is `true`. +If `threadContextIncludes` are also specified, this attribute will be ignored. + +|threadContextIncludes +|String +|A comma separated list of xref:manual/thread-context.adoc[] map keys to include when formatting the event. +This attribute only applies when `includeThreadContext` is `true`. +If `threadContextExcludes` are also, specified this attribute will override them. +Thread context fields specified here that have no value will be omitted. + +|threadContextPrefix +|String +|A string to prepend to all elements of the xref:manual/thread-context.adoc[] map when rendered as a field (defaults to an empty string) |=== -To include any custom field in the output, use following syntax: +GELF Layout is garbage-free if `compressionType` is `OFF` and there are no additional fields containing `${`. +.Example configurations +[%collapsible] +==== +[tabs] +===== +XML:: ++ +.Snippet from an example {antora-examples-url}/manual/gelf-layout/log4j2.xml[`log4j2.xml`] [source,xml] ---- - - %d %5p [%t] %c{1} %X{loginId, requestId} - %m%n - - - +include::example$manual/gelf-layout/log4j2.xml[lines=26..41,indent=0] ---- -Custom fields are included in the order they are declared. -The values support xref:manual/lookups.adoc[lookups]. +JSON:: ++ +.Snippet from an example {antora-examples-url}/manual/gelf-layout/log4j2.json[`log4j2.json`] +[source,json] +---- +include::example$manual/gelf-layout/log4j2.json[lines=5..48,indent=0] +---- -GELF Layout is garbage-free when used with `compressionType="OFF"` and as long as no additional field contains `${` (variable substitution). +YAML:: ++ +.Snippet from an example {antora-examples-url}/manual/gelf-layout/log4j2.yaml[`log4j2.yaml`] +[source,xml] +---- +include::example$manual/gelf-layout/log4j2.yaml[lines=21..53,indent=0] +---- + +Properties:: ++ +.Snippet from an example {antora-examples-url}/manual/gelf-layout/log4j2.properties[`log4j2.properties`] +[source,xml] +---- +include::example$manual/gelf-layout/log4j2.properties[lines=17..48,indent=0] +---- +===== +<1> Configuration with additional key value pairs +<2> Configuration for appending to a Graylog server using TCP +<3> Configuration for appending to a Graylog server using UDP +==== [#HTMLLayout] -== HTML Layout +=== HTML Layout -The HtmlLayout generates an HTML page and adds each LogEvent to a row in a table. +HTML Layout generates an HTML page, and adds each log event to a row in a table. +It is configured with the following parameters: -.HtmlLayout Parameters -[cols="1m,1,4"] +[%header,cols="1m,1m,4"] |=== -|Parameter Name |Type |Description +|Parameter +|Type +|Description |charset |String -|The character set to use when converting the HTML -String to a byte array. The value must be a valid -http://docs.oracle.com/javase/6/docs/api/java/nio/charset/Charset.html[Charset]. -If not specified, this layout uses UTF-8. +|The character encoding |contentType |String -|The value to assign to the Content-Type header. -The default is "text/html". - -|locationInfo -|boolean -a|[[HtmlLocationInfo]] - -If true, the filename and line number will be included in the HTML -output. The default value is false. +|The `Content-Type` header value (defaults to `text/html`) -Generating link:#LocationInformation[location information] is an -expensive operation and may impact performance. Use with caution. - -|title +|datePattern |String -|A String that will appear as the HTML title. +|The date format of the log event. +The default is `JVM_ELAPSE_TIME`, which outputs the milliseconds since JVM started. +For other valid values, refer to xref:manual/pattern-layout.adoc#converter-date[the `date` conversion specifier of Pattern Layout]. |fontName |String -|The `font-family` to use. The default is "arial,sans-serif". +|The `font-family` (defaults to `arial,sans-serif`) |fontSize |String -|The `font size` to use. The default is "small". +|The `font size` (defaults to `small`) -|datePattern -|String -|The date format of the log event. -The default is `JVM_ELAPSE_TIME`, which outputs the milliseconds since JVM started. -For other valid values, refer to xref:manual/pattern-layout.adoc#converter-date[the `date` conversion specifier of Pattern Layout]. +|locationInfo +|boolean +|If `true`, the xref:#LocationInformation[source location information] be included (defaults to `false`) |timezone |String @@ -300,39 +409,25 @@ For other valid values, refer to xref:manual/pattern-layout.adoc#converter-date[ If not specified, this layout uses the https://docs.oracle.com/javase/8/docs/api/java/util/TimeZone.html#getDefault()[`TimeZone.getDefault()`] as the default. You can use time zone IDs supported by https://docs.oracle.com/javase/8/docs/api/java/util/TimeZone.html#getTimeZone(java.lang.String)[`TimeZone.getTimeZone(String)`]. +|title +|String +|The HTML page title |=== -Configure as follows to use `dataPattern` and timezone in `HtmlLayout`: - -[source,xml] ----- - - - - - ----- - [[JSONLayout]] -== JSON Layout +=== JSON Layout + +JSON Layout encodes a log event into JSON. [WARNING] ==== JSON Layout is considered deprecated. -xref:#JSONTemplateLayout[] provides more capabilities and should be used instead. +It is succeeded by xref:#JSONTemplateLayout[] providing more capabilities and efficiency. ==== -Appends a series of JSON events as strings serialized as bytes. - -=== Complete well-formed JSON vs. fragment JSON - -If you configure `complete="true"`, the appender outputs a well-formed JSON document. -By default, with `complete="false"`, you should include the output as an _external file_ in a separate file to form a well-formed JSON document. - -If `complete="false"`, the appender does not write the JSON open array character "[" at the start of the document, "]" at the end, nor comma "," between records. - -Log event follows this pattern: - +.Click for an example output +[%collapsible] +==== [source,json] ---- { @@ -390,53 +485,118 @@ Log event follows this pattern: } } ---- +==== -=== Pretty vs. compact JSON - -The compact attribute determines whether the output will be "pretty" or not. -The default value is "false", which means the appender uses end-of-line characters and indent lines to format the text. -If `compact="true"`, then no end-of-line or indentation is used, which will cause the output to take less space. -Of course, the message content may contain, escaped end-of-lines. +JSON Layout is configured with the following parameters: -[options="header"] +[%header,cols="1m,1m,4"] |=== -| Parameter Name | Type | Description -| charset | String | The character set to use when converting to a byte array. The value must be a valid `Charset`. If not specified, UTF-8 will be used. -| compact | boolean | If true, the appender does not use end-of-lines and indentation. Defaults to false. -| eventEol | boolean | If true, the appender appends an end-of-line after each record. Defaults to false. Use with eventEol=true and compact=true to get one record per line. -| endOfLine | String | If set, overrides the default end-of-line string. E.g., set it to "\n" and use with eventEol=true and compact=true to have one record per line separated by "\n" instead of "\r\n". Defaults to null (i.e., not set). -| complete | boolean | If true, the appender includes the JSON header and footer, and comma between records. Defaults to false. -| properties | boolean | If true, the appender includes the thread context map in the generated JSON. Defaults to false. -| propertiesAsList | boolean | If true, the thread context map is included as a list of map entry objects, where each entry has a "key" attribute (whose value is the key) and a "value" attribute (whose value is the value). Defaults to false, in which case the thread context map is included as a simple map of key-value pairs. -| locationInfo | boolean | If true, the appender includes the location information in the generated JSON. Defaults to false. Generating location information is an expensive operation and may impact performance. Use with caution. -| includeStacktrace | boolean | If true, include full stacktrace of any logged Throwable (optional, default to true). -| includeTimeMillis | boolean | If true, the timeMillis attribute is included in the Json payload instead of the instant. timeMillis will contain the number of milliseconds since midnight, January 1, 1970 UTC. -| stacktraceAsString | boolean | Whether to format the stacktrace as a string, and not a nested object (optional, defaults to false). -| includeNullDelimiter | boolean | Whether to include NULL byte as delimiter after each event (optional, default to false). -| objectMessageAsJsonObject | boolean | If true, ObjectMessage is serialized as JSON object to the "message" field of the output log. Defaults to false. +|Parameter Name +|Type +|Description + +|additionalFields +|link:../javadoc/log4j-core/org/apache/logging/log4j/core/util/KeyValuePair.html[KeyValuePair[\]] +|link:../javadoc/log4j-core/org/apache/logging/log4j/core/util/KeyValuePair.html[`KeyValuePair`] elements denoting additional entries. +Entry values can contain xref:manual/lookups.adoc[] using `${` syntax. + +|charset +|String +|The character encoding (defaults to `UTF-8`) + +|compact +|boolean +|If `true`, does not use `endOfLine` and indentation. +This feature is also referred to as _pretty-printing_ too. +It defaults to `false`. + +|complete +|boolean +| +If `true`, uses `header` and `footer`, and places comma between records – this leads to a well-formed JSON document given `header` and `footer` defaults to `[` and `]`, respectively. +It defaults to `false`. + +|endOfLine +|String +|If set, overrides the default end-of-line string. +For instance, set it to `\n` and use with `eventEol=true` and `compact=true` to have one record per line separated by `\n` instead of `\r\n`. +It is not set by default. + +|eventEol +|boolean +|If `true`, appends an `endOfLine` after each log event, even if `compact=true`. +It defaults to `false`. +Use with `eventEol=true` and `compact=true` to get one record per line. + +|footer +|String +|The footer to include when the stream is closed and `complete=true` (defaults to `]`) + +|header +|String +|The header to include when the stream is opened and `complete=true` (defaults to `[`) + +|includeNullDelimiter +|boolean +|If `true`, a NULL byte will suffix each encoded log event (defaults to `false`) + +|includeStacktrace +|boolean +|If `true`, include full stacktrace of any logged `Throwable` (defaults to `true`) + +|includeTimeMillis +|boolean +|If `true`, a `timeMillis` attribute is included in the JSON payload instead of the `instant`. +`timeMillis` will contain the number of milliseconds since 1970-01-01T00:00:00Z. + +|locationInfo +|boolean +|If `true`, the appender includes the xref:#LocationInformation[source location information] in the generated JSON (defaults to `false`) + +|objectMessageAsJsonObject +|boolean +|If `true`, xref:manual/messages.adoc#ObjectMessage[`ObjectMessage`] is serialized as JSON object to the `message` field of the output log (defaults to `false`) + +|properties +|boolean +|If `true`, the appender includes the xref:manual/thread-context.adoc[] map in the generated JSON (defaults to `false`) + +|propertiesAsList +|boolean +|If `true`, the xref:manual/thread-context.adoc[] map is included as a list of map entry objects, where each entry is denoted as a JSON object containg `key` and `value` fields. +It defaults to `false`, in which case the thread context map is included as a simple map of key-value pairs. + +|stacktraceAsString +|boolean +|If `true`, the stacktrace will be formatted as `String`, and not a nested object (optional, defaults to `false`) |=== -To include any custom field in the output, use the following syntax: +Additional runtime dependencies are required for using JSON Layout: -[source,xml] +[tabs] +==== +Maven:: ++ +[source,xml,subs="+attributes"] ---- - - - - + + com.fasterxml.jackson.core + jackson-databind + {jackson-version} + runtime + ---- -Custom fields are always last, in the order they are declared. -The values support xref:manual/lookups.adoc[lookups]. - -.Additional runtime dependencies are required for using `JsonLayout` -[%collapsible,id=log4j-layout-json-deps] -===== -include::partial$manual/dependencies-log4j-layout-json.adoc[] -===== +Gradle:: ++ +[source,groovy,subs="+attributes"] +---- +runtimeOnly 'com.fasterxml.jackson.core:jackson-databind:{jackson-version}' +---- +==== [#JSONTemplateLayout] -== JSON Template Layout +=== JSON Template Layout JSON Template Layout is a customizable, efficient, and garbage-free JSON generating layout. It encodes ``LogEvent``s according to the structure described by the JSON template provided. @@ -514,7 +674,7 @@ It bundles several predefined event templates modeling popular JSON-based log fo Read more on xref:manual/json-template-layout.adoc[]... [id=pattern-layout] -== [[PatternLayout]] Pattern Layout +=== [[PatternLayout]] Pattern Layout Pattern Layout is a customizable, efficient, garbage-free, and human-readable string generating layout using a user-provided pattern. It is analogous to `String#format()` with specialized directives on injecting certain properties of a `LogEvent`. @@ -545,32 +705,34 @@ WARN [main]: Message 2 Read more on xref:manual/pattern-layout.adoc[]... [#RFC5424Layout] -== RFC5424 Layout +=== RFC 5424 Layout -As the name implies, the Rfc5424Layout formats LogEvents by http://tools.ietf.org/html/rfc5424[RFC 5424], the enhanced Syslog specification. -Although the specification is primarily directed at sending messages via Syslog, this format is quite useful for other purposes since items are passed in the message as self-describing key/value pairs. +RFC 5424 Layout encodes log events according to https://datatracker.ietf.org/doc/html/rfc5424#section-6[the Syslog message format described in RFC 5424]. -.Rfc5424Layout Parameters -[cols="1m,1,4"] +[NOTE] +==== +RFC 5424 obsoletes RFC 3164, implemented by xref:#SyslogLayout[]. +==== + +RFC 5424 Layout is configured with the following parameters: + +[%header,cols="1m,1m,4"] |=== -|Parameter Name |Type |Description +|Parameter +|Type +|Description |appName |String -|The value to use as the APP-NAME in the RFC 5424 -syslog record. +|The `APP-NAME` field as described in RFC 5424 |charset |String -|The character set to use when converting the syslog -String to a byte array. The String must be valid -http://docs.oracle.com/javase/6/docs/api/java/nio/charset/Charset.html[Charset]. -If not specified, the default system Charset will be used. +|The character encoding (defaults to `UTF-8`) |enterpriseNumber |integer -|The IANA enterprise number as described in -http://tools.ietf.org/html/rfc5424#section-7.2.2[RFC 5424] +|The `enterpriseId` parameter as described in RFC 5424 (defaults to `32473`) |exceptionPattern |String @@ -579,35 +741,21 @@ The default is to not include the `Throwable` from the event, if any, in the out |facility |String -|The facility is used to try to classify the message. -The facility option must be set to one of "KERN", "USER", "MAIL", -"DAEMON", "AUTH", "SYSLOG", "LPR", "NEWS", "UUCP", "CRON", "AUTHPRIV", -"FTP", "NTP", "AUDIT", "ALERT", "CLOCK", "LOCAL0", "LOCAL1", "LOCAL2", -"LOCAL3", "LOCAL4", "LOCAL5", "LOCAL6", or "LOCAL7". These values may be -specified as upper or lowercase characters. - -|format -|String -|If set to "RFC5424" the data will be formatted by RFC 5424. -Otherwise, it will be formatted as a BSD -Syslog record. Note that although BSD Syslog records are required to be -1024 bytes or shorter the SyslogLayout does not truncate them. The -RFC5424Layout also does not truncate records since the receiver must -accept records of up to 2048 bytes and may accept longer records. +|The name of link:../javadoc/log4j-core/org/apache/logging/log4j/core/net/Facility.html[`Facility`] as described in RFC 5424. +The matching is case-insensitive. +It defaults to `LOCAL0`. |id |String -|The default structured data-id to use when formatting -according to RFC 5424. If the LogEvent contains a StructuredDataMessage -the id from the Message will be used instead of this value. +|The default _Structured Data ID_ to use when formatting according to RFC 5424. +If the log event contains a xref:manual/messages.adoc#StructuredDataMessage[`StructuredDataMessage`], the ID from that message will be used instead. |includeMDC |boolean -|Indicates whether data from the ThreadContextMap -will be included in the RFC 5424 Syslog record. Defaults to true. +|Indicates whether data from the xref:manual/thread-context.adoc[] map will be included in the RFC 5424 Syslog record (defaults to `true`) |loggerFields -|`KeyValuePair[]` +|link:../javadoc/log4j-core/org/apache/logging/log4j/core/util/KeyValuePair.html[KeyValuePair[\]] |Allows arbitrary xref:manual/thread-context.adoc[] map entries. To use, include a `LoggerFields` nested element, containing one or more `KeyValuePair` elements. Each `KeyValuePair` must have `key` and `value` attributes associating them with a thread context map entry. @@ -615,101 +763,107 @@ The `value` attribute can be an arbitrary xref:manual/pattern-layout.adoc[] patt |mdcExcludes |String -|A comma-separated list of mdc keys that should be -excluded from the LogEvent. This is mutually exclusive with the -mdcIncludes attribute. This attribute only applies to RFC 5424 syslog -records. +|A comma-separated list of xref:manual/thread-context.adoc[] map (aka, MDC) keys that should be excluded. +This is mutually exclusive with `mdcIncludes`. +This attribute only applies to RFC 5424 Syslog records. |mdcIncludes |String -|A comma-separated list of mdc keys that should be -included in the FlumeEvent. Any keys in the MDC not found in the list -will be excluded. This option is mutually exclusive with the mdcExcludes -attribute. This attribute only applies to RFC 5424 syslog records. +|A comma-separated list of xref:manual/thread-context.adoc[] map (aka, MDC) keys that should be included. +Any keys in the thread context map not found in the list will be excluded. +This option is mutually exclusive with `mdcExcludes`. +This attribute only applies to RFC 5424 Syslog records. -|mdcRequired +|mdcId |String -|A comma-separated list of mdc keys that must be -present in the MDC. If a key is not present a LoggingException will be -thrown. This attribute only applies to RFC 5424 syslog records. +|The ID to use for the xref:manual/thread-context.adoc[] map (aka, MDC) Structured Data Element. +It defaults to `mdc`. +This attribute only applies to RFC 5424 Syslog records. |mdcPrefix |String -|A string that should be prepended to each MDC key to distinguish it from event attributes. -The default string is "mdc:". This attribute only applies to RFC 5424 syslog records. +|A string to be prepended to each xref:manual/thread-context.adoc[] map (aka, MDC) key to distinguish it from event attributes. +It defaults to `mdc:`. +This attribute only applies to RFC 5424 Syslog records. -|mdcId +|mdcRequired |String -|A required MDC ID. This attribute only applies to RFC 5424 syslog records. +|A comma-separated list of xref:manual/thread-context.adoc[] map (aka, MDC) keys that must be present. +If a key is not present, a link:../javadoc/log4j-core/org/apache/logging/log4j/LoggingException.html[`LoggingException`] will be thrown. +This attribute only applies to RFC 5424 Syslog records. |messageId |String -|The default value to be used in the MSGID field of RFC 5424 syslog records. +|The default value to be used in the `MSGID` field of RFC 5424 Syslog records |newLine |boolean -|If true, a newline will be appended to the end of the syslog record. The default is false. +|If `true`, a `\n` character will be appended to the end of the Syslog record (defaults to `false`) |newLineEscape |String -|String that should be used to replace newlines within the message text. +|The string that should be used to replace newlines within the message text |=== [#SerializedLayout] -== Serialized Layout +=== Serialized Layout -The SerializedLayout simply serializes the LogEvent into a byte array using Java Serialization. -The SerializedLayout accepts no parameters. +Serialized Layout encodes a log event using https://docs.oracle.com/javase/8/docs/platform/serialization/spec/protocol.html[Java Serialization]. +It accepts no configuration parameters. -This layout has been deprecated since version 2.9. Java Serialization has inherent security weaknesses, using this layout is no longer recommended. +[WARNING] +==== +This layout has been deprecated since version `2.9`. +Java Serialization has inherent security weaknesses. +*Serialized Layout users are strongly advised to migrate to another layout!* +==== [#SyslogLayout] -== Syslog Layout +=== Syslog Layout + +Syslog Layout encodes log events according to https://datatracker.ietf.org/doc/html/rfc3164#section-4.1[the syslog message format described in RFC 3164]. +This matches the same format used by {logging-services-url}/log4j/1.x[Log4j 1]. + +[IMPORTANT] +==== +RFC 3164, implemented by Syslog Layout, is obsoleted by RFC 5424, implemented by xref:#RFC5424Layout[]. +==== -The SyslogLayout formats the LogEvent as BSD Syslog records matching the the same format used by Log4j 1.2. +Syslog Layout is configured with the following parameters: -.SyslogLayout Parameters -[cols="1m,1,4"] +[%header,cols="1m,1m,4"] |=== -|Parameter Name |Type |Description +|Parameter +|Type +|Description |charset |String -|The character set to use when converting the syslog -String to a byte array. The String must be a valid -http://docs.oracle.com/javase/6/docs/api/java/nio/charset/Charset.html[Charset]. -If not specified, this layout uses UTF-8. +|The character encoding (defaults to `UTF-8`) |facility |String -|The facility is used to try to classify the message. -The facility option must be set to one of "KERN", "USER", "MAIL", -"DAEMON", "AUTH", "SYSLOG", "LPR", "NEWS", "UUCP", "CRON", "AUTHPRIV", -"FTP", "NTP", "AUDIT", "ALERT", "CLOCK", "LOCAL0", "LOCAL1", "LOCAL2", -"LOCAL3", "LOCAL4", "LOCAL5", "LOCAL6", or "LOCAL7". These values may be -specified as upper or lowercase characters. +|The name of link:../javadoc/log4j-core/org/apache/logging/log4j/core/net/Facility.html[`Facility`] as described in RFC 5424. +The matching is case-insensitive. +It defaults to `LOCAL0`. |newLine |boolean -|If true, a newline will be appended to the end of the -syslog record. The default is false. +|If `true`, a `\n` character will be appended to the end of the Syslog record (defaults to `false`) |newLineEscape |String -|String that should be used to replace newlines -within the message text. +|The string that should be used to replace newlines within the message text |=== [[XMLLayout]] -== XML Layout - -=== Complete well-formed XML vs. fragment XML - -If you configure `complete="true"`, the appender outputs a well-formed XML document where the default namespace is the Log4j namespace `https://logging.apache.org/log4j/2.0/events`. -By default, with `complete="false"`, you should include the output as an _external entity_ in a separate file to form a well-formed XML document, in which case the appender uses `namespacePrefix` with a default of `log4j`. +=== XML Layout -A well-formed XML document follows this pattern: +XML Layout encodes a log event into XML. +.Click for an example output +[%collapsible] +==== [source,xml] ---- ---- +==== -If `complete="false"`, the appender does not write the XML processing instruction and the root element. - -=== Marker - -Markers are represented by a `Marker` element within the `Event` element. -The `Marker` element appears only when a marker is used in the log message. -The name of the marker's parent will be provided in the `parent` attribute of the `Marker` element. - -=== Pretty vs. compact XML - -By default, the XML layout is not compact (a.k.a. not "pretty") with `compact="false"`, which means the appender uses end-of-line characters and indent lines to format the XML. -If `compact="true"`, then no end-of-line or indentation is used. -Message content may contain, of course, end-of-lines. +XML Layout configuration is identical to the one of xref:#JSONLayout[], with following exceptions: -[options="header"] -|=== -| Parameter Name | Type | Description -| charset | String | The character set to use when converting to a byte array. The value must be a valid `Charset`. If not specified, UTF-8 will be used. -| compact | boolean | If true, the appender does not use end-of-lines and indentation. Defaults to false. -| complete | boolean | If true, the appender includes the XML header and footer. Defaults to false. -| properties | boolean | If true, the appender includes the thread context map in the generated XML. Defaults to false. -| locationInfo | boolean | If true, the appender includes the location information in the generated XML. Defaults to false. Generating location information is an expensive operation and may impact performance. Use with caution. -| includeStacktrace | boolean | If true, include full stacktrace of any logged Throwable (optional, default to true). -| stacktraceAsString | boolean | Whether to format the stacktrace as a string, and not a nested object (optional, defaults to false). -| includeNullDelimiter | boolean | Whether to include NULL byte as delimiter after each event (optional, default to false). -|=== +* The `header` and `footer` parameters are discarded +* If `complete=true`, each log event will be encoded into a well-formed XML document, such that: +** ` - - - + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + {jackson-version} + runtime + ---- -Custom fields are always last, in the order they are declared. -The values support xref:manual/lookups.adoc[lookups]. - -.Additional runtime dependencies are required for using `XmlLayout` -[%collapsible,id=log4j-layout-xml-deps] -===== -include::partial$manual/dependencies-log4j-layout-xml.adoc[] -===== +Gradle:: ++ +[source,groovy,subs="+attributes"] +---- +runtimeOnly 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:{jackson-version}' +---- +==== [[YamlLayout]] -== YAML Layout +=== YAML Layout -Appends a series of YAML events as strings serialized as bytes. +YAML Layout encodes a log event into YAML. -A YAML log event follows this pattern: +[IMPORTANT] +==== +YAML is a superset of JSON. +*We strongly advise existing YAML Layout users to migrate to xref:#JSONTemplateLayout[]* providing more capabilities and efficiency. +==== +.Click for an example output +[%collapsible] +==== [source,yaml] ---- instant: @@ -855,33 +1002,60 @@ source: file: "Main.java" line: 29 ---- +==== -[options="header"] -|=== -| Parameter Name | Type | Description -| charset | String | The character set to use when converting to a byte array. The value must be a valid `Charset`. If not specified, UTF-8 will be used. -| properties | boolean | If true, the appender includes the thread context map in the generated YAML. Defaults to false. -| locationInfo | boolean | If true, the appender includes the location information in the generated YAML. Defaults to false. Generating location information is an expensive operation and may impact performance. Use with caution. -| includeStacktrace | boolean | If true, include full stacktrace of any logged Throwable (optional, default to true). -| stacktraceAsString | boolean | Whether to format the stacktrace as a string, and not a nested object (optional, defaults to false). -| includeNullDelimiter | boolean | Whether to include NULL byte as delimiter after each event (optional, default to false). -|=== +YAML Layout configuration is identical to the one of xref:#JSONLayout[], with following exceptions: -To include any custom field in the output, use the following syntax: +* The `header` defaults to an empty string +* The `footer` defaults to an empty string -[source,xml] +Additional runtime dependencies are required for using XML Layout: + +[tabs] +==== +Maven:: ++ +[source,xml,subs="+attributes"] ---- - - - - + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + {jackson-version} + runtime + ---- -Custom fields are always last, in the order they are declared. -The values support xref:manual/lookups.adoc[lookups]. +Gradle:: ++ +[source,groovy,subs="+attributes"] +---- +runtimeOnly 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:{jackson-version}' +---- +==== -.Additional runtime dependencies are required for using `YamlLayout` -[%collapsible,id=log4j-layout-yaml-deps] -===== -include::partial$manual/dependencies-log4j-layout-yaml.adoc[] -===== +[#extending] +== Extending + +Layouts are xref:manual/plugins.adoc[plugins] implementing link:../javadoc/log4j-core/org/apache/logging/log4j/core/Layout.html[the `Layout` interface]. +This section will guide you on how to create custom ones. + +[NOTE] +==== +While xref:#collection[the predefined layout collection] should address most common use cases, you might find yourself needing to implement a custom one. +If this is the case, we really appreciate it if you can *share your use case in a {logging-services-url}/support.html[user support channel]*. +==== + +[#extending-plugins] +=== Plugin preliminaries + +include::partial$manual/plugin-preliminaries.adoc[] + +[#extending-layouts] +=== Extending layouts + +Layouts are xref:manual/plugins.adoc[plugins] implementing link:../javadoc/log4j-core/org/apache/logging/log4j/core/Layout.html[the `Layout` interface]. +If your layout is a `String`-based one, we recommend you to extend your plugin class from link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/AbstractStringLayout.html[`AbstractStringLayout`], which contains convenience for some of the boilerplate code shared by `String`-based layouts. +You can check out following files for examples: + +* {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/SyslogLayout.java[`SyslogLayout.java`] – simple, single-file, extending from `AbstractStringLayout` +* {project-github-url}/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java[`JsonTemplateLayout.java`] – advanced, using plugins for composing several of its features, contains _recycler_ concept for xref:manual/garbagefree.adoc[garbage-free] operation, extends from link:../javadoc/log4j-core/org/apache/logging/log4j/core/StringLayout.html[`StringLayout`] diff --git a/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc b/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc index fc187be95ce..92260603a5c 100644 --- a/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc @@ -1668,10 +1668,24 @@ Patterns containing are not garbage-free. ==== -:plugin-extension-intro-component-name: Pattern Layout -include::partial$manual/plugin-extension-intro.adoc[tag=part1] +[#extending] +== Extending + +Pattern Layout relies on xref:manual/plugins.adoc[the Log4j plugin system] to compose the features it provides. +This makes it possible for users to extend the plugin-based feature set as they see fit. As of this moment, only xref:#extending-converters[extending pattern converters] is supported. -include::partial$manual/plugin-extension-intro.adoc[tag=part2] +Following sections cover how to extend these in detail. + +[NOTE] +==== +While existing features should address most common use cases, you might find yourself needing to implement a custom one. +If this is the case, we really appreciate it if you can *share your use case in a {logging-services-url}/support.html[user support channel]*. +==== + +[#extending-plugins] +=== Plugin preliminaries + +include::partial$manual/plugin-preliminaries.adoc[] [#extending-converters] === Pattern converters diff --git a/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-csv.adoc b/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-csv.adoc deleted file mode 100644 index 38b130a52db..00000000000 --- a/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-csv.adoc +++ /dev/null @@ -1,38 +0,0 @@ -//// - 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. -//// - -[tabs] -==== -Maven:: -+ -[source,xml,subs="+attributes"] ----- - - org.apache.commons - commons-csv - {commons-csv-version} - runtime - ----- - -Gradle:: -+ -[source,groovy,subs="+attributes"] ----- -runtimeOnly 'org.apache.commons:commons-csv:{commons-csv-version}' ----- -==== \ No newline at end of file diff --git a/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-layout-json.adoc b/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-layout-json.adoc deleted file mode 100644 index cbda221184e..00000000000 --- a/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-layout-json.adoc +++ /dev/null @@ -1,38 +0,0 @@ -//// - 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. -//// - -[tabs] -==== -Maven:: -+ -[source,xml,subs="+attributes"] ----- - - com.fasterxml.jackson.core - jackson-databind - {jackson-version} - runtime - ----- - -Gradle:: -+ -[source,groovy,subs="+attributes"] ----- -runtimeOnly 'com.fasterxml.jackson.core:jackson-databind:{jackson-version}' ----- -==== \ No newline at end of file diff --git a/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-layout-template-json.adoc b/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-layout-template-json.adoc deleted file mode 100644 index 644d2c51154..00000000000 --- a/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-layout-template-json.adoc +++ /dev/null @@ -1,38 +0,0 @@ -//// - 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. -//// - -[tabs] -==== -Maven:: -+ -[source,xml,subs="+attributes"] ----- - - org.apache.logging.log4j - log4j-layout-template-json - {log4j-layout-template-json-version} - runtime - ----- - -Gradle:: -+ -[source,groovy,subs="+attributes"] ----- -runtimeOnly 'org.apache.logging.log4j:log4j-layout-template-json:{log4j-layout-template-json-version}' ----- -==== \ No newline at end of file diff --git a/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-layout-xml.adoc b/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-layout-xml.adoc deleted file mode 100644 index 50007bccfb0..00000000000 --- a/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-layout-xml.adoc +++ /dev/null @@ -1,38 +0,0 @@ -//// - 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. -//// - -[tabs] -==== -Maven:: -+ -[source,xml,subs="+attributes"] ----- - - com.fasterxml.jackson.dataformat - jackson-dataformat-xml - {jackson-version} - runtime - ----- - -Gradle:: -+ -[source,groovy,subs="+attributes"] ----- -runtimeOnly 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:{jackson-version}' ----- -==== \ No newline at end of file diff --git a/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-layout-yaml.adoc b/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-layout-yaml.adoc deleted file mode 100644 index 1459a1a9b25..00000000000 --- a/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-layout-yaml.adoc +++ /dev/null @@ -1,38 +0,0 @@ -//// - 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. -//// - -[tabs] -==== -Maven:: -+ -[source,xml,subs="+attributes"] ----- - - com.fasterxml.jackson.dataformat - jackson-dataformat-yaml - {jackson-version} - runtime - ----- - -Gradle:: -+ -[source,groovy,subs="+attributes"] ----- -runtimeOnly 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:{jackson-version}' ----- -==== \ No newline at end of file diff --git a/src/site/antora/modules/ROOT/partials/manual/layouts-location-information.adoc b/src/site/antora/modules/ROOT/partials/manual/layouts-location-information.adoc index da26dff9919..4695c80bea5 100644 --- a/src/site/antora/modules/ROOT/partials/manual/layouts-location-information.adoc +++ b/src/site/antora/modules/ROOT/partials/manual/layouts-location-information.adoc @@ -57,5 +57,5 @@ You can override the default behaviour in your asynchronous logger or asynchrono [NOTE] ==== Even if a layout is configured not to **request** location information, it might use it if the information is already available. -This is always the case of information location computed at build time using the link:/log4j/transform/latest/#log4j-transform-maven-plugin[Log4j Transform Maven Plugin]. +This is always the case if the location information is captured at build time using the link:/log4j/transform/latest/#log4j-transform-maven-plugin[Log4j Transform Maven Plugin]. ==== diff --git a/src/site/antora/modules/ROOT/partials/manual/plugin-extension-intro.adoc b/src/site/antora/modules/ROOT/partials/manual/plugin-preliminaries.adoc similarity index 62% rename from src/site/antora/modules/ROOT/partials/manual/plugin-extension-intro.adoc rename to src/site/antora/modules/ROOT/partials/manual/plugin-preliminaries.adoc index 549f0a4a867..ce00948b590 100644 --- a/src/site/antora/modules/ROOT/partials/manual/plugin-extension-intro.adoc +++ b/src/site/antora/modules/ROOT/partials/manual/plugin-preliminaries.adoc @@ -15,25 +15,11 @@ limitations under the License. //// -// tag::part1[] -[#extending] -== Extending - -{plugin-extension-intro-component-name} relies on xref:manual/plugins.adoc[the Log4j plugin system] to compose the features it provides. -This makes it possible for users to extend the plugin-based feature set as they see fit. -// end::part1[] -// tag::part2[] -Following sections cover these in detail. - -[#extending-plugins] -=== Plugin preliminaries - -Log4j plugin system is the de facto extension mechanism embraced by various Log4j components, including {plugin-extension-intro-component-name}. +Log4j plugin system is the de facto extension mechanism embraced by various Log4j components. Plugins make it possible for extensible components to _receive_ feature implementations without any explicit links in between. It is analogous to a https://en.wikipedia.org/wiki/Dependency_injection[dependency injection] framework, but curated for Log4j-specific needs. -In a nutshell, you annotate your classes with `@Plugin` and their (`static`) factory methods with `@PluginFactory`. +In a nutshell, you annotate your classes with link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/Plugin.html[`@Plugin`] and their (`static`) factory methods with link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginFactory.html[`@PluginFactory`]. Last, you inform the Log4j plugin system to discover these custom classes. -This is done using running the `org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor` annotation processor while building your project. +This is done using running the link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessor.html[`PluginProcessor`] annotation processor while building your project. Refer to xref:manual/plugins.adoc[] for details. -// end::part2[] From 631dbe483b2de8a9b5ccdf949c6dfc2919863ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Thu, 6 Jun 2024 13:55:27 +0200 Subject: [PATCH 05/53] Small adjustments --- .../modules/ROOT/pages/manual/appenders.adoc | 2 +- .../modules/ROOT/pages/manual/extending.adoc | 39 ------------------- .../ROOT/pages/manual/getting-started.adoc | 2 +- .../modules/ROOT/pages/manual/layouts.adoc | 15 +++++-- .../modules/ROOT/pages/manual/plugins.adoc | 9 ----- src/site/resources/.htaccess | 2 + 6 files changed, 16 insertions(+), 53 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/appenders.adoc b/src/site/antora/modules/ROOT/pages/manual/appenders.adoc index 1811478a783..aad5a661a3d 100644 --- a/src/site/antora/modules/ROOT/pages/manual/appenders.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/appenders.adoc @@ -237,7 +237,7 @@ maximum capacity. The CassandraAppender writes its output to an https://cassandra.apache.org/[Apache Cassandra] database. A keyspace and table must be configured ahead of time, and the columns of that table are mapped in a configuration file. -Each column can specify either a https://logging.apache.org/log4j/2.x/manual/layouts.html#PatternLayout[StringLayout] (e.g., a PatternLayout) along with an optional conversion type, or only a conversion type for `org.apache.logging.log4j.spi.ThreadContextMap` or +Each column can specify either a xref:manual/pattern-layout.adoc[]https://logging.apache.org/log4j/2.x/manual/layouts.html#PatternLayout[StringLayout] (e.g., a PatternLayout) along with an optional conversion type, or only a conversion type for `org.apache.logging.log4j.spi.ThreadContextMap` or `org.apache.logging.log4j.spi.ThreadContextStack` to store the MDC or NDC in a map or list column respectively. A conversion type compatible with `java.util.Date` will use the log event timestamp converted to that type (e.g., use `java.util.Date` to fill a `timestamp` column type in Cassandra). diff --git a/src/site/antora/modules/ROOT/pages/manual/extending.adoc b/src/site/antora/modules/ROOT/pages/manual/extending.adoc index f36ad675319..b8d6395681a 100644 --- a/src/site/antora/modules/ROOT/pages/manual/extending.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/extending.adoc @@ -378,45 +378,6 @@ public final class StubAppender extends AbstractOutputStreamAppender ---- -<1> xref:manual/layouts.adoc#pattern-layout[Pattern Layout] is used for encoding the log event in a human-readable way. +<1> xref:manual/pattern-layout.adoc[] is used for encoding the log event in a human-readable way. <2> Increased logging verbosity for the `com.mycompany` package. [#next] diff --git a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc index 5dffde3add4..25d1139b027 100644 --- a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc @@ -210,9 +210,10 @@ GELF Layout encodes log events in https://go2docs.graylog.org/current/getting_in It can compress the output when it exceeds a certain threshold. This layout does not implement chunking. -[TIP] +[WARNING] ==== -Unless compression is needed, we advise you to use xref:manual/json-template-layout.adoc[JSON Template Layout] instead, which provides GELF layout support out of the box and offers a far richer set of features. +*This layout is planned to be removed in the next major release!* +Unless compression is needed, we advise you to use xref:manual/json-template-layout.adoc[JSON Template Layout] instead, which provides GELF Layout support out of the box and offers more capabilities and performance. ==== GELF Layout is configured with the following parameters: @@ -861,6 +862,12 @@ It defaults to `LOCAL0`. XML Layout encodes a log event into XML. +[WARNING] +==== +*This layout is planned to be removed in the next major release!* +XML Layout users are strongly advised to migrate to another layout! +==== + .Click for an example output [%collapsible] ==== @@ -950,8 +957,10 @@ runtimeOnly 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:{jackson-ve YAML Layout encodes a log event into YAML. -[IMPORTANT] +[WARNING] ==== +This layout is planned to be removed in the next major release! + YAML is a superset of JSON. *We strongly advise existing YAML Layout users to migrate to xref:#JSONTemplateLayout[]* providing more capabilities and efficiency. ==== diff --git a/src/site/antora/modules/ROOT/pages/manual/plugins.adoc b/src/site/antora/modules/ROOT/pages/manual/plugins.adoc index 5d9a613673d..c793d404c43 100644 --- a/src/site/antora/modules/ROOT/pages/manual/plugins.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/plugins.adoc @@ -78,15 +78,6 @@ Plugin factory fields and parameters can be automatically validated at runtime u * ValidHost: This annotation validates that a value corresponds to a valid hostname. This uses the same validation as InetAddress::getByName. * ValidPort: This annotation validates that a value corresponds to a valid port number between 0 and 65535. -[#converters] -== Converters - -Converters are used by PatternLayout to render the elements identified by the conversion pattern. Every converter must specify its category as "Converter" on the @Plugin annotation, have a static newInstance method that accepts an array of Strings as its only parameter and returns an instance of the Converter, and must have a @ConverterKeys annotation present that contains the array of converter patterns that will cause the Converter to be selected. Converters that are meant to handle LogEvents must extend the LogEventPatternConverter class and must implement a format method that accepts a LogEvent and a StringBuilder as arguments. The Converter should append the result of its operation to the StringBuilder. - -A second type of Converter is the FileConverter - which must have "FileConverter" specified in the category attribute of the @Plugin annotation. While similar to a LogEventPatternConverter, instead of a single format method these Converters will have two variations; one that takes an Object and one that takes an array of Objects instead of the LogEvent. Both append to the provided StringBuilder in the same fashion as a LogEventPatternConverter. These Converters are typically used by the RollingFileAppender to construct the name of the file to log to. - -If multiple Converters specify the same ConverterKeys, then the load order above determines which one will be used. For example, to override the %date converter which is provided by the built-in DatePatternConverter class, you would need to place your plugin in a JAR file in the CLASSPATH ahead oflog4j-core.jar. This is not recommended; pattern ConverterKeys collisions will cause a warning to be emitted. Try to use unique ConverterKeys for your custom pattern converters. - [#key-providers] == Key providers diff --git a/src/site/resources/.htaccess b/src/site/resources/.htaccess index 4490f1f6797..708c2c5df70 100644 --- a/src/site/resources/.htaccess +++ b/src/site/resources/.htaccess @@ -54,6 +54,7 @@ RewriteRule "^log4j-slf4j-impl\.html$" "manual/installation.html#impl-core-bridg RewriteRule "^log4j-slf4j2-impl/index\.html$" "manual/installation.html#impl-core-bridge-slf4j" [R=permanent,NE] RewriteRule "^log4j-slf4j2-impl\.html$" "manual/installation.html#impl-core-bridge-slf4j" [R=permanent,NE] RewriteRule "^manual/api-separation\.html$" "manual/api.html" [R=permanent] +RewriteRule "^manual/extending\.html#Layouts$" "manual/layouts.html#extending" [R=permanent] RewriteRule "^manual/extending\.html#PatternConverters$" "manual/pattern-layout.html#extending-converters" [R=permanent] RewriteRule "^manual/layouts\.html#enable-jansi$" "manual/pattern-layout.html#jansi" [R=permanent] RewriteRule "^manual/layouts\.html#EndOfBatch$" "manual/pattern-layout.html#converter-end-of-batch" [R=permanent] @@ -93,6 +94,7 @@ RewriteRule "^manual/layouts\.html#PatternNewLine$" "manual/pattern-layout.html# RewriteRule "^manual/layouts\.html#Process_ID$" "manual/pattern-layout.html#converter-pid" [R=permanent] RewriteRule "^manual/layouts\.html#ScriptPatternSelector$" "manual/pattern-layout.html#plugin-element-ScriptPatternSelector" [R=permanent] RewriteRule "^manual/layouts\.html#VariablesNotEmpty$" "manual/pattern-layout.html#converter-not-empty" [R=permanent] +RewriteRule "^manual/plugins\.html#converters$" "manual/pattern-layout.html#extending-converters" [R=permanent] RewriteRule "^manual/scala-api\.html$" "/log4j/scala/latest/index.html" [R=permanent] RewriteRule "^manual/usage\.html$" "manual/api.html" [R=permanent] RewriteRule "^runtime-dependencies\.html$" "manual/installation.html" [R=permanent] From 74a6ae462b201808bc06ca86888fc589b3adccd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 10 Jun 2024 20:17:35 +0200 Subject: [PATCH 06/53] Improve wording on UTF-8 default --- src/site/antora/modules/ROOT/pages/manual/layouts.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc index 25d1139b027..c5ee4562cd2 100644 --- a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc @@ -33,8 +33,8 @@ This section introduces you to some common concerns shared by almost all xref:#c === Character encoding All xref:#collection[predefined layouts] produce `String` that eventually get converted into a `byte[]` using the https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html[`Charset`] configured. -While doing so, they use `UTF-8` by default. -If you want all your log events to be formatted in a certain character encoding that is different than what the employed layout defaults to, make sure to configure the layout's character encoding as needed. +While doing so, unless an explicit encoding configuration is stated, they use `UTF-8` by default. +If you want all your log events to be formatted in a certain character encoding that is different from what the employed layout defaults to, make sure to configure the layout's character encoding as needed. [#LocationInformation] === Location information From f67176a39a0e05351225728a3cb7e85d1752b07a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 10 Jun 2024 20:18:54 +0200 Subject: [PATCH 07/53] Improve wording Co-authored-by: Piotr P. Karwasz --- src/site/antora/modules/ROOT/pages/manual/layouts.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc index 25d1139b027..bf5513ca021 100644 --- a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc @@ -179,7 +179,7 @@ Both `CsvParameterLayout` and `CsvLogEventLayout` are configured with the follow |The footer to include when the stream is closed |=== -There are additional runtime dependencies are required for using CSV layouts: +Additional runtime dependencies are required for using CSV layouts: [tabs] ==== From 78330f010feac9d753b851d9ed0c317fe1988170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 10 Jun 2024 20:24:46 +0200 Subject: [PATCH 08/53] Apply suggestions from code review Co-authored-by: Piotr P. Karwasz --- .../ROOT/pages/manual/json-template-layout.adoc | 4 ++-- src/site/antora/modules/ROOT/pages/manual/layouts.adoc | 4 +++- .../modules/ROOT/pages/manual/pattern-layout.adoc | 10 ++++++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc b/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc index 00e746b962f..1087a18e885 100644 --- a/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc @@ -984,7 +984,7 @@ Resolve the argument coming right after `--userId`: ===== `map` Resolves ``MapMessage``s. -See link:#map-resolver-template[] for details. +See <> for details. [#event-template-resolver-marker] ===== `marker` @@ -1025,7 +1025,7 @@ Resolve the names of the marker's parents: ===== `mdc` Resolves the xref:manual/thread-context.adoc[thread context] map, aka. Mapped Diagnostic Context (MDC). -See link:#map-resolver-template[] for details. +See <> for details. [WARNING] ==== diff --git a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc index bf5513ca021..6fab2c3b309 100644 --- a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc @@ -733,7 +733,9 @@ RFC 5424 Layout is configured with the following parameters: |enterpriseNumber |integer -|The `enterpriseId` parameter as described in RFC 5424 (defaults to `32473`) +| +The `enterpriseId` parameter as described in RFC 5424. +If missing, `32473` will be used, which is https://www.rfc-editor.org/rfc/rfc5612.html#section-2[reserved for documentation use]. |exceptionPattern |String diff --git a/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc b/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc index 92260603a5c..aad3380b53f 100644 --- a/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc @@ -133,6 +133,11 @@ Pattern Layout plugin configuration accepts the following attributes: A composite pattern string of one or more xref:#converters[]. `pattern` and xref:#plugin-attr-patternSelector[] are mutually exclusive, that is, only one can be specified. +[WARNING] +==== +If the provided pattern does not contain an exception converter and <> is not disabled, an implicit xref:#converter-exception-extended[`%xEX`] is appended to the pattern. +==== + [#plugin-attr-patternSelector] ==== `patternSelector` @@ -164,7 +169,7 @@ If configured, the `replace` element must specify the regular expression to matc |Default value |`true` |=== -If `true`, exceptions are always written even if the pattern contains no exception conversions. +If `true` and the user-provided pattern does not contain an exception converter, an implicit xref:#converter-exception-extended[`%xEX`] pattern is appended. This means that if you do not include a way to output exceptions in your pattern, the default exception formatter will be added to the end of the pattern. Setting this to `false` disables this behavior and allows you to exclude exceptions from your pattern output. @@ -561,7 +566,7 @@ Replaces occurrences of a string (`test`) with its replacement (`substitution`) .link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/EqualsReplacementConverter.html[`EqualsReplacementConverter`] and link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/EqualsIgnoreCaseReplacementConverter.html[`EqualsReplacementConverter`] specifiers' grammar [source,text] ---- -equals{pattern}{test}{substitution} + +equals{pattern}{test}{substitution} equalsIgnoreCase{pattern}{test}{substitution} ---- @@ -1459,6 +1464,7 @@ uuid{RANDOM|TIME} The time-based UUID is a Type 1 UUID generated using the MAC address of each host To ensure uniqueness across multiple JVMs and/or class loaders on the same host, a random number between 0 and 16,384 will be associated with each instance of the UUID generator class, and included in each time-based UUID generated. +See also xref:manual/systemproperties.adoc#log4j2.uuidSequence[`log4j2.uuidSequence`]. Because time-based UUIDs contain the MAC address and timestamp, they should be used with care. [#format-modifiers] From b95d668f7e9dfe7a7d5b7787be58f4a7b9b65a15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 10 Jun 2024 20:38:26 +0200 Subject: [PATCH 09/53] Explain `PatternLayout` documentation notation --- .../ROOT/pages/manual/pattern-layout.adoc | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc b/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc index aad3380b53f..bc013500bdd 100644 --- a/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc @@ -336,7 +336,23 @@ include::example$manual/pattern-layout/script-pattern-selector/log4j2.properties The Pattern Layout _conversion pattern_ is composed of literal text and format control expressions called _conversion specifiers_ – refer to xref:#usage[] for details. xref:manual/plugins.adoc[Plugins] implementing link:../javadoc/log4j-core/org/apache/logging/log4j/core/pattern/PatternConverter.html[`PatternConverter`] are admitted to the *pattern converter* registry of Pattern Layout, and used to resolve the conversion specifiers. -The predefined set of pattern converters are shared below. + +The predefined set of pattern converters will be shared in the following sections. +While doing so, their syntax will be documented in a certain notation. +Consider the following example for the syntax of xref:#converter-date[] pattern converter: + +[source,text] +---- +%d{dateSpecifier}[{timezone}] +---- + +This means that + +* `%d` identifies the associated pattern converter +* `\{dateSpecifier}` indicates that the converter accepts a *required* `dateSpecifier` parameter +* `[\{timezone}]` indicates that the converter accepts an *optional* `timezone` parameter + +If you want to have `%d\{something}` literal in your pattern without matching for the actual `%d` pattern converter, you can escape the `%` as follows: `%%d\{something}`. [#converter-class] ==== Class From 76f77b59edc56a27ae277dbcd8c605c0a04841ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 10 Jun 2024 20:56:04 +0200 Subject: [PATCH 10/53] Remove incorrectly documented `%m{lookups}` --- .../modules/ROOT/pages/manual/pattern-layout.adoc | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc b/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc index bc013500bdd..8d04768832a 100644 --- a/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc @@ -1115,17 +1115,6 @@ The call site can look like this: logger.info("@\|KeyStyle {}\|@ = @\|ValueStyle {}\|@", entry.getKey(), entry.getValue()); ---- -You can enable xref:manual/lookups.adoc[] using `\{lookups}` and log messages like `${date:YYYY-MM-dd}` using lookups. -This will replace the date template `YYYY-MM-dd` with an actual date. -This can be confusing in many cases, and it's often both easier and more obvious to handle the lookup in code. -This feature is disabled by default and the message string is logged untouched. - -[WARNING] -==== -Users are strongly discouraged from using the lookups option. -Doing so may allow uncontrolled user input containing lookups to take unintended actions. -==== - [#converter-method] ==== Method From f77b5c0fce41b93da448f5d78fe902a09f970092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 10 Jun 2024 21:01:32 +0200 Subject: [PATCH 11/53] Fix Pattern Layout gc-free lookups docs --- .../antora/modules/ROOT/pages/manual/pattern-layout.adoc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc b/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc index 8d04768832a..2b4edba82cf 100644 --- a/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc @@ -1606,7 +1606,7 @@ Format modifiers to control such things as field width, padding, left, and right |xref:#converter-message[%m + %msg + %message] -|Garbage-free unless message text contains `${` +| |xref:#converter-marker[%marker] | @@ -1665,7 +1665,10 @@ Format modifiers to control such things as field width, padding, left, and right | |literal text -|Garbage-free unless it contains `${` +|Garbage-free unless it contains `\$${`. +That is, adding `${date:YYYY-MM-dd}` is garbage-free, because it will be expanded at configuration time. +Whereas adding `$${date:YYYY-MM-dd}` is not, since it will be expanded at each log event. +See xref:manual/configuration.adoc#property-substitution[property substitution] for details. |=== [NOTE] From c5f012682d393ddb372a8b3cb0d2d184440a30f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 10 Jun 2024 21:33:50 +0200 Subject: [PATCH 12/53] Fix garbage-free notes about lookups for PL and JTL --- .../pages/manual/json-template-layout.adoc | 9 +++++++- .../ROOT/pages/manual/pattern-layout.adoc | 6 ++---- .../manual/layouts-gcfree-lookup.adoc | 21 +++++++++++++++++++ 3 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 src/site/antora/modules/ROOT/partials/manual/layouts-gcfree-lookup.adoc diff --git a/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc b/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc index 1087a18e885..9dcf261a727 100644 --- a/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc @@ -198,6 +198,11 @@ JSON Template Layout plugin configuration accepts the following attributes: `Charset` used for encoding the produced JSON into bytes +[WARNING] +==== +While https://datatracker.ietf.org/doc/html/rfc4627#section-3[RFC 4627, the JSON specification, supports multiple Unicode encodings], we strongly advise you to stick to the layout default, and use UTF-8 for https://stackoverflow.com/a/594881/1278899[several practical reasons]. +==== + [#plugin-attr-locationInfoEnabled] ==== [[locationInfoEnabled]]`locationInfoEnabled` @@ -209,6 +214,7 @@ JSON Template Layout plugin configuration accepts the following attributes: |=== Toggles access to the `LogEvent` source; file name, line number, etc. +See also xref:manual/layouts.adoc#LocationInformation[location information]. [#plugin-attr-stackTraceEnabled] ==== `stackTraceEnabled` @@ -2047,7 +2053,8 @@ To get the most out of it, mind the following checklist: === Are lookups supported in templates? Yes, xref:manual/lookups.adoc[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. + +include::partial$manual/layouts-gcfree-lookup.adoc[] [#faq-recursive-collection] === Are recursive collections supported? diff --git a/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc b/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc index 2b4edba82cf..b12cf243feb 100644 --- a/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc @@ -1665,10 +1665,8 @@ Format modifiers to control such things as field width, padding, left, and right | |literal text -|Garbage-free unless it contains `\$${`. -That is, adding `${date:YYYY-MM-dd}` is garbage-free, because it will be expanded at configuration time. -Whereas adding `$${date:YYYY-MM-dd}` is not, since it will be expanded at each log event. -See xref:manual/configuration.adoc#property-substitution[property substitution] for details. +a|Garbage-free, but care is needed for xref:manual/lookups.adoc[]. +include::partial$manual/layouts-gcfree-lookup.adoc[] |=== [NOTE] diff --git a/src/site/antora/modules/ROOT/partials/manual/layouts-gcfree-lookup.adoc b/src/site/antora/modules/ROOT/partials/manual/layouts-gcfree-lookup.adoc new file mode 100644 index 00000000000..1fd022a2f5a --- /dev/null +++ b/src/site/antora/modules/ROOT/partials/manual/layouts-gcfree-lookup.adoc @@ -0,0 +1,21 @@ +//// + 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. +//// + +Encoding the output of a lookup is garbage-free, but executing the lookup might not be. +For instance, even though xref:manual/lookups.adoc#DateLookup[the `date` lookup] allocates a new `String` for each invocation, adding `${date:YYYY-MM-dd}` is garbage-free, because it will be expanded at configuration time, that is, only once. +Whereas adding `$${date:YYYY-MM-dd}` is not, since it will be expanded for each log event. +See xref:manual/configuration.adoc#property-substitution[property substitution] for details. From 0cf6c346e775b6307a3b0d7f793835c94f6f2761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 10 Jun 2024 22:12:38 +0200 Subject: [PATCH 13/53] Add plugin ref links --- .../pages/manual/json-template-layout.adoc | 4 +- .../modules/ROOT/pages/manual/layouts.adoc | 68 ++++++++++++++----- .../ROOT/pages/manual/pattern-layout.adoc | 4 +- 3 files changed, 57 insertions(+), 19 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc b/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc index 9dcf261a727..8408762e53a 100644 --- a/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/json-template-layout.adoc @@ -16,7 +16,7 @@ //// = JSON Template Layout -JSON Template Layout is a customizable, xref:#performance[efficient], and xref:#faq-garbage-free[garbage-free] JSON generating layout. +`JsonTemplateLayout` is a customizable, xref:#performance[efficient], and xref:#faq-garbage-free[garbage-free] JSON generating layout. It encodes ``LogEvent``s according to the structure described by the JSON template provided. In a nutshell, it shines with its @@ -181,6 +181,8 @@ Are you trying to implement your own event (or stack trace) template and looking Please refer to xref:#template-config[] instead. ==== +xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-layout-template-json_org-apache-logging-log4j-layout-template-json-JsonTemplateLayout[📖 Plugin reference for `JsonTemplateLayout`] + [#plugin-attrs] === Plugin attributes diff --git a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc index 89901a29efa..ec12ec8ee36 100644 --- a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc @@ -55,15 +55,21 @@ Refer to xref:manual/pattern-layout.adoc[]. Following sections explain all predefined layouts in detail. +xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-core_org-apache-logging-log4j-core-Layout[📖 Plugin reference for all `Layout` implementations] + [#csv-layouts] === [[CSVLayouts]] CSV Layouts There are two layouts performing https://en.wikipedia.org/wiki/Comma-separated_values[Comma Separated Value (CSV)] encoding: -link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/CsvParameterLayout.html[`CsvParameterLayout`]:: -It encodes *only* the parameters of the message of a log event. +[#csv-layout-parameter] +==== CSV Parameter Layout + +`CsvParameterLayout` encodes *only* the parameters of the message of a log event. Generated CSV records will be composed of fields denoting the message parameters. -+ + +xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-core_org-apache-logging-log4j-core-layout-CsvParameterLayout[📖 Plugin reference for `CsvParameterLayout`] + .Click here for examples [%collapsible] ==== @@ -92,10 +98,12 @@ LOGGER.info(new ObjectArrayMessage("arg3", "arg4", "arg5")); ---- ==== -link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/CsvLogEventLayout.html[`CsvLogEventLayout`]:: -It encodes the complete log event, including the formatted message. +[#csv-layout-log-event] +==== CSV Log Event Layout + +`CsvLogEventLayout` encodes the complete log event, including the formatted message. Generated CSV records will be composed of following fields in the given order: -+ + . Time (in nanoseconds) . Time (in milliseconds) . Level @@ -111,7 +119,8 @@ Generated CSV records will be composed of following fields in the given order: . Thread context map . Thread context stack -+ +xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-core_org-apache-logging-log4j-core-layout-CsvLogEventLayout[📖 Plugin reference for `CsvLogEventLayout`] + .Click here for examples [%collapsible] ==== @@ -130,6 +139,9 @@ LOGGER.debug("one={}, two={}, three={}", 1, 2, 3); ---- ==== +[#csv-layout-config] +==== Configuration + Both `CsvParameterLayout` and `CsvLogEventLayout` are configured with the following parameters: [%header,cols="1m,1m,4"] @@ -206,7 +218,7 @@ runtimeOnly 'org.apache.commons:commons-csv:{commons-csv-version}' [#GELFLayout] === GELF Layout -GELF Layout encodes log events in https://go2docs.graylog.org/current/getting_in_log_data/gelf.html#GELFPayloadSpecification[the GELF specification] version `1.1`. +`GelfLayout` encodes log events in https://go2docs.graylog.org/current/getting_in_log_data/gelf.html#GELFPayloadSpecification[the GELF specification] version `1.1`. It can compress the output when it exceeds a certain threshold. This layout does not implement chunking. @@ -216,6 +228,8 @@ This layout does not implement chunking. Unless compression is needed, we advise you to use xref:manual/json-template-layout.adoc[JSON Template Layout] instead, which provides GELF Layout support out of the box and offers more capabilities and performance. ==== +xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-core_org-apache-logging-log4j-core-layout-GelfLayout[📖 Plugin reference for `GelfLayout`] + GELF Layout is configured with the following parameters: [%header,cols="1m,1m,4"] @@ -369,7 +383,10 @@ include::example$manual/gelf-layout/log4j2.properties[lines=17..48,indent=0] [#HTMLLayout] === HTML Layout -HTML Layout generates an HTML page, and adds each log event to a row in a table. +`HtmlLayout` generates an HTML page, and adds each log event to a row in a table. + +xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-core_org-apache-logging-log4j-core-layout-HtmlLayout[📖 Plugin reference for `HtmlLayout`] + It is configured with the following parameters: [%header,cols="1m,1m,4"] @@ -418,7 +435,7 @@ You can use time zone IDs supported by https://docs.oracle.com/javase/8/docs/api [[JSONLayout]] === JSON Layout -JSON Layout encodes a log event into JSON. +`JsonLayout` encodes a log event into JSON. [WARNING] ==== @@ -426,6 +443,8 @@ JSON Layout is considered deprecated. It is succeeded by xref:#JSONTemplateLayout[] providing more capabilities and efficiency. ==== +xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-core_org-apache-logging-log4j-core-layout-JsonLayout[📖 Plugin reference for `JsonLayout`] + .Click for an example output [%collapsible] ==== @@ -599,8 +618,11 @@ runtimeOnly 'com.fasterxml.jackson.core:jackson-databind:{jackson-version}' [#JSONTemplateLayout] === JSON Template Layout -JSON Template Layout is a customizable, efficient, and garbage-free JSON generating layout. +`JsonTemplateLayout` is a customizable, efficient, and garbage-free JSON generating layout. It encodes ``LogEvent``s according to the structure described by the JSON template provided. + +xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-layout-template-json_org-apache-logging-log4j-layout-template-json-JsonTemplateLayout[📖 Plugin reference for `JsonTemplateLayout`] + For instance, given the following event template stored in `MyLayout.json` in your classpath: [source,json] @@ -677,7 +699,7 @@ Read more on xref:manual/json-template-layout.adoc[]... [id=pattern-layout] === [[PatternLayout]] Pattern Layout -Pattern Layout is a customizable, efficient, garbage-free, and human-readable string generating layout using a user-provided pattern. +`PatternLayout` is a customizable, efficient, garbage-free, and human-readable string generating layout using a user-provided pattern. It is analogous to `String#format()` with specialized directives on injecting certain properties of a `LogEvent`. [IMPORTANT] @@ -686,6 +708,8 @@ Pattern Layout is not intended for _structural logging_ purposes. For production environments, you are strongly advised to use xref:manual/json-template-layout.adoc[] producing JSON output ready to be delivered to log ingestion systems such as Elasticsearch or Google Cloud Logging. ==== +xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-core_org-apache-logging-log4j-core-layout-PatternLayout[📖 Plugin reference for `PatternLayout`] + A conversion pattern is composed of literal text and format control expressions. For instance, given the `%-5p [%t]: %m%n` pattern, following statements @@ -708,13 +732,15 @@ Read more on xref:manual/pattern-layout.adoc[]... [#RFC5424Layout] === RFC 5424 Layout -RFC 5424 Layout encodes log events according to https://datatracker.ietf.org/doc/html/rfc5424#section-6[the Syslog message format described in RFC 5424]. +`Rfc5424` Layout encodes log events according to https://datatracker.ietf.org/doc/html/rfc5424#section-6[the Syslog message format described in RFC 5424]. [NOTE] ==== RFC 5424 obsoletes RFC 3164, implemented by xref:#SyslogLayout[]. ==== +xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-core_org-apache-logging-log4j-core-layout-Rfc5424Layout[📖 Plugin reference for `Rfc5424Layout`] + RFC 5424 Layout is configured with the following parameters: [%header,cols="1m,1m,4"] @@ -811,7 +837,7 @@ This attribute only applies to RFC 5424 Syslog records. [#SerializedLayout] === Serialized Layout -Serialized Layout encodes a log event using https://docs.oracle.com/javase/8/docs/platform/serialization/spec/protocol.html[Java Serialization]. +`SerializedLayout` encodes a log event using https://docs.oracle.com/javase/8/docs/platform/serialization/spec/protocol.html[Java Serialization]. It accepts no configuration parameters. [WARNING] @@ -821,10 +847,12 @@ Java Serialization has inherent security weaknesses. *Serialized Layout users are strongly advised to migrate to another layout!* ==== + xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-core_org-apache-logging-log4j-core-layout-SerializedLayout[📖 Plugin reference for `SerializedLayout`] + [#SyslogLayout] === Syslog Layout -Syslog Layout encodes log events according to https://datatracker.ietf.org/doc/html/rfc3164#section-4.1[the syslog message format described in RFC 3164]. +`SyslogLayout` encodes log events according to https://datatracker.ietf.org/doc/html/rfc3164#section-4.1[the syslog message format described in RFC 3164]. This matches the same format used by {logging-services-url}/log4j/1.x[Log4j 1]. [IMPORTANT] @@ -832,6 +860,8 @@ This matches the same format used by {logging-services-url}/log4j/1.x[Log4j 1]. RFC 3164, implemented by Syslog Layout, is obsoleted by RFC 5424, implemented by xref:#RFC5424Layout[]. ==== +xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-core_org-apache-logging-log4j-core-layout-SyslogLayout[📖 Plugin reference for `SyslogLayout`] + Syslog Layout is configured with the following parameters: [%header,cols="1m,1m,4"] @@ -862,7 +892,7 @@ It defaults to `LOCAL0`. [[XMLLayout]] === XML Layout -XML Layout encodes a log event into XML. +`XmlLayout` encodes a log event into XML. [WARNING] ==== @@ -870,6 +900,8 @@ XML Layout encodes a log event into XML. XML Layout users are strongly advised to migrate to another layout! ==== +xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-core_org-apache-logging-log4j-core-layout-XmlLayout[📖 Plugin reference for `XmlLayout`] + .Click for an example output [%collapsible] ==== @@ -957,7 +989,7 @@ runtimeOnly 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:{jackson-ve [[YamlLayout]] === YAML Layout -YAML Layout encodes a log event into YAML. +`YamlLayout` encodes a log event into YAML. [WARNING] ==== @@ -967,6 +999,8 @@ YAML is a superset of JSON. *We strongly advise existing YAML Layout users to migrate to xref:#JSONTemplateLayout[]* providing more capabilities and efficiency. ==== +xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-core_org-apache-logging-log4j-core-layout-YamlLayout[📖 Plugin reference for `YamlLayout`] + .Click for an example output [%collapsible] ==== diff --git a/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc b/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc index b12cf243feb..915ca6d8793 100644 --- a/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc @@ -17,7 +17,7 @@ = Pattern Layout -Pattern Layout is a customizable, xref:#performance[efficient], xref:#garbage-free[garbage-free], and human-readable string generating layout using a user-provided pattern. +`PatternLayout` is a customizable, xref:#performance[efficient], xref:#garbage-free[garbage-free], and human-readable string generating layout using a user-provided pattern. It is analogous to `String#format()` with specialized directives on injecting certain properties of a `LogEvent`. [IMPORTANT] @@ -105,6 +105,8 @@ To suppress the formatting of the `Throwable` completely simply add `%ex\{0}` as This section explains how to configure Pattern Layout plugin element in a Log4j configuration file. +xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-core_org-apache-logging-log4j-core-layout-PatternLayout[📖 Plugin reference for `PatternLayout`] + [#plugin-attrs] === Plugin attributes From f9deaaf961726256099e0449af8d33544a2b0a77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Tue, 11 Jun 2024 10:30:06 +0200 Subject: [PATCH 14/53] Move extending appenders/filters/lookups to their own pages --- .../modules/ROOT/pages/manual/appenders.adoc | 166 +++++++++++------ .../modules/ROOT/pages/manual/extending.adoc | 170 ------------------ .../modules/ROOT/pages/manual/filters.adoc | 66 +++++-- .../modules/ROOT/pages/manual/lookups.adoc | 83 ++++++--- src/site/resources/.htaccess | 3 + 5 files changed, 225 insertions(+), 263 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/appenders.adoc b/src/site/antora/modules/ROOT/pages/manual/appenders.adoc index aad5a661a3d..be44a32d4d8 100644 --- a/src/site/antora/modules/ROOT/pages/manual/appenders.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/appenders.adoc @@ -14,8 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. //// + = Appenders -Ralph Goers; Gary Gregory; Nick Williams; Matt Sicker Appenders are responsible for delivering LogEvents to their destination. Every Appender must implement the @@ -70,8 +70,14 @@ This setting only guarantees that a byte representation of the log event is pass It does not ensure that the operating system writes the event to the underlying storage. ==== +[#collection] +== Collection + +Log4j bundles several predefined appenders to assist in several common deployment use cases. +Following sections explain all these in detail. + [id=AsyncAppender] -== [[asyncappender]] AsyncAppender +=== [[asyncappender]] AsyncAppender The AsyncAppender accepts references to other Appenders and causes LogEvents to be written to them on a separate Thread. Note that exceptions while writing to those Appenders will be hidden from the application. @@ -232,7 +238,7 @@ maximum capacity. |======================================================================= [#CassandraAppender] -== CassandraAppender +=== CassandraAppender The CassandraAppender writes its output to an https://cassandra.apache.org/[Apache Cassandra] database. @@ -371,7 +377,7 @@ CREATE TABLE logs ( ---- [id=consoleappender] -== [[ConsoleAppender]] ConsoleAppender +=== [[ConsoleAppender]] ConsoleAppender As one might expect, the ConsoleAppender writes its output to either `System.out` or `System.err` with `System.out` being the default target. @@ -434,7 +440,7 @@ A typical Console configuration might look like: ---- [#FailoverAppender] -== FailoverAppender +=== FailoverAppender The FailoverAppender wraps a set of appenders. If the primary Appender fails the secondary appenders will be tried in order until one succeeds or there are no more secondaries to try. @@ -497,7 +503,7 @@ A Failover configuration might look like: ---- [id=fileappender] -== [[FileAppender]] FileAppender +=== [[FileAppender]] FileAppender The FileAppender is an OutputStreamAppender that writes to the File defined in the `fileName` parameter. The FileAppender uses a FileManager (which extends OutputStreamManager) to perform the file I/O. @@ -625,7 +631,7 @@ Here is a sample File configuration: ---- [#FlumeAppender] -== FlumeAppender +=== FlumeAppender _This is an optional component supplied in a separate jar._ @@ -857,7 +863,7 @@ A sample FlumeAppender configuration that is configured with a primary and a sec ---- [#JDBCAppender] -== JDBCAppender +=== JDBCAppender As of Log4j 2.11.0, JDBC support has moved from the existing module `log4j-core` to the new module `log4j-jdbc`. @@ -1251,7 +1257,7 @@ table based on a Log4j `MapMessage` instead of values from `LogEvent`. ---- -== JMS Appender +=== JMS Appender The JMS Appender sends the formatted log event to a JMS Destination. @@ -1384,7 +1390,7 @@ To map your Log4j `MapMessage` to JMS `javax.jms.MapMessage`, set the layout of ---- [[JPAAppender]] -== JPAAppender +=== JPAAppender As of Log4j 2.11.0, JPA support has moved from the existing module `log4j-core` to the new module `log4j-jpa`. @@ -1562,7 +1568,7 @@ public class JpaLogEntity extends AbstractLogEventWrapperEntity { ---- [#HttpAppender] -== HttpAppender +=== HttpAppender The HttpAppender sends log events over HTTP. A Layout must be provided to format the LogEvent. @@ -1636,7 +1642,7 @@ Here is a sample HttpAppender configuration snippet: ---- [[KafkaAppender]] -== KafkaAppender +=== KafkaAppender The KafkaAppender logs events to an https://kafka.apache.org/[Apache Kafka] topic. Each log event is sent as a Kafka record. @@ -1721,7 +1727,7 @@ _Note:_ Make sure to not let `org.apache.kafka` log to a Kafka appender on DEBUG ---- [#MemoryMappedFileAppender] -== MemoryMappedFileAppender +=== MemoryMappedFileAppender _New since 2.1. Be aware that this is a new addition, and although it has been tested on several platforms, it does not have as much track record as the other file appenders._ @@ -1816,7 +1822,7 @@ Here is a sample MemoryMappedFile configuration: ---- [#NoSQLAppender] -== NoSQLAppender +=== NoSQLAppender The NoSQLAppender writes log events to a NoSQL database using an internal lightweight provider interface. Provider implementations currently exist for MongoDB, and writing a custom provider is quite simple. @@ -1907,10 +1913,10 @@ The following example demonstrates how log events are persisted in NoSQL databas ---- [#NoSQLAppenderMongoDB] -=== NoSQL providers for MongoDB +==== NoSQL providers for MongoDB [#mongo-installation] -==== Installation +===== Installation Starting with version 2.11.0, Log4j supplies providers for the https://www.mongodb.com/[MongoDB] @@ -1980,7 +1986,7 @@ If you are note sure, which implementation to choose, `log4j-mongodb` is the rec ==== [#log4j-mongodb] -==== NoSQL provider for MongoDB (current) +===== NoSQL provider for MongoDB (current) This section details specializations of the link:#NoSQLAppender[NoSQLAppender] provider for MongoDB using the current MongoDB driver (version 5). @@ -2069,7 +2075,7 @@ You can define additional fields to log using KeyValuePair elements, for example ---- [#log4j-mongodb4] -==== [[NoSQLAppenderMongoDB4]] NoSQL provider for MongoDB 4 (deprecated) +===== [[NoSQLAppenderMongoDB4]] NoSQL provider for MongoDB 4 (deprecated) The `log4j-mongodb4` module is deprecated in favor of link:#log4j-mongodb[NoSQL provider for MongoDB]. @@ -2159,7 +2165,7 @@ You can define additional fields to log using KeyValuePair elements, for example ---- [[NoSQLAppenderApacheCouchDB]] -== NoSQLAppender for Apache CouchDB +=== NoSQLAppender for Apache CouchDB This section details specializations of the link:#NoSQLAppender[NoSQLAppender] provider for CouchDB. The NoSQLAppender writes log events to a NoSQL database using an internal lightweight provider interface. @@ -2222,7 +2228,7 @@ Here are a few sample configurations for the NoSQLAppender and CouchDB provider: ---- [#OutputStreamAppender] -== OutputStreamAppender +=== OutputStreamAppender The OutputStreamAppender provides the base for many of the other Appenders such as the File and Socket appenders that write the event to an Output Stream. It cannot be directly configured. @@ -2230,7 +2236,7 @@ Support for immediateFlush and buffering is provided by the OutputStreamAppender The OutputStreamAppender uses an OutputStreamManager to handle the actual I/O, allowing the stream to be shared by Appenders in multiple configurations. [#RandomAccessFileAppender] -== RandomAccessFileAppender +=== RandomAccessFileAppender The RandomAccessFileAppender is similar to the standard link:#FileAppender[FileAppender] except it is always buffered (this cannot be switched off) and internally it uses a @@ -2310,7 +2316,7 @@ Here is a sample RandomAccessFile configuration: ---- [#RewriteAppender] -== RewriteAppender +=== RewriteAppender The RewriteAppender allows the LogEvent to be manipulated before it is processed by another Appender. This can be used to mask sensitive information such as passwords or to inject information into each event. @@ -2342,13 +2348,13 @@ Appender in a link:#FailoverAppender[FailoverAppender]. |======================================================================= [#RewritePolicy] -=== RewritePolicy +==== RewritePolicy RewritePolicy is an interface that allows implementations to inspect and possibly modify LogEvents before they are passed to Appender. RewritePolicy declares a single method named rewrite that must be implemented. The method is passed the LogEvent and can return the same event or create a new one. -==== MapRewritePolicy +===== MapRewritePolicy MapRewritePolicy will evaluate LogEvents that contain a MapMessage and will add or update elements of the Map. @@ -2384,7 +2390,7 @@ The following configuration shows a RewriteAppender configured to add a product ---- -==== PropertiesRewritePolicy +===== PropertiesRewritePolicy PropertiesRewritePolicy will add properties configured on the policy to the ThreadContext Map being logged. The properties will not be added to the actual ThreadContext Map. @@ -2423,7 +2429,7 @@ The following configuration shows a RewriteAppender configured to add a product ---- -==== LoggerNameLevelRewritePolicy +===== LoggerNameLevelRewritePolicy You can use this policy to make loggers in third-party code less chatty by changing event levels. The LoggerNameLevelRewritePolicy will rewrite log event levels for a given logger name prefix. @@ -2466,7 +2472,7 @@ The following configuration shows a RewriteAppender configured to map level INFO ---- [id=rollingfileappender] -== [[RollingFileAppender]] RollingFileAppender +=== [[RollingFileAppender]] RollingFileAppender The RollingFileAppender is an OutputStreamAppender that writes to the file named in the fileName parameter and rolls the file over according to the `TriggeringPolicy` and the `RolloverPolicy`. The `RollingFileAppender` @@ -2602,9 +2608,9 @@ file attribute view. |======================================================================= [#TriggeringPolicies] -== TriggeringPolicies +=== TriggeringPolicies -=== Composite Triggering Policy +==== Composite Triggering Policy The `CompositeTriggeringPolicy` combines multiple triggering policies and returns true if any of the configured policies return true. The @@ -2621,7 +2627,7 @@ For example, the following XML fragment defines policies that rollover the log w ---- -=== Cron Triggering Policy +==== Cron Triggering Policy The `CronTriggeringPolicy` triggers rollover based on a cron expression. This policy is controlled by a timer and is asynchronous in processing log events, so it is possible that log events from the previous or next period may appear at the beginning or end of the log file. @@ -2642,7 +2648,7 @@ expression indicates a rollover should have occurred between that time and the current time the file will be immediately rolled over. |======================================================================= -=== OnStartup Triggering Policy +==== OnStartup Triggering Policy The `OnStartupTriggeringPolicy` policy causes a rollover if the log file is older than the current JVM's start time and the minimum file size is met or exceeded. @@ -2661,7 +2667,7 @@ When running in Google App Engine, the OnStartup policy causes a rollover if the `java.lang.management.ManagementFactory.getRuntimeMXBean().getStartTime()` and falls back to Log4J initialization time instead.) -=== SizeBased Triggering Policy +==== SizeBased Triggering Policy The `SizeBasedTriggeringPolicy` causes a rollover once the file has reached the specified size. The size can be specified in bytes, with the suffix KB, MB, GB, or TB for example `20MB`. @@ -2672,7 +2678,7 @@ When combined with a time-based triggering policy the file pattern must contain Otherwise, the target file will be overwritten on every rollover as the SizeBased Triggering Policy will not cause the timestamp value in the file name to change. When used without a time-based triggering policy the SizeBased Triggering Policy will cause the timestamp value to change. -=== TimeBased Triggering Policy +==== TimeBased Triggering Policy The `TimeBasedTriggeringPolicy` causes a rollover once the date/time pattern no longer applies to the active file. This policy accepts an @@ -2701,10 +2707,10 @@ load of doing so across time. |======================================================================= [#RolloverStrategies] -== RolloverStrategies +=== RolloverStrategies [#DefaultRolloverStrategy] -=== DefaultRolloverStrategy +==== DefaultRolloverStrategy Default Rollover Strategy @@ -2798,7 +2804,7 @@ archived log file during compression. |======================================================================= [#DirectWriteRolloverStrategy] -=== DirectWriteRolloverStrategy +==== DirectWriteRolloverStrategy DirectWrite Rollover Strategy @@ -2959,7 +2965,7 @@ This sample configuration is the same as the previous one but limits the number ---- [#CustomDeleteOnRollover] -=== CustomDeleteOnRollover +==== CustomDeleteOnRollover Log Archive Retention Policy: Delete on Rollover @@ -3046,7 +3052,7 @@ and must return a list with the paths to delete. |======================================================================= [#DeleteIfFileName] -== DeleteIfFileName +=== DeleteIfFileName .IfFileName Condition Parameters [cols="20%,20%,60%",options="header",] @@ -3072,7 +3078,7 @@ the path name matches). |======================================================================= [#DeleteIfLastModified] -== DeleteIfLastModified +=== DeleteIfLastModified .IfLastModified Condition Parameters [cols="20%,20%,60%",options="header",] @@ -3091,7 +3097,7 @@ the file is old enough). |======================================================================= [#DeleteIfAccumulatedFileCount] -== DeleteIfAccumulatedFileCount +=== DeleteIfAccumulatedFileCount .IfAccumulatedFileCount Condition Parameters [cols="20%,20%,60%",options="header",] @@ -3108,7 +3114,7 @@ the threshold count has been exceeded). |======================================================================= [#DeleteIfAccumulatedFileSize] -== DeleteIfAccumulatedFileSize +=== DeleteIfAccumulatedFileSize .IfAccumulatedFileSize Condition Parameters [cols="20%,20%,60%",options="header",] @@ -3202,7 +3208,7 @@ During every rollover, this configuration will delete files that match "*/app-*. ---- [#ScriptCondition] -== ScriptCondition +=== ScriptCondition .ScriptCondition Parameters [cols="20%,20%,60%",options="header",] @@ -3217,7 +3223,7 @@ how ScriptFiles and ScriptRefs can be configured. |======================================================================= [#ScriptParameters] -== ScriptParameters +=== ScriptParameters .Script Parameters [cols="20%,20%,60%",options="header",] @@ -3307,7 +3313,7 @@ The Delete action will delete all files returned by the script. ---- [#CustomPosixViewAttributeOnRollover] -== CustomPosixViewAttributeOnRollover +=== CustomPosixViewAttributeOnRollover Log Archive File Attribute View Policy: Custom file attribute on Rollover @@ -3399,7 +3405,7 @@ Below is a sample configuration that uses a RollingFileAppender and defines diff ---- [#RollingRandomAccessFileAppender] -== RollingRandomAccessFileAppender +=== RollingRandomAccessFileAppender The RollingRandomAccessFileAppender is similar to the standard link:#RollingFileAppender[RollingFileAppender] except it is always buffered (this cannot be switched off) and internally it uses a @@ -3511,11 +3517,11 @@ file attribute view. |======================================================================= -=== Triggering Policies +==== Triggering Policies See xref:#TriggeringPolicies[RollingFileAppender Triggering Policies]. -=== Rollover Strategies +==== Rollover Strategies See link:#RolloverStrategies[RollingFileAppender Rollover Strategies]. @@ -3599,7 +3605,7 @@ Below is a sample configuration that uses a RollingRandomAccessFileAppender with ---- [#RoutingAppender] -== RoutingAppender +=== RoutingAppender The `RoutingAppender` evaluates LogEvents and then routes them to a subordinate Appender. The target Appender may be an appender previously configured and may be referenced by its name or the Appender can be dynamically created as needed. @@ -3678,7 +3684,7 @@ Note that the List Appender is one of our test appenders, any appender can be us ---- [#Routes] -=== Routes +==== Routes The `Routes` element accepts a single attribute named "pattern". The pattern is evaluated against all the registered Lookups and the result is used to select a `Route`. @@ -3763,7 +3769,7 @@ In this example, the script runs for each log event and picks a route based on t ---- [#Purge] -=== Purge Policy +==== Purge Policy The RoutingAppender can be configured with a PurgePolicy whose purpose is to stop and remove dormant Appenders that have been dynamically created by the RoutingAppender. Log4j currently provides the IdlePurgePolicy is the only PurgePolicy available for cleaning up the Appenders. @@ -3807,7 +3813,7 @@ Note that the AuditAppender was predefined while the RollingFileAppenders are cr ---- [[SMTPAppender]] -== SMTPAppender +=== SMTPAppender Sends an e-mail when a specific logging event occurs, typically on errors or fatal errors. @@ -3916,7 +3922,7 @@ As with other Appenders, the formatting can be controlled by specifying a Layout ---- [#ScriptAppenderSelector] -== ScriptAppenderSelector +=== ScriptAppenderSelector When the configuration is built, the `ScriptAppenderSelector` appender calls a `ScriptPlugin` to compute an appender name. Log4j then creates one of the appender named listed under `AppenderSet` using the name of the @@ -3952,7 +3958,7 @@ The appender name is recorded under the name of the ---- [#SocketAppender] -== SocketAppender +=== SocketAppender The `SocketAppender` is an OutputStreamAppender that writes its output to a remote destination specified by a host and port. The data can be sent over either TCP or UDP and can be sent in any format. @@ -4062,7 +4068,7 @@ This is a secured link:#SSL[SSL] configuration: ---- [#SSL] -== SSL +=== SSL Several appenders can be configured to use either a plain network connection or a Secure Socket Layer (SSL) connection. This section documents the parameters available for SSL configuration. @@ -4085,7 +4091,7 @@ counterparty. Determines whether the remote authentication credentials |======================================================================= [#KeyStore] -=== KeyStore +==== KeyStore The Keystore is meant to contain your private keys and certificates, and determines which authentication credentials to send to the remote host. @@ -4118,7 +4124,7 @@ algorithms]. |======================================================================= [#TrustStore] -=== TrustStore +==== TrustStore The trust store is meant to contain the CA certificates you are willing to trust when a remote party presents its certificate. Determines whether the remote authentication credentials (and thus the connection) should be trusted. @@ -4154,7 +4160,7 @@ algorithms]. |======================================================================= [#Example] -=== Example +==== Example [source,xml] ---- @@ -4165,7 +4171,7 @@ algorithms]. ---- [#SyslogAppender] -== SyslogAppender +=== SyslogAppender The `SyslogAppender` is a `SocketAppender` that writes its output to a remote destination specified by a host and port in a format that conforms with either the BSD Syslog format or the RFC 5424 format. The data can be sent over either TCP or UDP. @@ -4331,7 +4337,7 @@ For link:#SSL[SSL] this appender writes its output to a remote destination speci ---- [[JeroMQAppender]] -== ZeroMQ/JeroMQ Appender +=== ZeroMQ/JeroMQ Appender The ZeroMQ appender uses the https://github.com/zeromq/jeromq[JeroMQ] library to send log events to one or more ZeroMQ endpoints. @@ -4462,3 +4468,45 @@ Please consult the JeroMQ and ZeroMQ documentation for details. |boolean |The ZMQ_XPUB_VERBOSE option. Defaults to false. |=== + +[#extending] +== Extending + +Appenders are xref:manual/plugins.adoc[plugins] implementing link:../javadoc/log4j-core/org/apache/logging/log4j/core/Appender.html[the `Appender` interface]. +This section will guide you on how to create custom ones. + +[WARNING] +==== +*Implementing a reliable and efficient appender is a difficult task!* +We strongly advise you to + +. Use existing appenders and/or managers whenever appropriate +. Share your use case and ask for feedback in a {logging-services-url}/support.html[user support channel] +==== + +[#extending-plugins] +=== Plugin preliminaries + +include::partial$manual/plugin-preliminaries.adoc[] + +[#extending-appenders] +=== Extending appenders + +Appenders are xref:manual/plugins.adoc[plugins] implementing link:../javadoc/log4j-core/org/apache/logging/log4j/core/Appender.html[the `Appender` interface]. +We recommend users to extend from link:../javadoc/log4j-core/org/apache/logging/log4j/core/appender/AbstractAppender.html[`AbstractAppender`], which provides implementation convenience. +While annotating your appender with `@Plugin`, you need to make sure that + +* It has a unique `name` attribute across all available `Appender` plugins +* The `category` attribute is set to link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Node.html#CATEGORY[`Node.CATEGORY`] +* The `elementType` attribute is set to link:../javadoc/log4j-core/org/apache/logging/log4j/core/Appender.html#ELEMENT_TYPE[`Appender.ELEMENT_TYPE`] + +Most appender implementation use *managers*, which model an abstraction owning the resources, such as an `OutputStream` or a socket. +When a reconfiguration occurs a new appender will be created. +However, if nothing significant in the previous manager has changed, the new appender will simply reference it instead of creating a new one. +This ensures that events are not lost while a reconfiguration is taking place without requiring that logging pause while the reconfiguration takes place. +You are strongly advised to study the manager concept in xref:#collection[the predefined appenders], and either use an existing manager, or create your own. + +You can check out following files for examples: + +* {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/HttpAppender.java[`HttpAppender.java`] – xref:#HttpAppender[] sends log events over HTTP using `HttpURLConnectionManager` +* {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java[`ConsoleAppender.java`] – xref:#ConsoleAppender[Console Appender] writes log events to either `System.out` or `System.err` using `OutputStreamManager` diff --git a/src/site/antora/modules/ROOT/pages/manual/extending.adoc b/src/site/antora/modules/ROOT/pages/manual/extending.adoc index b8d6395681a..6b3e67233ed 100644 --- a/src/site/antora/modules/ROOT/pages/manual/extending.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/extending.adoc @@ -208,176 +208,6 @@ a separate `FlowMessageFactory`. Applications may replace the `log4j2.flowMessageFactory` to the name of the custom `FlowMessageFactory` class. -[#Lookups] -== Lookups - -Lookups are the means in which parameter substitution is performed. -During Configuration initialization an "Interpolator" is created that -locates all the Lookups and registers them for use when a variable needs -to be resolved. The interpolator matches the "prefix" portion of the -variable name to a registered Lookup and passes control to it to resolve -the variable. - -A Lookup must be declared using a `@Plugin @Lookup` annotation. The `value` specified on the `@Plugin` annotation will be used to -match the prefix. The example below shows a Lookup that will return -the value of a System Property. - -The provided Lookups are documented here: xref:manual/lookups.adoc[Lookups] - -[source,java] ----- -@Lookup -@Plugin("sys") -public class SystemPropertiesLookup implements StrLookup { - - /** - * Lookup the value for the key. - * @param key the key to be looked up, may be null - * @return The value for the key. - */ - public String lookup(String key) { - return System.getProperty(key); - } - - /** - * Lookup the value for the key using the data in the LogEvent. - * @param event The current LogEvent. - * @param key the key to be looked up, may be null - * @return The value associated with the key. - */ - public String lookup(LogEvent event, String key) { - return System.getProperty(key); - } -} ----- - -[#Filters] -== Filters - -As might be expected, Filters are used to reject or accept log -events as they pass through the logging system. A Filter is declared -using a `@Configurable` annotation with an `elementType` of "filter". -The `value` attribute on the `@Plugin` annotation is used to specify the name -of the element users should use to enable the Filter. Specifying the -`printObject` attribute with a value of "true" indicates that a call to -`toString` will format the arguments to the filter as the configuration is -being processed. The Filter must also specify a `@PluginFactory` method -or `@PluginFactoryBuilder` builder class and method -that will be called to create the Filter. - -The example below shows a Filter used to reject LogEvents based upon -their logging level. Notice the typical pattern where all the filter -methods resolve to a single filter method. - -[source,java] ----- -@Plugin(name = "ThresholdFilter", category = "Core", elementType = "filter", printObject = true) -public final class ThresholdFilter extends AbstractFilter { - - private final Level level; - - private ThresholdFilter(Level level, Result onMatch, Result onMismatch) { - super(onMatch, onMismatch); - this.level = level; - } - - public Result filter(Logger logger, Level level, Marker marker, String msg, Object[] params) { - return filter(level); - } - - public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) { - return filter(level); - } - - public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) { - return filter(level); - } - - @Override - public Result filter(LogEvent event) { - return filter(event.getLevel()); - } - - private Result filter(Level level) { - return level.isAtLeastAsSpecificAs(this.level) ? onMatch : onMismatch; - } - - @Override - public String toString() { - return level.toString(); - } - - /** - * Create a ThresholdFilter. - * @param level The log Level. - * @param onMatch The action to take on a match. - * @param onMismatch The action to take on a mismatch. - * @return The created ThresholdFilter. - */ - @PluginFactory - public static ThresholdFilter createFilter(@PluginAttribute(defaultStringValue = "ERROR") Level level, - @PluginAttribute(defaultStringValue = "NEUTRAL") Result onMatch, - @PluginAttribute(defaultStringValue = "DENY") Result onMismatch) { - return new ThresholdFilter(level, onMatch, onMismatch); - } -} ----- - -[#Appenders] -== Appenders - -Appenders are passed an event, (usually) invoke a Layout to format the -event, and then "publish" the event in whatever manner is desired. -Appenders are declared as `@Configurable` with an -`elementType` of "appender". The `value` attribute on the `@Plugin` annotation -specifies the name of the element users must provide in their -configuration to use the Appender. Appenders should specify `printObject` -as "true" if the toString method renders the values of the attributes -passed to the Appender. - -Appenders must also declare a `@PluginFactory` method that returns an instance -of the appender or a builder class used to create the appender. The example below shows -an Appender named "Stub" that can be used as an initial template. - -Most Appenders use Managers. A manager actually "owns" the resources, -such as an `OutputStream` or socket. When a reconfiguration occurs a new -Appender will be created. However, if nothing significant in the -previous Manager has changed, the new Appender will simply reference it -instead of creating a new one. This insures that events are not lost -while a reconfiguration is taking place without requiring that logging -pause while the reconfiguration takes place. - -[source,java] ----- -@Plugin(name = "Stub", category = "Core", elementType = "appender", printObject = true) -public final class StubAppender extends AbstractOutputStreamAppender { - - private StubAppender(String name, - Layout layout, - Filter filter, - boolean ignoreExceptions, - StubManager manager) { - super(name, layout, filter, ignoreExceptions, true, manager); - } - - @PluginFactory - public static StubAppender createAppender(@PluginAttribute @Required(message = "No name provided for StubAppender") String name, - @PluginAttribute boolean ignoreExceptions, - @PluginElement Layout layout, - @PluginElement Filter filter) { - - StubManager manager = StubManager.getStubManager(name); - if (manager == null) { - return null; - } - if (layout == null) { - layout = PatternLayout.createDefaultLayout(); - } - return new StubAppender(name, layout, filter, ignoreExceptions, manager); - } -} ----- - [#Plugin_Builders] == Plugin Builders diff --git a/src/site/antora/modules/ROOT/pages/manual/filters.adoc b/src/site/antora/modules/ROOT/pages/manual/filters.adoc index baea1d507b4..88e10b79acf 100644 --- a/src/site/antora/modules/ROOT/pages/manual/filters.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/filters.adoc @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. //// + [id=filters] = Filters @@ -85,8 +86,14 @@ Users migrating from Log4j 1 often replace the `threshold` property of a Log4j 1 Using the `level` property of appender references will give a better performance. ==== +[#collection] +== Collection + +Log4j bundles several predefined filters to assist in several common deployment use cases. +Following sections explain all these in detail. + [#BurstFilter] -== BurstFilter +=== BurstFilter The BurstFilter provides a mechanism to control the rate at which LogEvents are processed by silently discarding events after the maximum limit has been reached. @@ -148,7 +155,7 @@ A configuration containing the BurstFilter might look like: ---- [#CompositeFilter] -== CompositeFilter +=== CompositeFilter The CompositeFilter provides a way to specify more than one filter. It is added to the configuration as a filter element and contains other filters to be evaluated. @@ -194,7 +201,7 @@ A configuration containing the CompositeFilter might look like: ---- [#DynamicThresholdFilter] -== DynamicThresholdFilter +=== DynamicThresholdFilter The DynamicThresholdFilter allows filtering by log level based on specific attributes. For example, if the user's loginId is being captured in the ThreadContext Map then it is possible to enable debug logging for only that user. @@ -262,7 +269,7 @@ Here is a sample configuration containing the DynamicThresholdFilter: ---- [#LevelRangeFilter] -== LevelRangeFilter +=== LevelRangeFilter `LevelRangeFilter` allows filtering against a level range, where levels get compared by their associated integral values; `OFF` has an integral value of 0, `FATAL` 100, `ERROR` 200, and so on. @@ -311,7 +318,7 @@ The filter will return `onMismatch` result (i.e., `DENY`, the default) for log e ---- [#MapFilter] -== MapFilter +=== MapFilter The MapFilter allows filtering against data elements that are in a MapMessage. @@ -431,7 +438,7 @@ This third sample configuration will exhibit the same behavior as the preceding ---- [#MarkerFilter] -== MarkerFilter +=== MarkerFilter The MarkerFilter compares the configured Marker value against the Marker that is included in the LogEvent. A match occurs when the Marker name matches either the Log Event's Marker or one of its parents. @@ -481,7 +488,7 @@ A sample configuration that only allows the event to be written by the appender ---- [#MutableThreadContextMapFilter] -== MutableThreadContextMapFilter +=== MutableThreadContextMapFilter The MutableThreadContextMapFilter or MutableContextMapFilter allows filtering against data elements that are in the current context. By default, this is the ThreadContext Map. @@ -555,7 +562,7 @@ The configuration file supplied to the filter should look similar to: ---- [#NoMarkerFilter] -== NoMarkerFilter +=== NoMarkerFilter The NoMarkerFilter checks that there is no marker included in the LogEvent. A match occurs when there is no marker in the Log Event. @@ -601,7 +608,7 @@ A sample configuration that only allows the event to be written by the appender ---- [#RegexFilter] -== RegexFilter +=== RegexFilter The RegexFilter allows the formatted or unformatted message to be compared against a regular expression. @@ -771,7 +778,7 @@ xref:manual/appenders.adoc#ScriptCondition[ScriptCondition] for an example of ho ---- [#StructuredDataFilter] -== StructuredDataFilter +=== StructuredDataFilter The StructuredDataFilter is a MapFilter that also allows filtering on the event id, type and message. @@ -836,7 +843,7 @@ As in this configuration, the StructuredDataFilter can be used to log particular ---- [#ThreadContextMapFilter] -== ThreadContextMapFilter +=== ThreadContextMapFilter The ThreadContextMapFilter or ContextMapFilter allows filtering against data elements that are in the current context. By default, this is the ThreadContext Map. @@ -928,7 +935,7 @@ The ContextMapFilter can also be applied to a logger for filtering: ---- [#ThresholdFilter] -== ThresholdFilter +=== ThresholdFilter This filter returns the onMatch result if the level in the LogEvent is the same or more specific than the configured level and the `onMismatch` value otherwise. @@ -980,7 +987,7 @@ A sample configuration that only allows the event to be written by the appender ---- [#TimeFilter] -== TimeFilter +=== TimeFilter The time filter can be used to restrict the filter to only a certain portion of the day. @@ -1037,3 +1044,36 @@ A sample configuration that only allows the event to be written by the appender ---- + +[#extending] +== Extending + +Filters are xref:manual/plugins.adoc[plugins] implementing link:../javadoc/log4j-core/org/apache/logging/log4j/core/Filter.html[the `Filter` interface]. +This section will guide you on how to create custom ones. + +[NOTE] +==== +While xref:#collection[the predefined filter collection] should address most common use cases, you might find yourself needing to implement a custom one. +If this is the case, we really appreciate it if you can *share your use case in a {logging-services-url}/support.html[user support channel]*. +==== + +[#extending-plugins] +=== Plugin preliminaries + +include::partial$manual/plugin-preliminaries.adoc[] + +[#extending-filters] +=== Extending filters + +Filter are xref:manual/plugins.adoc[plugins] implementing link:../javadoc/log4j-core/org/apache/logging/log4j/core/Filter.html[the `Filter` interface]. +We recommend users to extend from link:../javadoc/log4j-core/org/apache/logging/log4j/core/filter/AbstractFilter.html[`AbstractFilter`], which provides implementation convenience. +While annotating your filter with `@Plugin`, you need to make sure that + +* It has a unique `name` attribute across all available `Filter` plugins +* The `category` attribute is set to link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Node.html#CATEGORY[`Node.CATEGORY`] +* The `elementType` attribute is set to link:../javadoc/log4j-core/org/apache/logging/log4j/core/Filter.html#ELEMENT_TYPE[`Filter.ELEMENT_TYPE`] + +You can check out following files for examples: + +* {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/MarkerFilter.java[`MarkerFilter.java`] – xref:#MarkerFilter[] matching on markers associated with the effective `LogEvent` in the context +* {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/RegexFilter.java[`RegexFilter.java`] – xref:#RegexFilter[] matching on the message associated with the effective `LogEvent` in the context diff --git a/src/site/antora/modules/ROOT/pages/manual/lookups.adoc b/src/site/antora/modules/ROOT/pages/manual/lookups.adoc index 6eea0d2afe6..33e9749801a 100644 --- a/src/site/antora/modules/ROOT/pages/manual/lookups.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/lookups.adoc @@ -25,8 +25,14 @@ be found in the xref:manual/configuration.adoc#PropertySubstitution[Property Substitution] section of the xref:manual/configuration.adoc[Configuration] page. +[#collection] +== Collection + +Log4j bundles several predefined lookups to assist in several common deployment use cases. +Following sections explain all these in detail. + [#ContextMapLookup] -== Context Map Lookup +=== Context Map Lookup The ContextMapLookup allows applications to store data in the Log4j ThreadContext Map and then retrieve the values in the Log4j @@ -47,7 +53,7 @@ resolve the variable for each event. Note that the pattern ---- [#DateLookup] -== Date Lookup +=== Date Lookup The DateLookup is somewhat unusual from the other lookups as it doesn't use the key to locate an item. Instead, the key can be used to specify a @@ -67,7 +73,7 @@ be formatted as specified. ---- [#DockerLookup] -== Docker Lookup +=== Docker Lookup The DockerLookup can be used to lookup attributes from the Docker container the application is running in. @@ -107,7 +113,7 @@ Log4j Docker provides access to the following container attributes: This Lookup is subject to the requirements listed at xref:log4j-docker.adoc[Log4j Docker Support] [id=environment-lookup] -== [[EnvironmentLookup]] Environment Lookup +=== [[EnvironmentLookup]] Environment Lookup The EnvironmentLookup allows systems to configure environment variables, either in global files such as /etc/profile or in the startup scripts @@ -136,8 +142,9 @@ when the `USER` environment variable is undefined, the default value ---- + [#EventLookup] -== Event Lookup +=== Event Lookup The EventLookup provides access to fields within the log event from the configuration. @@ -212,7 +219,7 @@ present in the log event. ---- [#JavaLookup] -== Java Lookup +=== Java Lookup The JavaLookup allows Java environment information to be retrieved in convenient preformatted strings using the `java:` prefix. @@ -265,7 +272,7 @@ For example: ---- [#JndiLookup] -== JNDI Lookup +=== JNDI Lookup As of Log4j 2.15.1 JNDI operations require that `log4j2.enableJndi=true` be set as a system property or the corresponding environment variable for this lookup to function. See the @@ -295,7 +302,7 @@ or ip address are supported along with any hosts or ip addresses listed in the *Java's JNDI module is not available on Android.* [#JmxRuntimeInputArgumentsLookup] -== JVM Input Arguments Lookup (JMX) +=== JVM Input Arguments Lookup (JMX) Maps JVM input arguments -- but not _main_ arguments -- using JMX to acquire the JVM arguments. @@ -308,7 +315,7 @@ https://docs.oracle.com/javase/8/docs/api/java/lang/management/RuntimeMXBean.htm *Java's JMX module is not available on Android or on Google App Engine.* [#KubernetesLookup] -== Kubernetes Lookup +=== Kubernetes Lookup For retrieving attributes using Fabric8's Kubernetes Client, see their https://github.com/fabric8io/kubernetes-client/blob/main/doc/KubernetesLog4j.md[Kubernetes Log4j Lookup]. @@ -333,7 +340,7 @@ relative to the log4j configuration file. ---- [#LowerLookup] -== Lower Lookup +=== Lower Lookup The LowerLookup converts the passed in argument to lower case. Presumably the value will be the result of a nested lookup. @@ -348,7 +355,7 @@ result of a nested lookup. ---- [#AppMainArgsLookup] -== Main Arguments Lookup (Application) +=== Main Arguments Lookup (Application) This lookup requires that you manually provide the main arguments of the application to Log4j: @@ -378,9 +385,10 @@ key must be followed by a backslash as an escape character as in `${main:\--file For example, suppose the static void main String[] arguments are: -.... +[source,text] +---- --file foo.txt --verbose -x bar -.... +---- Then the following substitutions are possible: @@ -428,7 +436,7 @@ Example usage: ---- [#MapLookup] -== Map Lookup +=== Map Lookup The MapLookup serves several purposes. @@ -469,7 +477,7 @@ page for information on how to set the default values. ---- -== Marker Lookup +=== Marker Lookup The marker lookup allows you to use markers in interesting configurations like a routing appender. Consider the following YAML @@ -538,7 +546,8 @@ You can use the notation `"${marker:name}"` and `"$${marker:name}"` to check for the existence of a marker where `name` is the marker name. If the marker exists, the expression returns the name, otherwise `null`. -== Resource Bundle Lookup +=== Resource Bundle Lookup + The resource bundle lookup retrieves values from Resource Bundles (see Java documentation). The format is `${bundle:BundleName:BundleKey}`. The bundle name follows package naming conventions, for example: `${bundle:com.domain.Messages:MyKey}`. [source, xml] @@ -550,7 +559,8 @@ The resource bundle lookup retrieves values from Resource Bundles (see Java docu ---- -== Spring Boot Lookup +=== Spring Boot Lookup + The Spring Boot Lookup retrieves the values of Spring properties from the Spring configuration as well as values of the active and default profiles. Specifying a key of "profiles.active" will return the active profiles while a key of "profiles.default" will return the default profiles. The default and active profiles can be an array. If more than one profile is present they will be returned as a comma separated list. To retrieve a single item from the array append "[\{index}]" to the key. For example, to return the first active profile in the list specify "profiles.active[0]". This Lookup will return null values until Spring Boot initializes application logging. The Spring Boot Lookup requires the log4j-spring-boot jar be included as a dependency. @@ -567,7 +577,7 @@ This Lookup will return null values until Spring Boot initializes application lo This Lookup requires log4j-spring-cloud-config-client be included in the application. [#StructuredDataLookup] -== Structured Data Lookup +=== Structured Data Lookup The StructuredDataLookup is very similar to the MapLookup in that it will retrieve values from StructuredDataMessages. In addition to the Map @@ -596,7 +606,7 @@ while "type" would have to be an item in the Map in a MapMessage. ---- [id=system-properties-lookup] -== [[SystemPropertiesLookup]] System Properties Lookup +=== [[SystemPropertiesLookup]] System Properties Lookup As it is quite common to define values inside and outside the application by using System Properties, it is only natural that they @@ -623,7 +633,7 @@ when the `logPath` system property is undefined, the default value ---- [#UpperLookup] -== Upper Lookup +=== Upper Lookup The LowerLookup converts the passed in argument to upper case. Presumably the value will be the result of a nested lookup. @@ -638,7 +648,7 @@ result of a nested lookup. ---- [#WebLookup] -== Web Lookup +=== Web Lookup The WebLookup allows applications to retrieve variables that are associated with the ServletContext. In addition to being able to @@ -698,3 +708,34 @@ located then the corresponding value will be returned. ---- + +[#extending] +== Extending + +Lookups are xref:manual/plugins.adoc[plugins] implementing link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/StrLookup.html[the `StrLookup` interface]. +This section will guide you on how to create custom ones. + +[NOTE] +==== +While xref:#collection[the predefined lookup collection] should address most common use cases, you might find yourself needing to implement a custom one. +If this is the case, we really appreciate it if you can *share your use case in a {logging-services-url}/support.html[user support channel]*. +==== + +[#extending-plugins] +=== Plugin preliminaries + +include::partial$manual/plugin-preliminaries.adoc[] + +[#extending-lookups] +=== Extending lookups + +Lookups are xref:manual/plugins.adoc[plugins] implementing link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/StrLookup.html[the `StrLookup` interface]. +While annotating your lookup with `@Plugin`, you need to make sure that + +* It has a unique `name` attribute across all available `StrLookup` plugins +* The `category` attribute is set to link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/StrLookup.html#CATEGORY[`StrLookup.CATEGORY`] + +You can check out following files for examples: + +* {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/DateLookup.java[`LowerLookup.java`] – xref:#LowerLookup[] lower-cases its input +* {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/EventLookup.java[`EventLookup.java`] – xref:#EventLookup[] extracts specified fields from the effective `LogEvent` in the context diff --git a/src/site/resources/.htaccess b/src/site/resources/.htaccess index 708c2c5df70..6bef5ba1911 100644 --- a/src/site/resources/.htaccess +++ b/src/site/resources/.htaccess @@ -54,7 +54,10 @@ RewriteRule "^log4j-slf4j-impl\.html$" "manual/installation.html#impl-core-bridg RewriteRule "^log4j-slf4j2-impl/index\.html$" "manual/installation.html#impl-core-bridge-slf4j" [R=permanent,NE] RewriteRule "^log4j-slf4j2-impl\.html$" "manual/installation.html#impl-core-bridge-slf4j" [R=permanent,NE] RewriteRule "^manual/api-separation\.html$" "manual/api.html" [R=permanent] +RewriteRule "^manual/extending\.html#Appenders$" "manual/appenders.html#extending" [R=permanent] +RewriteRule "^manual/extending\.html#Filters$" "manual/filters.html#extending" [R=permanent] RewriteRule "^manual/extending\.html#Layouts$" "manual/layouts.html#extending" [R=permanent] +RewriteRule "^manual/extending\.html#Lookups$" "manual/lookups.html#extending" [R=permanent] RewriteRule "^manual/extending\.html#PatternConverters$" "manual/pattern-layout.html#extending-converters" [R=permanent] RewriteRule "^manual/layouts\.html#enable-jansi$" "manual/pattern-layout.html#jansi" [R=permanent] RewriteRule "^manual/layouts\.html#EndOfBatch$" "manual/pattern-layout.html#converter-end-of-batch" [R=permanent] From 08d942ba7497a57ca5abac461c83d91c1e995d4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Tue, 11 Jun 2024 10:30:22 +0200 Subject: [PATCH 15/53] Improve extending layouts --- src/site/antora/modules/ROOT/pages/manual/layouts.adoc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc index ec12ec8ee36..8c376c290ca 100644 --- a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc @@ -1100,6 +1100,12 @@ include::partial$manual/plugin-preliminaries.adoc[] Layouts are xref:manual/plugins.adoc[plugins] implementing link:../javadoc/log4j-core/org/apache/logging/log4j/core/Layout.html[the `Layout` interface]. If your layout is a `String`-based one, we recommend you to extend your plugin class from link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/AbstractStringLayout.html[`AbstractStringLayout`], which contains convenience for some of the boilerplate code shared by `String`-based layouts. +While annotating your layout with `@Plugin`, you need to make sure that + +* It has a unique `name` attribute across all available `Layout` plugins +* The `category` attribute is set to link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Node.html#CATEGORY[`Node.CATEGORY`] +* The `elementType` attribute is set to link:../javadoc/log4j-core/org/apache/logging/log4j/core/Layout.html#ELEMENT_TYPE[`Layout.ELEMENT_TYPE`] + You can check out following files for examples: * {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/SyslogLayout.java[`SyslogLayout.java`] – simple, single-file, extending from `AbstractStringLayout` From d495947f48895810b6be8788f85f859333ce2226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Wed, 12 Jun 2024 11:03:58 +0200 Subject: [PATCH 16/53] Rewrite `plugins.adoc` --- .../modules/ROOT/pages/manual/extending.adoc | 119 -------- .../modules/ROOT/pages/manual/plugins.adoc | 278 +++++++++++++++--- src/site/resources/.htaccess | 3 + 3 files changed, 234 insertions(+), 166 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/extending.adoc b/src/site/antora/modules/ROOT/pages/manual/extending.adoc index 6b3e67233ed..4ce2ac33337 100644 --- a/src/site/antora/modules/ROOT/pages/manual/extending.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/extending.adoc @@ -208,125 +208,6 @@ a separate `FlowMessageFactory`. Applications may replace the `log4j2.flowMessageFactory` to the name of the custom `FlowMessageFactory` class. -[#Plugin_Builders] -== Plugin Builders - -Some plugins take a lot of optional configuration options. When a plugin -takes many options, it is more maintainable to use a builder class -rather than a factory method (see _Item 2: Consider a builder when faced -with many constructor parameters_ in _Effective Java_ by Joshua Bloch). -There are some other advantages to using an annotated builder class over -an annotated factory method: - -* Attribute names don't need to be specified if they match the field name or the parameter name. -* Default values can be specified in code rather than through an -annotation (also allowing a runtime-calculated default value which isn't -allowed in annotations). -* Adding new optional parameters doesn't require existing programmatic -configuration to be refactored. -* Easier to write unit tests using builders rather than factory methods -with optional parameters. -* Default values are specified via code rather than relying on -reflection and injection, so they work programmatically as well as in a -configuration file. - -Here is an example of a plugin factory from `ListAppender`: - -[source,java] ----- -@PluginFactory -public static ListAppender createAppender( - @PluginAttribute("name") @Required(message = "No name provided for ListAppender") final String name, - @PluginAttribute("entryPerNewLine") final boolean newLine, - @PluginAttribute("raw") final boolean raw, - @PluginElement("Layout") final Layout layout, - @PluginElement("Filter") final Filter filter) { - return new ListAppender(name, filter, layout, newLine, raw); -} ----- - -Here is that same factory using a builder pattern instead: - -[source,java] ----- -@PluginBuilderFactory -public static Builder newBuilder() { - return new Builder(); -} - -public static class Builder implements org.apache.logging.log4j.core.util.Builder { - - @PluginBuilderAttribute - @Required(message = "No name provided for ListAppender") - private String name; - - @PluginBuilderAttribute - private boolean entryPerNewLine; - - @PluginBuilderAttribute - private boolean raw; - - @PluginElement("Layout") - private Layout layout; - - @PluginElement("Filter") - private Filter filter; - - public Builder setName(final String name) { - this.name = name; - return this; - } - - public Builder setEntryPerNewLine(final boolean entryPerNewLine) { - this.entryPerNewLine = entryPerNewLine; - return this; - } - - public Builder setRaw(final boolean raw) { - this.raw = raw; - return this; - } - - public Builder setLayout(final Layout layout) { - this.layout = layout; - return this; - } - - public Builder setFilter(final Filter filter) { - this.filter = filter; - return this; - } - - @Override - public ListAppender build() { - return new ListAppender(name, filter, layout, entryPerNewLine, raw); - } -} ----- - -The only difference in annotations is using `@PluginBuilderAttribute` instead of `@PluginAttribute` -so that default values and reflection can be used instead of specifying them in the annotation. -Either annotation can be used in a builder, but the former is better suited for field injection -while the latter is better suited for parameter injection. Otherwise, the same annotations -(`@PluginConfiguration`, `@PluginElement`, `@PluginNode`, and `@PluginValue`) are all supported on fields. -Note that a factory method is still required to supply a builder, and this factory method -should be annotated with `@PluginBuilderFactory`. - -When plugins are being constructed after a configuration has been -parsed, a plugin builder will be used if available, otherwise a plugin -factory method will be used as a fallback. If a plugin contains neither -factory, then it cannot be used from a configuration file (it can still -be used programmatically of course). - -Here is an example of using a plugin factory versus a plugin builder -programmatically: - -[source,java] ----- -ListAppender list1 = ListAppender.createAppender("List1", true, false, null, null); -ListAppender list2 = ListAppender.newBuilder().setName("List1").setEntryPerNewLine(true).build(); ----- - [#Custom_Plugins] == Custom Plugins diff --git a/src/site/antora/modules/ROOT/pages/manual/plugins.adoc b/src/site/antora/modules/ROOT/pages/manual/plugins.adoc index c793d404c43..cbca9ff4ec5 100644 --- a/src/site/antora/modules/ROOT/pages/manual/plugins.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/plugins.adoc @@ -14,39 +14,227 @@ See the License for the specific language governing permissions and limitations under the License. //// + = Plugins -Log4j 1.x allowed for extension by requiring class attributes on most of the configuration declarations. In the case of some elements, notably the PatternLayout, the only way to add new pattern converters was to extend the PatternLayout class and add them via code. One goal of Log4j 2 is to make extending it extremely easy through the use of plugins. +Log4j plugin system is the de facto extension mechanism embraced by various Log4j Core components. +Plugins make it possible for extensible components to _receive_ feature implementations without any explicit links in between. +It is analogous to a https://en.wikipedia.org/wiki/Dependency_injection[dependency injection] framework, but curated for Log4j-specific needs. + +[NOTE] +==== +Log4j plugin system is implemented by Log4j Core, the logging implementation. +It is deliberately not a part of the Log4j API to keep the logging API footprint small. +==== + +[TIP] +==== +Did you know about *xref:plugin-reference.adoc[], the documentation extracted from the source code* of all predefined Log4j plugins? +Like Javadoc, but specialized for plugins! +==== + +In this section we will give an overview of the Log4j plugin system by answering certain questions: + +. <<#declare-plugin,How can you declare a plugin?>> +. <<#core,How can you declare a plugin that needs to be represented in a Log4j configuration file?>> +. <<#plugin-registry,How can you register your plugin to Log4j?>> +. <<#plugin-discovery,How does Log4j discover plugins?>> +. <<#plugin-load,How can you load other plugins in a plugin?>> + +[#declare-plugin] +== Declaring plugins + +A class can be declared as a plugin by adding a link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/Plugin.html[`@Plugin`] annotation, which is essentially composed of following attributes: + +`name`:: +Denotes the name of the plugin and is recommended to be distinct among plugins sharing the same `category`. +`name` matching is case-insensitive. + +`category`:: +A name used for grouping a set of plugins. +`name` matching is case-sensitive. + +`elementType`:: +Name of the corresponding category of the xref:manual/configuration.adoc[Log4j configuration] file element this plugin belongs under. +You can omit this attribute if your plugin is not meant to be represented in a configuration file. + +For example, xref:manual/lookups.adoc#extending[lookups] only use the `category` attribute and set it to link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/StrLookup.html#CATEGORY[`StrLookup.CATEGORY`] (`Lookup`). +On the other hand, xref:manual/appenders.adoc#extending[appenders] use both the `category` and `elementType` attributes and set them to link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Node.html#CATEGORY[`Node.CATEGORY`] (`Core`) and link:../javadoc/log4j-core/org/apache/logging/log4j/core/Appender.html#ELEMENT_TYPE[`Appender.ELEMENT_TYPE`] (`appender`), respectively. +The reason appenders use an `elementType` and lookups don't is that appenders need to be referred to in an element in a Log4j configuration file (e.g., `log4j2.xml`) whereas lookups don't. +See <> for details. + +For clarity's sake, see following examples: + +* {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/DateLookup.java[`LowerLookup.java`] (uses only `category`) +* {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/CsvParameterLayout.java[`CsvParameterLayout.java`] (uses both `category` and `elementType`) + +.Click to read more on *name collision* and *overriding an existing plugin* +[%collapsible] +==== +The `name` attribute of plugins of a certain `category` is recommended to be distinct and this matching is case-insensitive. +In case of a name collision, a warning will be emitted, and the plugin <> will determine the effective plugin. +For example, to override the `File` plugin which is provided by the built-in xref:manual/appenders.adoc#FileAppender[File Appender], you would need to place your plugin in a JAR file in the classpath ahead of Log4j Core JAR. +In an OSGi environment, the order that bundles are scanned for plugins generally follows the same order that bundles were installed into the framework; see https://www.osgi.org/javadoc/r5/core/org/osgi/framework/BundleContext.html#getBundles()[`getBundles()`] and https://www.osgi.org/javadoc/r5/core/org/osgi/framework/SynchronousBundleListener.html[`SynchronousBundleListener`]. +In short, name collisions are even more unpredictable in an OSGi environment. +==== + +[#core] +== Declaring plugins represented in a configuration file + +If your plugin needs to be represented by an element in a configuration file (such as an xref:manual/appenders.adoc[appender], xref:manual/layouts.adoc[layout], xref:manual/api.adoc#loggers[logger], or xref:manual/filters.adoc[filter]), following requirements must be met: + +* The `category` attribute of the `@Plugin` annotation must be set to link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Node.html#CATEGORY[`Node.CATEGORY`] (`Core`) +* The `elementType` attribute of the `@Plugin` annotation must be configured, if it belongs under a certain category of elements (appenders, layouts, etc.) +* It must have a xref:declare-plugin-factory[plugin factory] + +See {project-github-url}/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java[`JsonTemplateLayout.java`] for following details: + +* Layout's `@Plugin` annotation sets `category` and `elementType` to `Node.CATEGORY` and link:../javadoc/log4j-core/org/apache/logging/log4j/core/Layout.html#ELEMENT_TYPE[`Layout.ELEMENT_TYPE`], respectively +* All plugin declarations are provided with a `@PluginBuilderFactory`-annotated static method +* ``EventTemplateAdditionalField`` class' `@Plugin` annotation doesn't have an `elementType` attribute, even though it maps to a configuration file element + +[#declare-plugin-factory] +=== Declaring plugin factories + +A *plugin factory* is responsible for + +* Creating an instance of the plugin +* Receiving values (`Configuration` instance, configuration attributes, etc.) available in the context + +Every plugin that needs to be represented by an element in a configuration file must declare a plugin factory using one of the following: + +a link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginFactory.html[`@PluginFactory`]-annotated static method:: +What is expected to be received is modelled as method arguments. +Intended for simple plugins that receive less than a handful of values. ++ +See {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/CsvParameterLayout.java[`CsvParameterLayout.java`] for an example on `@PluginFactory` usage. + +a link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginBuilderFactory.html[`@PluginBuilderFactory`]-annotated static method of return type link:../javadoc/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Builder.java[`Builder`]:: +What is expected to be received is modelled as fields of a builder class. +Intended for more sophisticated wiring needs. ++ +.Click for advantages of builder class over factory method +[%collapsible] +==== +* Attribute names don't need to be specified, if they match the field name +* Default values can be specified in code rather than through an annotation. +This also allows a runtime-calculated default value, which isn't allowed in annotations. +* Default values are specified via code rather than relying on reflection and injection, so they work programmatically as well as in a configuration file. +* Adding new optional parameters doesn't require existing programmatic configuration to be refactored. +* Easier to write unit tests using builders rather than factory methods with optional parameters. +==== ++ +See {project-github-url}/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java[`JsonTemplateLayout.java`] for an example on `@PluginBuilderFactory` usage. + +If a plugin class implements `Collection` or `Map`, then no factory method is used. +Instead, the class is instantiated using the default constructor, and all child configuration nodes are added to the `Collection` or `Map`. + +[#attribute-types] +==== Plugin factory attribute types + +To allow the current `Configuration` to populate the correct arguments for the `@PluginFactory`-annotated method (or fields for the builder class), every argument to the method must be annotated using one of the following attribute types. + +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginAliases.html[`@PluginAliases`]:: +Identifies a list of aliases for a `@Plugin`, `@PluginAttribute`, or `@PluginBuilderAttribute` -In Log4j 2 a plugin is declared by adding a link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/Plugin.html[`@Plugin`] annotation to the class declaration. During initialization the link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`] will invoke the link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/util/PluginManager.html[`PluginManager`] to load the built-in Log4j plugins as well as any custom plugins. The `PluginManager` locates plugins by looking in five places: +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginAttribute.html[`@PluginAttribute`]:: +Denotes a configuration element attribute. +The parameter must be convertible from a `String` using a `TypeConverter`. +Most built-in types are already supported, but custom `TypeConverter` plugins may also be provided for more type support. +Note that `PluginBuilderAttribute` can be used in builder class fields as an easier way to provide default values. -1. Serialized plugin listing files on the classpath. These files are generated automatically during the build (more details below). -2. (OSGi only) Serialized plugin listing files in each active OSGi bundle. A `BundleListener` is added on activation to continue checking new bundles after `log4j-core` has started. -3. **(Deprecated)** A comma-separated list of packages specified by the `log4j.plugin.packages` system property. -4. **(Deprecated)** Packages passed to the static `PluginManager.addPackages` method (before Log4j configuration occurs). -5. **(Deprecated)** The [packages](./configuration.html#ConfigurationSyntax) declared in your log4j2 configuration file. +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginConfiguration.html[`@PluginConfiguration`]:: +The current `Configuration` object will be passed to the plugin as a parameter. -If multiple Plugins specify the same (case-insensitive) `name`, then the load order above determines which one will be used. For example, to override the `File` plugin which is provided by the built-in `FileAppender` class, you would need to place your plugin in a JAR file in the CLASSPATH ahead of`log4j-core.jar`. This is not recommended; plugin name collisions will cause a warning to be emitted. Note that in an OSGi environment, the order that bundles are scanned for plugins generally follows the same order that bundles were installed into the framework. See https://www.osgi.org/javadoc/r5/core/org/osgi/framework/BundleContext.html#getBundles()[`getBundles()`] and https://www.osgi.org/javadoc/r5/core/org/osgi/framework/SynchronousBundleListener.html[`SynchronousBundleListener`]. In short, name collisions are even more unpredictable in an OSGi environment. +[[PluginElement]] link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginElement.html[`@PluginElement`]:: +The parameter may represent a complex object that itself has parameters that can be configured. +This also supports injecting an array of elements. -Serialized plugin listing files are generated by an annotation processor contained in the log4j-core artifact which will automatically scan your code for Log4j 2 plugins and output a metadata file in your processed classes. There is nothing extra that needs to be done to enable this; the Java compiler will automatically pick up the annotation processor on the class path unless you explicitly disable it. In that case, it would be important to add another compiler pass to your build process that only handles annotation processing using the Log4j 2 annotation processor class, `org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor`. To do this using Apache Maven, add the following execution to your *maven-compiler-plugin* (version 2.2 or higher) build plugin: +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginNode.html[`@PluginNode`]:: +The current `Node` being parsed will be passed to the plugin as a parameter. -[source,xml] +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginValue.html[`@PluginValue`]:: +The value of the current `Node` or its attribute named `value`. + +Each attribute or element annotation must include the name that must be present in the configuration in order to match the configuration item to its respective parameter. +For plugin builders, the names of the fields will be used by default if no name is specified in the annotation. + +[#type-converters] +=== Plugin factory attribute type converters + +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/convert/TypeConverter.html[`TypeConverter`]s are a certain group of plugins for converting ``String``s read from configuration file elements into the types used in plugin factory attributes. +Other plugins can already be injected via xref:#PluginElement[the `@PluginElement` annotation]; now, any type supported by ``TypeConverter``s can be used in a `@PluginAttribute`-annotated factory attribute. + +Conversion of enum types are supported on demand and do not require custom ``TypeConverter``s. +A large number of built-in Java classes (`int`, `long`, `BigDecimal`, etc.) are already supported; see link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.html[`TypeConverters`] for a more exhaustive listing. + +You can create custom ``TypeConverter``s as follows: + +* Extend from link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/convert/TypeConverter.html[the `TypeConverter` interface] + +* Set the `category` attribute of the `@Plugin` annotation to link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.html#CATEGORY[`TypeConverters.CATEGORY`] (`TypeConverter`). +Unlike other plugins, the plugin name of a `TypeConverter` is purely cosmetic. + +* Have a default constructor + +* Optionally, extend from `Comparable>`, which will be used for determining the order in case of multiple `TypeConverter` candidates for a certain type + +See {project-github-url}/log4j-core/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.java[`TypeConverters.java`] for example implementations. + +[#constraint-validators] +==== Plugin factory attribute validators + +Plugin factory fields and parameters can be automatically validated at runtime using constraint validators inspired by https://beanvalidation.org[Bean Validation]. +The following annotations are bundled in Log4j, but custom ``ConstraintValidator`` can be created as well. + +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/validation/constraints/Required.html[`@Required`]:: +This annotation validates that a value is non-empty. +This covers a check for null as well as several other scenarios: empty `CharSequence` objects, empty arrays, empty `Collection` instances, and empty `Map` instances. + +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/validation/constraints/ValidHost.html[`@ValidHost`]:: +This annotation validates that a value corresponds to a valid host name. +This uses the same validation as https://docs.oracle.com/javase/{java-target-version}/docs/api/java/net/InetAddress.html#getByName-java.lang.String-[`InetAddress.getByName(String)`]. + +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/validation/constraints/ValidPort.html[`@ValidPort`]:: +This annotation validates that a value corresponds to a valid port number between 0 and 65535. + +[#plugin-registry] +== Registering plugins + +Registering plugins are done by placing a *Log4j plugin descriptor* (i.e., `Log4j2Plugins.dat`) into the classpath. +This file is generated using the link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessor.html[`PluginProcessor`] annotation processor at compile-time. +You need to configure your build tool as follows to employ `PluginProcessor` by the Java compiler: + +[tabs] +==== +Maven:: ++ +[source,xml,subs="+attributes"] ---- org.apache.maven.plugins maven-compiler-plugin - 3.1 + ${maven-compiler-plugin.version} - log4j-plugin-processor + generate-log4j-plugin-descriptor compile process-classes only + + + + org.apache.logging.log4j + log4j-core + {log4j-core-version} + + - org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor + + org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor @@ -54,48 +242,44 @@ Serialized plugin listing files are generated by an annotation processor contain ---- -[#core] -== Core -Core plugins are those that are directly represented by an element in a configuration file, such as an Appender, Layout, Logger or Filter. Custom plugins that conform to the rules laid out in the next paragraph may simply be referenced in the configuration, provided they are appropriate configured to be loaded by the PluginManager. - -Every Core plugin must declare a static method annotated with @PluginFactory or @PluginBuilderFactory. The former is used for static factory methods that provide all options as method parameters, and the latter is used to construct a new Builder class whose fields are used for injecting attributes and child nodes. To allow the Configuration to pass the correct parameters to the method, every parameter to the method must be annotated as one of the following attribute types. Each attribute or element annotation must include the name that must be present in the configuration in order to match the configuration item to its respective parameter. For plugin builders, the names of the fields will be used by default if no name is specified in the annotation. There are dozens of plugins in Log4j Core that can be used as examples for more complex scenarios including hierarchical builder classes (e.g., see FileAppender). See xref:manual/extending.adoc#Plugin_Builders[Extending Log4j with Plugin Builders] for more details. - -[#attribute-types] -=== Attribute types - -* PluginAttribute: The parameter must be convertible from a String using a TypeConverter. Most built-in types are already supported, but custom TypeConverter plugins may also be provided for more type support. Note that PluginBuilderAttribute can be used in builder class fields as an easier way to provide default values. -* PluginElement: The parameter may represent a complex object that itself has parameters that can be configured. This also supports injecting an array of elements. -* PluginConfiguration: The current Configuration object will be passed to the plugin as a parameter. -* PluginNode: The current Node being parsed will be passed to the plugin as a parameter. -* PluginValue: The value of the current Node or its attribute named value. - -[#constraint-validators] -=== Constraint validators +Gradle:: ++ +[source,groovy,subs="+attributes"] +---- +dependencies { + // Process sources using `log4j-core` providing `org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor` that generates `Log4j2Plugins.dat` --> + annotationProcessor('org.apache.logging.log4j:log4j-core:{log4j-core-version}') +} +---- +==== -Plugin factory fields and parameters can be automatically validated at runtime using constraint validators inspired by the Bean Validation spec. The following annotations are bundled in Log4j, but custom ConstraintValidators can be created as well. +[#plugin-discovery] +== Discovering plugins -* Required: This annotation validates that a value is non-empty. This covers a check for null as well as several other scenarios: empty CharSequence objects, empty arrays, empty Collection instances, and empty Map instances. -* ValidHost: This annotation validates that a value corresponds to a valid hostname. This uses the same validation as InetAddress::getByName. -* ValidPort: This annotation validates that a value corresponds to a valid port number between 0 and 65535. +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/util/PluginManager.html[`PluginManager`] is responsible for discovering plugins and loading their descriptions. +It locates plugins by looking in following places in given order: -[#key-providers] -== Key providers +. Plugin descriptor files on the classpath. +These files are generated automatically at compile-time by the Log4j plugin annotation processor. +See <> for details. -Some components within Log4j may provide the ability to perform data encryption. These components require a secret key to perform the encryption. Applications may provide the key by creating a class that implements the SecretKeyProvider interface. +. *[OSGi only]* Serialized plugin listing files in each active OSGi bundle. +A `BundleListener` is added on activation to continue checking new bundles after Log4j Core has started. -[#lookups] -== Lookups +. *[Deprecated]* A comma-separated list of packages specified by the `log4j.plugin.packages` system property -Lookups are perhaps the simplest plugins of all. They must declare their type as "Lookup" on the plugin annotation and must implement the StrLookup interface. They will have two methods; a lookup method that accepts a String key and returns a String value and a second lookup method that accepts both a LogEvent and a String key and returns a String. Lookups may be referenced by specifying ${name:key} where name is the name specified in the Plugin annotation and key is the name of the item to locate. +. *[Deprecated]* Packages passed to the static `PluginManager.addPackages()` method before Log4j configuration takes place -[#type-converters] -== Type converters +. *[Deprecated]* The `packages` attribute declared at the root element of your Log4j configuration file -TypeConverters are a sort of meta-plugin used for converting strings into other types in a plugin factory method parameter. Other plugins can already be injected via the @PluginElement annotation; now, any type supported by the type conversion system can be used in a @PluginAttribute parameter. Conversion of enum types are supported on demand and do not require custom TypeConverter classes. A large number of built-in Java classes are already supported; see TypeConverters for a more exhaustive listing. +[#plugin-load] +== Loading plugins -Unlike other plugins, the plugin name of a TypeConverter is purely cosmetic. Appropriate type converters are looked up via the Type interface rather than via Class objects only. Do note that TypeConverter plugins must have a default constructor. When multiple converters match for a type, the first will be returned. If any extends from Comparable>, it will be used for determining the order. +It is pretty common that a plugin uses other plugins; appenders accept layouts, some layouts accept key-value pairs, etc. +You can do this as follows: -[#developer-notes] -== Developer notes +* If your plugin has a <<#declare-plugin-factory,plugin factory>> (i.e., it is represented by a configuration file element), you can use <<#PluginElement, the `@PluginElement` annotation>> to receive other plugins. +See `@PluginElement("EventTemplateAdditionalField")` usage in {project-github-url}/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java[`JsonTemplateLayout.java`] for an example. -If a plugin class implements Collection or Map, then no factory method is used. Instead, the class is instantiated using the default constructor, and all child configuration nodes are added to the Collection or Map. +* Otherwise, you can use link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/util/PluginUtl.html[`PluginUtil`], which is a convenient wrapper around <<#plugin-discovery,`PluginManager`>>, to discover and load plugins. +See {project-github-url}/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TemplateResolverFactories.java[`TemplateResolverFactories.java`] for example usages. diff --git a/src/site/resources/.htaccess b/src/site/resources/.htaccess index 6bef5ba1911..a96bb00fa1e 100644 --- a/src/site/resources/.htaccess +++ b/src/site/resources/.htaccess @@ -59,6 +59,7 @@ RewriteRule "^manual/extending\.html#Filters$" "manual/filters.html#extending" [ RewriteRule "^manual/extending\.html#Layouts$" "manual/layouts.html#extending" [R=permanent] RewriteRule "^manual/extending\.html#Lookups$" "manual/lookups.html#extending" [R=permanent] RewriteRule "^manual/extending\.html#PatternConverters$" "manual/pattern-layout.html#extending-converters" [R=permanent] +RewriteRule "^manual/extending\.html#Plugin_Builders$" "manual/plugins.html#declare-plugin-factory" [R=permanent] RewriteRule "^manual/layouts\.html#enable-jansi$" "manual/pattern-layout.html#jansi" [R=permanent] RewriteRule "^manual/layouts\.html#EndOfBatch$" "manual/pattern-layout.html#converter-end-of-batch" [R=permanent] RewriteRule "^manual/layouts\.html#LevelPatternSelector$" "manual/pattern-layout.html#plugin-element-LevelPatternSelector" [R=permanent] @@ -98,6 +99,8 @@ RewriteRule "^manual/layouts\.html#Process_ID$" "manual/pattern-layout.html#conv RewriteRule "^manual/layouts\.html#ScriptPatternSelector$" "manual/pattern-layout.html#plugin-element-ScriptPatternSelector" [R=permanent] RewriteRule "^manual/layouts\.html#VariablesNotEmpty$" "manual/pattern-layout.html#converter-not-empty" [R=permanent] RewriteRule "^manual/plugins\.html#converters$" "manual/pattern-layout.html#extending-converters" [R=permanent] +RewriteRule "^manual/plugins\.html#developer-notes$" "manual/plugins.html" [R=permanent] +RewriteRule "^manual/plugins\.html#lookups$" "manual/lookups.html#extending" [R=permanent] RewriteRule "^manual/scala-api\.html$" "/log4j/scala/latest/index.html" [R=permanent] RewriteRule "^manual/usage\.html$" "manual/api.html" [R=permanent] RewriteRule "^runtime-dependencies\.html$" "manual/installation.html" [R=permanent] From e5b8161b0860858b6794e4cd998d05e2479a17c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Fri, 21 Jun 2024 17:30:47 +0200 Subject: [PATCH 17/53] Revamp `extending.adoc` --- .../antora/modules/ROOT/pages/manual/api.adoc | 5 + .../ROOT/pages/manual/architecture.adoc | 14 +- .../modules/ROOT/pages/manual/extending.adoc | 294 +++++++----------- .../modules/ROOT/pages/manual/plugins.adoc | 14 +- 4 files changed, 133 insertions(+), 194 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/api.adoc b/src/site/antora/modules/ROOT/pages/manual/api.adoc index 174c5abcfca..dc8d18df739 100644 --- a/src/site/antora/modules/ROOT/pages/manual/api.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/api.adoc @@ -85,6 +85,11 @@ include::partial$manual/api-best-practice-use-supplier.adoc[] link:../javadoc/log4j-api/org/apache/logging/log4j/Logger.html[`Logger`]s are the primary entry point for logging. In this section we will introduce you to further details about ``Logger``s. +[TIP] +==== +Refer to xref:manual/architecture.adoc[] to see where ``Logger``s stand in the big picture. +==== + [#logger-names] === Logger names diff --git a/src/site/antora/modules/ROOT/pages/manual/architecture.adoc b/src/site/antora/modules/ROOT/pages/manual/architecture.adoc index 448b3925735..f151b790811 100644 --- a/src/site/antora/modules/ROOT/pages/manual/architecture.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/architecture.adoc @@ -84,14 +84,15 @@ static method by passing the name of the desired Logger. Further information on the Logging API can be found in the xref:manual/api.adoc[Log4j API]. +[#logger-context] === LoggerContext -The -link:../javadoc/log4j-core/org/apache/logging/log4j/core/LoggerContext.html[`LoggerContext`] -acts as the anchor point for the Logging system. However, it is possible -to have multiple active LoggerContexts in an application depending on -the circumstances. More details on the LoggerContext are in the -xref:manual/logsep.adoc[Log Separation] section. +The link:../javadoc/log4j-api/org/apache/logging/log4j/spi/LoggerContext.html[`LoggerContext`] acts as the anchor point for the logging system. +It is primarily responsible for instantiating <>s. + +In most cases, applications have a single global `LoggerContext`. +Though in certain cases (e.g., Java EE applications), Log4j can be configured to accommodate multiple ``LoggerContext``s. +Refer to xref:manual/logsep.adoc[] for details. === Configuration @@ -103,6 +104,7 @@ During reconfiguration, two Configuration objects will exist. Once all Loggers have been redirected to the new Configuration, the old Configuration will be stopped and discarded. +[#logger] === Logger As stated previously, Loggers are created by calling diff --git a/src/site/antora/modules/ROOT/pages/manual/extending.adoc b/src/site/antora/modules/ROOT/pages/manual/extending.adoc index 00c05df047a..679aabb23b0 100644 --- a/src/site/antora/modules/ROOT/pages/manual/extending.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/extending.adoc @@ -14,202 +14,132 @@ See the License for the specific language governing permissions and limitations under the License. //// -= Extending Log4j -Log4j provides numerous ways that it can be manipulated and extended. -This section includes an overview of the various ways that are directly -supported by the Log4j 3 implementation. += Extending + +Log4j provides numerous extension points to adapt it for custom needs. +Several of such extension points are covered in the page of the associated component: + +* Log4j API +** xref:manual/customloglevels.adoc[Extending levels] +** xref:manual/markers.adoc[Extending markers] +** xref:manual/messages.adoc#extending[Extending messages] +** xref:manual/thread-context.adoc#extending[Extending thread context] +* Log4j Core +** xref:manual/appenders.adoc#extending[Extending appenders] +** xref:manual/filters.adoc#extending[Extending filters] +** xref:manual/layouts.adoc#extending[Extending layouts] +*** xref:manual/json-template-layout.adoc#extending[Extending JSON Template Layout] +*** xref:manual/pattern-layout.adoc#extending[Extending Pattern Layout] +** xref:manual/lookups.adoc#extending[Extending lookups] + +This section guides you on the rest of the Log4j extension points. + +[#mechanisms] +== Extension mechanisms + +Log4j allows extensions primarily using following mechanisms: + +[#Custom_Plugins] +=== Plugins + +include::partial$manual/plugin-preliminaries.adoc[] + +[#service-loader] +=== ``ServiceLoader``s + +https://docs.oracle.com/javase/{java-target-version}/docs/api/java/util/ServiceLoader.html[`ServiceLoader`] is a simple service-provider loading facility baked into the Java platform itself. +Log4j uses ``ServiceLoader``s for extending places where + +* The service needs to be implementation agnostic. +As a result, <> cannot be used, since it is provided by the logging implementation, i.e., Log4j Core. +For instance, this is why xref:manual/thread-context.adoc#extending[extending Thread Context], which is a Log4j API component, works using ``ServiceLoader``s. + +* The service needs to be loaded before <>. +For instance, this is why <> works using ``ServiceLoader``s. + +Refer to https://docs.oracle.com/javase/{java-target-version}/docs/api/java/util/ServiceLoader.html[the `ServiceLoader` documentation] for details. + +[#system-properties] +=== System properties + +Log4j uses system properties to determine the fully-qualified class name (FQCN) to load for extending a certain functionality. +For instance, <> works using system properties. + +[WARNING] +==== +Loading a class using _only_ its FQCN can result in unexpected behaviour when there are multiple class loaders. +==== + +[#points] +== Extension points + +In this section we will guide you on certain Log4j extension points that are not covered elsewhere. + +[#Provider] +=== `Provider` + +link:../javadoc/log4j-api/org/apache/logging/log4j/spi/Provider.html[`Provider`] is the anchor contract binding Log4j API to an implementation. +For instance, it has been implemented by Log4j Core, Log4j-to-JUL bridge, and Log4j-to-SLF4J bridge modules. + +Under the hood, link:../javadoc/log4j-api/org/apache/logging/log4j/LogManager.html[`LogManager`] locates a `Provider` implementation using <>, and delegates invocations to it. +Hence, you can extend it by providing a `org.apache.logging.log4j.spi.Provider` implementation in the form of a `ServiceLoader`. + +Having multiple ``Provider``s in the classpath is strongly discouraged. +Yet when this happens, you can use xref:manual/systemproperties.adoc#log4j2.provider[the `log4j2.provider` property] to explicitly select one. [#LoggerContextFactory] -== LoggerContextFactory - -The `LoggerContextFactory` binds the Log4j API to its implementation. -The Log4j `LogManager` locates a `LoggerContextFactory` by using -`java.util.ServiceLoader` to locate all instances of -`org.apache.logging.log4j.spi.Provider`. Each implementation must -provide a class that extends `org.apache.logging.log4j.spi.Provider` and -should have a no-arg constructor that delegates to Provider's -constructor passing the Priority, the API versions it is compatible -with, and the class that implements -`org.apache.logging.log4j.spi.LoggerContextFactory`. Log4j will compare -the current API version and if it is compatible the implementation -will be added to the list of providers. The API version in -`org.apache.logging.log4j.LogManager` is only changed when a feature is -added to the API that implementations need to be aware of. If more than -one valid implementation is located the value for the Priority will be -used to identify the factory with the highest priority. Finally, the -class that implements -`org.apache.logging.log4j.spi.LoggerContextFactory` will be instantiated -and bound to the LogManager. In Log4j 2 this is provided by -`Log4jContextFactory`. - -Applications may change the LoggerContextFactory that will be used by - -1. Create a binding to the logging implementation. -.. Implement a new link:../javadoc/log4j-core/org/apache/logging/log4j/core/impl/Log4jContextFactory.html[`LoggerContextFactory`]. -.. Implement a class that extends -link:../javadoc/log4j-api/org/apache/logging/log4j/spi/Provider.html[`org.apache.logging.log4j.spi.Provider`] -with a no-arg constructor that calls super-class's constructor with the -Priority, the API version(s), `LoggerContextFactory` class, and -optionally, a -link:../javadoc/log4j-api/org/apache/logging/log4j/spi/ThreadContextMap.html[`ThreadContextMap`] -implementation class. -.. Create a `META-INF/services/org.apache.logging.spi.Provider` file -that contains the name of the class that implements -`org.apache.logging.spi.Provider`. -2. Setting the system property "log4j2.loggerContextFactory" to the name -of the `LoggerContextFactory` class to use. -3. Setting the property "log4j2.loggerContextFactory" in a properties -file named "log4j2.LogManager.properties" to the name of the -LoggerContextFactory class to use. The properties file must be on the -classpath. +=== `LoggerContextFactory` + +link:../javadoc/log4j-api/org/apache/logging/log4j/spi/LoggerContextFactory.html[`LoggerContextFactory`] is the factory class used by Log4j API implementations to create xref:manual/architecture.adoc#logger-context[`LoggerContext`]s. +If you are using Log4j Core, you can provide a custom implementation using xref:manual/systemproperties.adoc#log4j2.loggerContextFactory[the `log4j2.loggerContextFactory` property]. +Another way to provide a custom `LoggerContextFactory` is to <>. +But this is generally discouraged, since it requires a more complicated setup and is intended mostly to be used by Log4j API implementations. [#ContextSelector] -== ContextSelector - -ContextSelectors are called by the -link:../javadoc/log4j-core/org/apache/logging/log4j/core/impl/Log4jContextFactory.html[Log4j `LoggerContext` factory]. They perform the actual work of locating or -creating a LoggerContext, which is the anchor for Loggers and their -configuration. ContextSelectors are free to implement any mechanism they -desire to manage LoggerContexts. The default Log4jContextFactory checks -for the presence of an `Injector` binding for `ContextSelector`. -If none are defined, the System Property named "Log4jContextSelector" is checked. -If found, the property is expected to contain the name of the Class that implements the ContextSelector to be used. -This class is then used for creating `ContextSelector` instances. - -Log4j provides five ContextSelectors: - -link:../javadoc/log4j-core/org/apache/logging/log4j/core/selector/BasicContextSelector.html[`BasicContextSelector`]:: - Uses either a LoggerContext that has been stored in a ThreadLocal or a - common LoggerContext. -link:../javadoc/log4j-core/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.html[`ClassLoaderContextSelector`]:: - Associates LoggerContexts with the ClassLoader that created the caller - of the getLogger(...) call. This is the default ContextSelector. -link:../javadoc/log4j-core/org/apache/logging/log4j/core/selector/JndiContextSelector.html[`JndiContextSelector`]:: - Locates the LoggerContext by querying JNDI. -link:../javadoc/log4j-core/org/apache/logging/log4j/core/async/AsyncLoggerContextSelector.html[`AsyncLoggerContextSelector`]:: - Creates a LoggerContext that ensures that all loggers are - AsyncLoggers. -link:../javadoc/log4j-core/org/apache/logging/log4j/core/osgi/BundleContextSelector.html[`BundleContextSelector`]:: - Associates LoggerContexts with the ClassLoader of the bundle that - created the caller of the getLogger call. This is enabled by default - in OSGi environments. +=== `ContextSelector` + +link:../javadoc/log4j-core/org/apache/logging/log4j/core/impl/Log4jContextFactory.html[`Log4jContextFactory`], the Log4j Core implementation of <>, delegates the actual work to a link:../javadoc/log4j-core/org/apache/logging/log4j/core/selector/ContextSelector.html[`ContextSelector`]. +It can be configured using xref:manual/systemproperties.adoc#log4j2.contextSelector[the `log4j2.contextSelector` property]. [#ConfigurationFactory] -== ConfigurationFactory - -Modifying the way in which logging can be configured is usually one of -the areas with the most interest. The primary method for doing that is -by implementing or extending a -link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFactory.html[`ConfigurationFactory`]. -Log4j provides two ways of adding new ConfigurationFactories. The first -is by defining the system property named "log4j.configurationFactory" to -the name of the class that should be searched first for a configuration. -The second method is by defining the `ConfigurationFactory` as a `Plugin`. - -All the ConfigurationFactories are then processed in order. Each factory -is called on its `getSupportedTypes()` method to determine the file -extensions it supports. If a configuration file is located with one of -the specified file extensions then control is passed to that -`ConfigurationFactory` to load the configuration and create the link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`] object. - -Most `Configuration` extend the link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/AbstractConfiguration.html[`AbstractConfiguration`] class. This class expects that the subclass will process the configuration file and create -a hierarchy of `Node` objects. Each `Node` is fairly simple in that it -consists of the name of the node, the name/value pairs associated with -the node, The `PluginType` of the node and a List of all of its child -Nodes. `Configuration` will then be passed the `Node` tree and -instantiate the configuration objects from that. - -[source,java] ----- -@Namespace("ConfigurationFactory") -@Plugin("XMLConfigurationFactory") -@Order(5) -public class XMLConfigurationFactory extends ConfigurationFactory { - - /** - * Valid file extensions for XML files. - */ - public static final String[] SUFFIXES = new String[] {".xml", "*"}; - - /** - * Returns the Configuration. - * @param loggerContext The logger context. - * @param source The InputSource. - * @return The Configuration. - */ - @Override - public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) { - return new XmlConfiguration(loggerContext, source); - } - - /** - * Returns the file suffixes for XML files. - * @return An array of File extensions. - */ - public String[] getSupportedTypes() { - return SUFFIXES; - } -} ----- +=== `ConfigurationFactory` + +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFactory.html[`ConfigurationFactory`] is the factory class used by Log4j Core to create link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`] instances given a xref:manual/architecture.adoc#logger-context[`LoggerContext`] and a link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationSource.html[`ConfigurationSource`]. + +You can provide a custom `ConfigurationFactory` in the form of a <>. +For example, see {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfigurationFactory.java[`XmlConfigurationFactory.java`] and {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfiguration.java[`XmlConfiguration.java`] of Log4j Core. + +You can use xref:manual/systemproperties.adoc#log4j2.configurationFactory[the `log4j2.configurationFactory` property] to explicitly set a `ConfigurationFactory` to be used before any other factory implementation. [#LoggerConfig] -== LoggerConfig - -`LoggerConfig` objects are where Loggers created by applications tie into -the configuration. The Log4j implementation requires that all -LoggerConfigs are based on the LoggerConfig class, so applications -wishing to make changes must do so by extending the `LoggerConfig` class. -To declare the new `LoggerConfig`, declare it as a Plugin of type "Core" -and providing the name that applications should specify as the element -name in the configuration. The `LoggerConfig` should also define a -PluginFactory that will create an instance of the `LoggerConfig`. - -The following example shows how the root `LoggerConfig` simply extends a -generic `LoggerConfig`. - -[source,java] ----- -@Configurable(printObject = true) -@Plugin("root") -public static class RootLogger extends LoggerConfig { - - @PluginFactory - public static LoggerConfig createLogger(@PluginAttribute(defaultBooleanValue = true) boolean additivity, - @PluginAttribute(defaultStringValue = "ERROR") Level level, - @PluginElement AppenderRef[] refs, - @PluginElement Filter filter) { - List appenderRefs = Arrays.asList(refs); - return new LoggerConfig(LogManager.ROOT_LOGGER_NAME, appenderRefs, filter, level, additivity); - } -} ----- +=== `LoggerConfig` -[#LogEventFactory] -== LogEventFactory +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/LoggerConfig.html[`LoggerConfig`] denotes the `Logger` configurations in a `Configuration`. +A custom `LoggerConfig` needs to suffice following conditions: -A LogEventFactory is used to generate LogEvents. Applications may replace the standard LogEventFactory by setting the value of the system property Log4jLogEventFactory to the name of the custom LogEventFactory class. +* It needs to extend from `LoggerConfig` class +* It needs to be declared as a <> +** Its plugin `category` should be set to link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Node.html#CATEGORY[`Node.CATEGORY`] -Note: When log4j is configured to have xref:manual/async.adoc#AllAsync[all -loggers asynchronous], log events are pre-allocated in a ring buffer and -the `LogEventFactory` is not used. +For example, see `RootLogger` definition in {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java[`LoggerConfig.java`]. -[#MessageFactory] -== MessageFactory +[#LogEventFactory] +=== `LogEventFactory` -A `MessageFactory` is used to generate `Message` objects. Applications may -replace the standard `ReusableMessageFactory` by setting the value of the -system property `log4j2.messageFactory` to the name of the custom -`MessageFactory` class. +Log4j Core uses link:../javadoc/log4j-core/org/apache/logging/log4j/core/impl/LogEventFactory.html[`LogEventFactory`] to create link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html[`LogEvent`]s. +You can replace the default `LogEventFactory` implementation with a custom one of yours by using xref:manual/systemproperties.adoc#log4j2.logEventFactory[the `log4j2.logEventFactory` property]. -Flow messages for the `Logger.entry()` and `Logger.exit()` methods have -a separate `FlowMessageFactory`. Applications may replace the -`DefaultFlowMessageFactory` by setting the value of the system property -`log4j2.flowMessageFactory` to the name of the custom `FlowMessageFactory` -class. +[NOTE] +==== +xref:manual/async.adoc[] discard `LogEventFactory` and any configuration related with it. +==== -[#Custom_Plugins] -== Custom Plugins +[#MessageFactory] +=== `MessageFactory2` + +Log4j Core uses link:../javadoc/log4j-api/org/apache/logging/log4j/message/MessageFactory2.html[`MessageFactory2`] to create link:../javadoc/log4j-api/org/apache/logging/log4j/message/Message.html[`Message`]s. +You can replace the default `MessageFactory2` implementation with a custom one of yours by using xref:manual/systemproperties.adoc#log4j2.messageFactory[the `log4j2.messageFactory` property]. -// TODO -See the xref:manual/plugins.adoc[Plugins] section of the manual. +In the case of xref:manual/flowtracing.adoc[], Log4j Core uses link:../javadoc/log4j-api/org/apache/logging/log4j/message/FlowMessageFactory.html[`FlowMessageFactory`]. +You can replace the default `FlowMessageFactory` implementation with a custom one of yours by using xref:manual/systemproperties.adoc#log4j2.flowMessageFactory[the `log4j2.flowMessageFactory` property]. diff --git a/src/site/antora/modules/ROOT/pages/manual/plugins.adoc b/src/site/antora/modules/ROOT/pages/manual/plugins.adoc index f8beb69933c..67dad5c9f25 100644 --- a/src/site/antora/modules/ROOT/pages/manual/plugins.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/plugins.adoc @@ -47,18 +47,20 @@ In this section we will give an overview of the Log4j plugin system by answering A class can be declared as a plugin by adding a link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/Plugin.html[`@Plugin`] annotation, which is essentially composed of following attributes: `name`:: -Denotes the name of the plugin and is recommended to be distinct among plugins sharing the same `category`. +Name of the plugin. +It is recommended to be distinct among plugins sharing the same `category`. `name` matching is case-insensitive. -`category`:: +`category` (optional):: A name used for grouping a set of plugins. -`name` matching is case-sensitive. +`category` matching is case-sensitive. -`elementType`:: +`elementType` (optional):: Name of the corresponding category of the xref:manual/configuration.adoc[Log4j configuration] file element this plugin belongs under. You can omit this attribute if your plugin is not meant to be represented in a configuration file. -For example, xref:manual/lookups.adoc#extending[lookups] only use the `category` attribute and set it to link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/StrLookup.html#CATEGORY[`StrLookup.CATEGORY`] (`Lookup`). +We will try to explain when to use `category` and `elementType` attributes with an example. +xref:manual/lookups.adoc#extending[Lookups] only use the `category` attribute and set it to link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/StrLookup.html#CATEGORY[`StrLookup.CATEGORY`] (`Lookup`). On the other hand, xref:manual/appenders.adoc#extending[appenders] use both the `category` and `elementType` attributes and set them to link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Node.html#CATEGORY[`Node.CATEGORY`] (`Core`) and link:../javadoc/log4j-core/org/apache/logging/log4j/core/Appender.html#ELEMENT_TYPE[`Appender.ELEMENT_TYPE`] (`appender`), respectively. The reason appenders use an `elementType` and lookups don't is that appenders need to be referred to in an element in a Log4j configuration file (e.g., `log4j2.xml`) whereas lookups don't. See <> for details. @@ -160,7 +162,7 @@ Each attribute or element annotation must include the name that must be present For plugin builders, the names of the fields will be used by default if no name is specified in the annotation. [#type-converters] -=== Plugin factory attribute type converters +==== Plugin factory attribute type converters link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/convert/TypeConverter.html[`TypeConverter`]s are a certain group of plugins for converting ``String``s read from configuration file elements into the types used in plugin factory attributes. Other plugins can already be injected via <>; now, any type supported by ``TypeConverter``s can be used in a `@PluginAttribute`-annotated factory attribute. From 34eb9f036e853c7d4b05bfd9e36e2e170a12087e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Tue, 25 Jun 2024 10:25:03 +0200 Subject: [PATCH 18/53] Rewrite architecture page and several other affected pages --- .../manual/customloglevels/custom/log4j2.json | 34 + .../customloglevels/custom/log4j2.properties | 30 + .../manual/customloglevels/custom/log4j2.xml | 28 + .../manual/customloglevels/custom/log4j2.yaml | 37 + .../customloglevels/filtering/log4j2.json | 24 + .../filtering/log4j2.properties | 24 + .../customloglevels/filtering/log4j2.xml | 21 + .../customloglevels/filtering/log4j2.yaml | 31 + .../customloglevels/filtering/logback.xml | 34 + .../modules/ROOT/images/Log4jClasses.jpg | Bin 31443 -> 0 bytes .../ROOT/pages/manual/architecture.adoc | 1162 +++++++++++------ .../ROOT/pages/manual/configuration.adoc | 2 +- .../ROOT/pages/manual/customloglevels.adoc | 170 ++- .../modules/ROOT/pages/manual/extending.adoc | 4 +- .../modules/ROOT/pages/manual/markers.adoc | 6 +- 15 files changed, 1117 insertions(+), 490 deletions(-) create mode 100644 src/site/antora/modules/ROOT/examples/manual/customloglevels/custom/log4j2.json create mode 100644 src/site/antora/modules/ROOT/examples/manual/customloglevels/custom/log4j2.properties create mode 100644 src/site/antora/modules/ROOT/examples/manual/customloglevels/custom/log4j2.xml create mode 100644 src/site/antora/modules/ROOT/examples/manual/customloglevels/custom/log4j2.yaml create mode 100644 src/site/antora/modules/ROOT/examples/manual/customloglevels/filtering/log4j2.json create mode 100644 src/site/antora/modules/ROOT/examples/manual/customloglevels/filtering/log4j2.properties create mode 100644 src/site/antora/modules/ROOT/examples/manual/customloglevels/filtering/log4j2.xml create mode 100644 src/site/antora/modules/ROOT/examples/manual/customloglevels/filtering/log4j2.yaml create mode 100644 src/site/antora/modules/ROOT/examples/manual/customloglevels/filtering/logback.xml delete mode 100755 src/site/antora/modules/ROOT/images/Log4jClasses.jpg diff --git a/src/site/antora/modules/ROOT/examples/manual/customloglevels/custom/log4j2.json b/src/site/antora/modules/ROOT/examples/manual/customloglevels/custom/log4j2.json new file mode 100644 index 00000000000..06be7e3bcd6 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customloglevels/custom/log4j2.json @@ -0,0 +1,34 @@ +{ + "Configuration": { + "Appenders": { + "Console": { + "name": "CONSOLE", + "PatternLayout": {} + }, + "File": { + "name": "FILE", + "JsonTemplateLayout": {} + } + }, + "CustomLevels": [ + { //<1> + "name": "VERBOSE", + "intLevel": 550 + } + ], + "Loggers": { + "Root": { + "level": "ALL", + "AppenderRef": [ + { + "ref": "CONSOLE", + "level": "VERBOSE" //<2> + }, + { + "ref": "FILE" + } + ] + } + } + } +} diff --git a/src/site/antora/modules/ROOT/examples/manual/customloglevels/custom/log4j2.properties b/src/site/antora/modules/ROOT/examples/manual/customloglevels/custom/log4j2.properties new file mode 100644 index 00000000000..6033c0851d5 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customloglevels/custom/log4j2.properties @@ -0,0 +1,30 @@ +# +# 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. +# +appender.0.type = Console +appender.0.name = CONSOLE +appender.0.layout.type = JsonTemplateLayout +appender.1.type = File +appender.1.name = FILE +appender.1.layout.type = PatternLayout + +customLevel.0.name = VERBOSE #<1> +customLevel.0.intLevel = 550 + +rootLogger.level = ALL +rootLogger.appenderRef.0.ref = CONSOLE +rootLogger.appenderRef.0.level = VERBOSE #<2> +rootLogger.appenderRef.1.ref = FILE diff --git a/src/site/antora/modules/ROOT/examples/manual/customloglevels/custom/log4j2.xml b/src/site/antora/modules/ROOT/examples/manual/customloglevels/custom/log4j2.xml new file mode 100644 index 00000000000..357d1979329 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customloglevels/custom/log4j2.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/site/antora/modules/ROOT/examples/manual/customloglevels/custom/log4j2.yaml b/src/site/antora/modules/ROOT/examples/manual/customloglevels/custom/log4j2.yaml new file mode 100644 index 00000000000..d702f4490d5 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customloglevels/custom/log4j2.yaml @@ -0,0 +1,37 @@ +# +# 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. +# +Configuration: + + Appenders: + Console: + name: "CONSOLE" + PatternLayout: {} + File: + name: "FILE" + JsonTemplateLayout: {} + + CustomLevels: #<1> + - name: "VERBOSE" + intLevel: 550 + + Loggers: + Root: + level: "ALL" + AppenderRef: + - ref: "CONSOLE" + level: "VERBOSE" #<2> + - ref: "FILE" diff --git a/src/site/antora/modules/ROOT/examples/manual/customloglevels/filtering/log4j2.json b/src/site/antora/modules/ROOT/examples/manual/customloglevels/filtering/log4j2.json new file mode 100644 index 00000000000..14f24253fce --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customloglevels/filtering/log4j2.json @@ -0,0 +1,24 @@ +{ + "Configuration": { + "Appenders": { + "Console": { + "name": "CONSOLE", + "JsonTemplateLayout": {} + } + }, + "Loggers": { + "Logger": [ + { + "name": "com.mycompany", + "level": "INFO" //<1> + } + ], + "Root": { + "level": "ERROR", //<2> + "AppenderRef": { + "ref": "CONSOLE" + } + } + } + } +} diff --git a/src/site/antora/modules/ROOT/examples/manual/customloglevels/filtering/log4j2.properties b/src/site/antora/modules/ROOT/examples/manual/customloglevels/filtering/log4j2.properties new file mode 100644 index 00000000000..a2d008872e1 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customloglevels/filtering/log4j2.properties @@ -0,0 +1,24 @@ +# +# 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. +# +appender.0.type = Console +appender.0.name = CONSOLE +appender.0.layout.type = JsonTemplateLayout + +logger.0.name = com.mycompany +logger.0.level = INFO #<1> +rootLogger.level = ERROR #<2> +rootLogger.appenderRef.0.ref = CONSOLE diff --git a/src/site/antora/modules/ROOT/examples/manual/customloglevels/filtering/log4j2.xml b/src/site/antora/modules/ROOT/examples/manual/customloglevels/filtering/log4j2.xml new file mode 100644 index 00000000000..bc5d5bb726b --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customloglevels/filtering/log4j2.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + diff --git a/src/site/antora/modules/ROOT/examples/manual/customloglevels/filtering/log4j2.yaml b/src/site/antora/modules/ROOT/examples/manual/customloglevels/filtering/log4j2.yaml new file mode 100644 index 00000000000..8b7d3ce2c02 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customloglevels/filtering/log4j2.yaml @@ -0,0 +1,31 @@ +# +# 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. +# +Configuration: + + Appenders: + Console: + name: "CONSOLE" + JsonTemplateLayout: {} + + Loggers: + Logger: + - name: "com.mycompany" + level: "INFO" #<1> + Root: + level: "ERROR" #<2> + AppenderRef: + ref: "CONSOLE" diff --git a/src/site/antora/modules/ROOT/examples/manual/customloglevels/filtering/logback.xml b/src/site/antora/modules/ROOT/examples/manual/customloglevels/filtering/logback.xml new file mode 100644 index 00000000000..09491b95715 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customloglevels/filtering/logback.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/site/antora/modules/ROOT/images/Log4jClasses.jpg b/src/site/antora/modules/ROOT/images/Log4jClasses.jpg deleted file mode 100755 index ea16084bd681e085c769f67d39a108528a305234..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31443 zcmeFZ2UHc?mNr@lh-Aq*NRTW_&RayXfaENabI!?45KwXuP*8G|oHIxU0m(sfQj+8h zo4sFg?m4%w-0r^JJ^p{ZH{NI%<+f_CT2*tcx#s-lH|GK|g;)gc%1Oye0Z2#yfCT;m zh#5cvxO4k9+HKT3XlQ8Y=yx!%2(hs+F|o+-32+Ii$Z4pl$SEml86Ggv(y`N1QZnBo$G`*|RNVz`At58*LP17FML_{u`-0yC zD0ry&_a2GgCQvp+qjMzW@{dWsL;s|#l}KfHkAd6BDF7XVn1qy!obf&rGYcyZFCV{v zppe8aP_A1jD99*i*K#4k8b0OE29}Y63}t^ z-ywVwlU~+}PS35fM`YwQj6ux6v&6W6E!rQF{nrEw_#a93cftNmu4w=Z83~*`WIO-@ z95tbdS5MgN?AqJACVZ+AKDwGObH@*o&sTn|7R`*Lh6MP8Wfn>uiuJ_9 z#67Sg0vzg+voR+npnnktA5MrGbyV+)$@7KtsBJ_^>xgu3x<`g zgzWyuW|9$~_s2;2x;%?B-EK2_#QnD1zD2qM$XT)eajnaKLUoNC8M>A7Vwr_cMvjiyzykT&}~<;N%8v1(X6J}K4tm0i#4$iXMFMrx4F0i`*4=a zj{B4dH-*~fzUyoz%$v9^m~xd4$#Qx z3tqUvwh;gdV}^Yd`0=e>Wmtqe^Kr#y(nO{)CSPalR!5>Ei(M^h4zdV&0w=ZwcfVnX z>Uh>Wsq$Y+cjpPpDymBFuRAz+FOFEdxu3vU%O*K0zq`s9RV0iTrN=VH^0{fui*%{e z22H=RIa)l_;=kZ)!knMAoWAw=0nZkjNp*Q6-W!ZQdVwcVc)}8#6q(qu+_JV;o(RC; z5qKmX9|+l>Nw~C}Ura*wyuA@XcOC+G%`cAtQj44s09s88%&YdTivcAr`bI&6dYoEy z($*>6?hjYa_u<74hD&b)=RdwM(S9VKX&7x(YZkgRjnn7N>XE2COs#nA6DuZ30Go2i z#lGzc;g_=)r%2rk-hdt+7eg0MP7y$qG6J|`mxur$I%{xTd+6D*W4%=1%A=(w6%MIO zE!~>nyLpk>^XfJY^vUIq4O@}YNiWkC+~)6wsRy=e>}@^baep&{B~nEHZr&~7zAVmm zZO{DFaqdg{e-fe0g$8$OQ?q`HNfKlLwPH~?*w;xGKQeRL^c%W+S}cIk0US0Iv_%dKeA zg?}NG3*mD#$W2LUe!odmToM4s`}d3gG(7*?)Blds+428s!T-;+z!3ouR=(a_Ymd;? z7;)urn!mth>Px(|+CJI&K)5#&?ZM#0HpVsx=R*L!_a>p+pr?|fPs609sM}}o@?D;? z)KNRhR3CGb70YE%T-b;+XQh29IJi&qLP(=~M#K;SJm$a!Z8w zUU4MVV*qRL?<=?$NEXmD!DIvwV{vI74p~P{M*xdI?ctKe0ipk2ZDGaczkKzCvz)g& zK1Z5mhH4g4qp!-YNFW(#-LpLnaFzVetS!Yd27@OV=oxc~Pw|WsPyBg~x zD-tWqYZb~2Wc5z4n1QAr##lOO$j^= z2Q}c2Q>$sc$94*(N!&td(Vew+7_vxZlVm97(}kgfpW?rU5Ba=={^Y*aj|_XbY_9(b zG6Hy11gaOxA@o4OGBn0w&G2h@I!!n(tg8|NBNza6R?YL(Lj;gE5rO~$JX_9LEA2T8 zk7lInTN(l;zCWtBT)h((=O61qIl67PT~cIRUs)TvJTG3y__jS0_qp`#NHfa3z5^Jj z$KroINS)dMR8!vT*GRF4%N2b#QRx15JYSigjiZqc+u4{+k(^{K0D zMpd-T8KJ;G9?Lu}Bpz`ZBR0pRBBEJ^{0KZZ#dvG62MA*Ok)=TK zI$B^6+Rd2YxRJCYfD`O$1TZYGe``YTSF@Tb`DExI?2C&5j1t7VJ!6@g`=E}!#|Z=D zp|{X;@{%H%%DSm@<&V4u#Hrx8rk)92Pk<$~5~d5<)PEaSt5A;(X<-~S6R4#}4ct$M z365im09JJ%m5tX8IsQHYHp9tX;#>oXwV(eX7XwnYpaPR*pvJ^Ymw`B2R-^ zvjS8_^0qX#V)sa3zE%du2p|u-VG{2cXVR;+W04VB{v?z+?bd&G+%!Q)7>5WTqUAuw zb>Y$-0gSv^M^4+bhxzS5f3f95H*Cg-G$D%%=SZ^%U`kU3Z1BAxE$vpVJ?LM$BX&Xm zLUYBQ)R1&YMbp8j@o1BHR6L(Mry$RyHAk=i}yh2N9 zE#&SNT^!rh6jxBdi&b!yh&i#ZulP_p6}`h+{>r2C#juT!WnPZI7cZzd<)YaY=z;cA z?gaN@bUaS&NLRByQa_Xyt=)G6?TFa)BpjXdkLMZu{dvOR^AxXaXZD`X$Wv~Ygt+!; zR!ls|XdZj8U8VbBwIcSip97POnEtN&!Pw{ekER~qJOQ=(Z>c47;#k!>ymR4B5-yS{ z$$1R8RDBO04Y0AX)=-22dL~D&s)nLSHc}6_ia^5`c?wPtIPz_VIncr48c@h3E!iInTx}CwLu@gmFM&fRX$t>U<07N=7i9ZIZ-#AatoSr0 zCQkOZwG&K(#-oyJeY4f$Yr8a_v3$XhVjF@iJsgFsO@eCNO0|ms$TQ&t*xYbm*A^Jw zJObGNY*}^o>Xs#U)HqCV*;a0{gKw8Oov~=U>Z_J+a|Xm=H5^?G`d*Oig^c*YN0XJI z%J)gw)Zgw0duZQNe>BMSfi7Bz+s-D{C$-4zrXbR=`{d@Zi!JCG{WTk*R#g=;fdD3z z4w%n;I%y(&G(|dCC0xqwAnjrDHmN;Hn=ZaO*!D^!)x&zyc(F2VvrmnDGT#ZR_WW=T zKfbx-VEo_ekSl24emUEboub+5>eN5w4)rSQR;SWfQ#1xKFm zGvW3zsWidAUJ+HgahrZ+&Uhbiy?iRFn#?92kC>0yO}Ixga{QR-Fp1eiZ_7JDO96n` z$0!pKYUAuJm6geTG~9ic&v0?B>SpR>z6l?vSxt_0?zi9G>v$$aC9eoM7sQrht%=GG z9c{a%{X=Fc7y&3*GPq%U&e*}1W%2_GTUQ_$ppov&gf99Rz|_rE9otUEx+ZOk3_21k ztm}E_^Q{>Rt#iY?HQ#qF*1RVyxyWTmpO-tL;qPnzIp!lz^CpRuiFYD$hudF zy(m3q(@+l@dWvr{$d=!%EqyGP3@*qj?P0i}!3)ieB0p1w2m6vB0OVx^u>6^~Ds0R7 zO>^bGMpas;aeiOEp0ulN&g`=YW72~k+(stdi2(poXsJNAhzVJVD=wae7tb=1XU=ph zbvC=bWsa1DOl&HHj7T{VhG~tV^Q3!;RWsjS>J>IQCmgL40d%U%U%4;s6c6*|em;9~ zoIaRfXUDPnJ*9S5Q3&^IvO5e~63zC?V6wkI%3z<{3f84OkL}4RMb|aT{4f%LyroP;QVU&?<&{gl#YNtDbUu-}0_8lSDQVZ-w zLRKw@A$x|PkVK%#NAG^!vi|oAlCanHl(vOl2OiCvs;EbBv%=WVv)|1a!oOFOF|ZuK z5Wu~56JxXl;ZN3|&%Rk(ObGI7Sc-K#$Vyevnw~d*&)$3Yk>CDnhMjgKKlhK5pey;k ze`y#KHR!L?bFba*Yj8?QW)v6vAj)v!I5pn~9><>d6P77@mt}L`NF8*P-wl7_C#dHH z-2Si9Z>R(v>kBCii68I8y{Ii3yLo1^t-Y%Jc}|- z4{|M%T};cf%X3rXgB`d(KCclU8X2>j?^djBh#c{*3Fg{D0Z5`F`(kff)|;*-1t+79 zO{jjCr~6T~^Qf8o}Av0Z{(kR2KPZJlMm6qw^|=dXT6zJEXaDn2)P15zpWEh5XV!D&Qnwt?U$ z3l)jk9s0aYD`K5hp}mHx5aqYf>dfdjw!N}wQ}Jr0Q?C?O3`K7%(fUc-h}bW2UXb%B z!AK>+6{Xr8DZ&_aPVVyOxw?LEYU;8hpHOa8aiw6Aww-aEr!C%_a(f~C1OEp@Cp|&8 z;t+r+-wNX!-pz+nLo?kJ0}MN%MA^~I=Ja*bK<^3Qt-*OyYiagAddS0rKY^g;HXd|e zO|+Us)ho0Wq}*28432m}l=Sf0o`8-b3LQE$3WCZuPB?2$JAc6qa030kMTHpEAoi$% zp6=H;3639^-3&Hl`U?VH!Iqy8?rjk4 z;jF>i>J#A!1Nx}W@S8C)Tkzfj%megqQ<@+JH_}^BcQcU6OX-w$wzX1L=8$8f>qV8R zBF!iEW1wQOM3d9`J6K(0uhi97T_$dx#9v4!>1kP?Je&#ljYJ;df8l#Esd4BzZ8}!@Me+2Rf;O6Db8~3d6>Y$E)z3_lu4!skfW#|^|^ zwAs4f4Ffd~A3F753js*-^45~hQx5S`VNCyAleiR}Re7dkt=p1dE`Y`+hR&3N;@`y2 zT;yM5b-MQJ!aihYsg6?qe)b!~*H=={LazD)uaKu`XACqynZ=gic!ppOm!*8?HZ++?bU z$`4MMLBWCqRh#bY0@|BFyS<9M${_UVMLvEuoKFQq)~032v!Kl~!eGwuyFLV4evkeM z2Pv~kmI90dJp0`zRLaFM7@P79{bJVkPvmy6o`V%A$eW}>-t7QHc?*`rA6kM>tC&+ zJ+>r{z>!q=Yb0|s>Kykm+XuKGGmM4jr6)bg(%%8rP@>q_Jn%4AuWigE*uT#p(! zvZETfAS6pYi-Whi8(gq_#ywNwfhd5M5oIzJW>qetzI?^0viGXS(2}@-abV!}UR&yS z5@Y8|8$y$A|6VE!+0gZtbW8GtI8(;%kTt200pX*D1Tj?y$au?r`t7fmI9218UTI@# z>?iJSs`NIo6nUX{=lz?vqFfm?UPV5L3~^vjA*|!N8^x6%Deyur0KGd$%FDzxXG&Xo z*#ON_TI;@dNKL4-i1}C7Wb=y-wt$Glqi|1TB3}*b)M` zygdV5&o1`24%v$e^WnE$oWntfNn~~M==j#lQYMxa(BB!mD=vsaJzm?)vsZG3HO>Dw z6s>_x{^{0OnBLxgNf2b+d>FbX2722p>T9Ttb@3rn;RKFh)Zz>dhUq7Joo-e z6uL27>hGRh3;Ti%?RTdfb*3o7hyWxM--4XTotCrD?6;4J0{F6W85t^L->y@mnK>lF(8 zC*5Iz9_=57EZ|US_~?>s_dzD!%uLtF*B>(&pShF_#C47nmql5QOK|74Bxxfp33;B) z=EcaE&I%X*AfxJ%1RN|g?_`QVy(IOa7n0f5HYW}#zyNnD_zxx`;>RW^2Bjt+O{bC*)~-r z3r0n?RuFJY$+`2Hu6I0r^1f1VQL3WCldqj-O`{9jcpVFnk7-6Sr5?i3(U9qBZ*|p0 z1c13kZZx-1{Twu9K0S7Y+{ixdtRUhb6A2t}8y~27y;@;zOQ6*-I{qQ`*EeEs2ie{> z{{Bpcy~2#Q_fPPR8}C=2)-!%O{DKdEI5g|m{o@0=>}~E*2ODprLW|SM>Ez9ci%*4j z?>z8%zY_Xnt?v;%_JIGP&ziuZ3(5-9?T)Qx{N^!xUXLQX%CNfEBML8z$-kcjS(4Mo zm2RVKo3T21qQp#UOPjUH_wh;VwpH-tT_z~KV}9uzZmYhb_FC4~8avBOfWU~LHau={ zMJbedG3Z_}Pb9r55$h9mfsJH1Wl%bB8LT-#X3V92Zuf6h+TkeVi6^=b8puD&?s zo?OEV7B#NYBicRd6nNiwqv)YUEH6AoXd_Vtwhq|{&&|DzikadH1vc^u=a}@%CQ#nm z?Nap%5KMn-xc?dfREDIcuTpnR!~=xNlECw(k(Vv+<=J{jA1u5JH)fidFMzi3n}cx- z69Q-sIn(GQM2Z5T^;|0KC>4G|vm_0pcY_{|Krh#!K{m>dNx>w0#CMPxlm+EUhZFu^ zyjjDh@;&=~u8sp@wwj;xq&+7m1xJ5F#D5{<|LF4tbiW2JivuDYI|Oi{Oyz|N7Ho7a zoHGdRUV#@kp1L3ndHj|TVoUvAdS5}5%HUrU<|pp#6s^1?b&n&9CosbDt3x^bh9({tJxB)a!I=SrCVO~7 zyKZ?qSWmDj2R zagjm>Wy$4M{LT}Mjl49h%O)ke(2IoRJ7e!?WANu6`-}|=xN z-nXHbAwju9m~<>wmW(0gxV9M+gd&(i{BS@Lyl?Ck=I4Qzcu2y$Y^UUzh2{HvTYc5Qy~S%zq6Fk5nmOV3Q$%o=*r7c|f1ZBcY~~v| z+9qCx&%wtT9A^6n1xyZ~q1gmFzIZrma=e{XDq@|Esrsn47OMi&8K`(a@RM*JExy7N zRa~&^A$O8aI21+;zh?M!uNgj&`%$3T3j`5YkS&O~Q1_wxyoy`he9x`9HVRPEW7#Zy zHdf;AzRjAt{e+TQ8jVcEG5DTK3ysBeMAKL~**12|^rr+129!=@RCk&OxXNF8*Qmr> zOO97*Cg*iQQh)|zTi7g8&p5*nz*{hmhk}y*B})Fx83K?_IDn(+R$g*^o$1mT(I#I? zdc@T8`SnMvKYnjvTWFuvNRGLZva=VbEPF2MzB?#dHNmTP=wEUxc8dw71+xUiMnh4* z_xu?q_Flr&HM2 zgHHmL%c>iLTrSt}W}lWb+jEVXW8L|NzBb?BwF;(d&Z#SvE^jYD*JtEZrmUUb>=^_> z*R{bM=`C>1vZyika{o=|Ccfkom)50+ix4a+T@dpaz<}$~$R}x~28TS|;4iHjNbD-G zUv7vXmzN26%%wGlBpk)w+jNhXj3A&+MNxXLo0qDlBZ777iS^XWdj3E~TZvy0_RJXm zS`i~Q(`mnG30k*5hBw(lw=RY_iXyW2ARuq72@=T$*E$NK#NQ{QfJEpjAvJeef1c;< zNl^xKdeG09seBt&x&#|Q2{~;*Q;cWtjBjcTIlE1ot|z)@IHPCgc|*_)wG6F2eYE8t zs@nehut%MV!x$03lWGu$2b_WHq{Mv1mrGv$`>xh>GPlDhYe8Q*bC$(vnfi*bEOLHp zmxI;ZT3DqnMQ}~hPgS9Q+D8)B?8PRJ&eI=DGQd9W!#1phtqoIuS5F>j8!pHv5>+a} zNWfLqF;NwCRh(~#di<`Bw{o3YkR%iNc-0qMD^rb}QmiIpeV%ecgcq7+k!1R5@FXrh z7E>)eo-dIMB@c}fIm%G~sxpGNkgF8w6H%m)N$84?Exe^2dO~P_#ZX(^+zuC?SUB#5 zL>C#B;m?-+ONZzB^b=YjbQR6{x|Ts6q&Cco1Hih~8&uvO2;mwfTp!MGuIpNtnyVm} zg)5HWdW2*gw?95RTOkX<8|-AIYsduHbt<8=4jh#dHzC_-auxt z?a4V8po6YxX;B`@p?o;TNR$1 zwiTCJbBj(yD^68OUG8~tbk}5kf;2^DA?Ia8-~CD^LmM6-JTi36wjt}|KV_jSNM9_# z3e-EvT%4W}qU=;P=vl6aLfy7vwL;M04?RwhE9D=(BedKMkR~{fXil&v>!4vwGTYt1|kox z=tJix7W!~r3)IBLUfb}x-*Nfc~dVW862Hp^Id`j4|gj^AMBHw4Lq*7gLIkC(i z-r2<};-zg=SSieR)6;xb9BGUT5o(%p$F>7S1g@W`29qV*WO2 z(&psnEfcm6wh=%TeN&Sxxd;E3?UQ`*JK13!IJk56c~%aDk&|%zDffq}WFPrXYRo! zk_R73m9;t&bgZH5;KjMa}hftjKTIVdGyb(U5I+E!7>?9i_{bBX0T4AI^O*TJ^quo3B#7Z%S&L`K0DNs z=W=8>f6(z&!_$OW*2XXSy_>iKtJDv(ODEQzvq~2G=$sY613`3zVR}8b(K=@(@Ialv zPQMJf4hC`TR+%)_jdA`66@(8Z*waM9#ImaQzWGzc4^G zky3BXQ$+nE$USJL3d~?g$%vvg+uU3M9Jyf-P?5moQwFGnah0>&*Td*NA}YOz}4*$aVLg)zB!Dfr$<`Hi_fyWD145BcLk^5l+Yv#2IL2OF0+*8TlUh4d`j{_cDu?^s!;Elq2*|6H}H(^U3rE)W1V zXwj5gj$)VVFLBS&bfJ?B@F$I@9i`SW24}8SqSA_WA(yfN*;RpGTATYEd(A$yk`9vY z2(F|)DFwZWYt5CD(kh#l&C?oo%~Y5z_?i;M2w?x!FVb=k=aVGOd>ZTz9fT7TVam zX`*MW~AHvB1Tm$6;~)ctq503mWVe(8yA*Y{Zm2{LvsZ4>$8u*<*9heKk>P@K?- zvj6a-lq4X5zACu~*1rYXwj8NUKCT)(``r<_VCwxEi?Nc-o5<1u-l=xWz08e#2UPy) z^oe@U9*`CD47f2rcrhV8w)^-4k#^kG1D|M8ypJwD^ixO0<#h%?PwTNvDPMKwtL{m< z1A~2D2hIBX(&9_z2} z%A1OD+(wa8Pd|?$Gh01S1lG|Qc9mtYKB@3c@^n>=9Jd<_7dnL{>&FU&r`$oK$3g-w zDK+jF$4aiy;C8cjS(3;#X}-%t?TH;_LiSH-)HK#hXaNfVCqRq;a%T66!ctE6QBfb5 z?g&>4(;~;`kf|ac3ww-TB$QOS`S<+d50QT#QWZ;dEo{L+^)ZwJ6N&PnTfZQyRG>=f z#hqhJP~dU&5rLFF1#~rftK|gD4QAs_rrp$K4cY$|zW`a`JV%;=PH8HD51g`2D~5I% z93WR;Yk4yGfYN_NN`E1yf9I1D*61Tqc{-AN#TW(ydwB(F?FRfpeQ<-FW<@t<5UX4+ z=4<3WUoVjseuSTcFU}pv6XsiTEJbwm8Qgj9-^Q5|y7jz9^KMr4BGYRPsVXN3Z)s2W zsrx%(uUf}NHc&w$a9ttK#eZgwRIMc)Ub1QU+f=!#%xl?nJ7gBLb?@RTpC;C>E6yp8 zyM&3F4gNZfi6si`rhBAK=QunZ!rDIe9Cec|sd{{uNBI2_0=U~l%|gmp{F#tdPU-18 z9QwG0of;d0>Uteu5xRsvUfJNSO9j@0c28|D%4BNja;Y(YA$z#Gh%^xg$4e!QI)7hf zaJk$EuK!7>B-_2YkGL@USFALLk2YIQJ3(_u2AYf4?w!VPGf_?WSvi=f;+-FsjMo21 z1EYttgTYRmIs#~MGk}>TUZQoj9C2B-obGgAcjqoiEbiuzIwi*O_f`;?S=29@Ym?sl z838Ob(7?I%=Loe?zqdhOp4GsE6*S>jQy|@)1S$0aV@-+s;QD{{3wk!-?xJi$Vvc?l z2pVcKUW3b1u$HmQTB$7wbs~6#VVk|7zg%RLi`bJPx~JncfTMK;Xevt*KdA+Q_Lo2M z^JpDnl(t476kbqiDr>9^u*k4B#KU1=mNBGzxJB;L?KPj_CxQU3hXgv*dHr(4*CN63 zO8)&JO1U{i-kpK}G(=1%XkpT@{-lzt%j<#Txgq4vdPdKKZv?*&+wct;8#2OZ8w8YY zjGFWbE{NMq#-Y1&An8a}yc5bE@ayn&ueizC-CPu2koTe}E5{<^z0T)d=H9q+UWn^V zle@;7VQq1WaIUV;(&@Kyz{0Xd@TLB-{QG}adl}015lXpkEi%k9mi_SSUW0hnoLus7 zXem}%yueFol%q79$k3l)8b)xqWi6osgqDAl3xc_vF7VYwTuc6}=+i{b%%~eYQ#gX2 zuM|Q5t;*0g3p$7m;v*FI7MN9--|jy;B`t?3yp>jg(FcjjBnPs*`~CsEAN+!p_$lOe zzh5@4KIDi57YP1YO84`TwBU(B-B`&n_8j_4{5?bZ`cP?+&hP3`%>qBGAxpd?(6t3n z<64P8!$h7gEF7q*!LgY@+{`#i-lpw(hc3qWUFB_g_b#W~zitURHAy>gvB}Nj+1nD{ zTXiHy!KEiQ`_d&Vs?=l%ZL`h-S>EOvP*66oE)py!tXG0vg5`vCf0PsM1awPCIxp-c zZ9oopi$J&(dI}OrV7Ui+aU59Id9eV$Qvy93XY(H#*1nLcF)o%AN#GE?6ls2zFIpKb zBsT6t6rR6-I8uB1XBlx&wkWLna2APOnf;;d{4PTnxqn^!tHa1F4e%vM zphwJGsiy~nAar2Sr!5;(*6W5bPNp6)TmN|#^w_Vf58*-B&TzOkh({$!Br1fw*gk-l z%TNDy`8)JLk`u=0zHr0@_H_bUjacS-Oz!NAx{@iz%ggUCKN(G+5pW2`D(h>wn$V8Aw>OZ^R{c|>vhz2T25=M0O z+9`+RvKb;Ju?lnZXpe~o%uZXcd@d0HZX~Q%^bOqUDTLVIBxG(gFYV6u&b@}}$mMC1 zBy=^A>y+lZBihJ}WLJTzij>8CLqeNXoHMdQ%m})}Cv|bp_l}HY7>rJJlwaoG6G?oX zg0G}}s@se{r!#FHQJI}pT|Qc?=G%`*+1}NZ zk9$H$UzYQyPoEadC?6?H-V6G7y!YR^kbmzx&)@SGuIo23$Pn6p2`f9yQ-J$MDHgzu zF^~&wUU%Mh4|}+XJy?_u>d}VDIKfojj%|?~D`UQ-i8d`*jfVh;tw?mtL*_TX8L%2U z+WA)ppX-=tkkUqq(|uT`AyCSgEov%yt*OHCadPB*QtU~gvMqMywoOgHfB@PJC{T-;jb^oGfJeM=UN2ovTVu z(Oq*C0s4EQ`}z^YmU<6BKu;?uddm_m5+-psro%QbpP6(qwM=}vA#{o7MzzVrmNEae zi|x3M0-Rq7fi%1oQjYf<;DS&=dSoaC1>Pxd&LRg}0o-5|F~i6@uYW zXh1l?(ej!%e=%k`L6~fgL5lTjD>@3^P-sW}{-=723)PMKZ?PJd#1utOJW+Q?Ae#dZ-i5;b;yl%Z+t#IxDz-MWvT(Tq9pBruCauE`87PcV=Nf^;rEV zGJTgmy8EQ!xq6t^n}jIUc`P?O=XOLg)jSkX!+!)_MsqyZj7+JVw%&&!Szoz}K2@1a zn(~pR%`;*F_4Ki(0?R%=$OsC1>Z_&0Nm-# z(6+fKo89U33nSk*^yLo~QGb~@i{92*=brHzIEY9eG5NNXY<8y)zc9Am$JDB;4sV-Q z?~n5hb8aV0W}mlwZHtoW*&mAImB08yn&?yE*2VE^fOl9=+@;v0I#;{Z~gm%GSJq1y#zb>ESgZt3ADS&f9X zPOG^)&1Kcs&-N3Ud~i)J-9dY-lpK3okwt{$W(is$g;|1>%wA^<0!W+#30Jhl7MNR# z2!|2$$0=xWTYrGQnW`WuGW~X3_M8<7V`G>_lIAp*46*?AvC3qp*VB5HO%hE<%VDxC zzoMgjJZ-!;rh)>4UzN~udGsTDJod6fVDP{G%0T7*5(fkh<^8jAar)y~prm}5x7!5I zampf_I9}c3k>Ja0DJSO{Dl6p@3%Kd5!SsZfX>3BhQsw=Z)WzHfQN_#+L@`QmJPzT1 z=m)Zd4_0LyUcc{k0VGucQ%C)sa__2Dj|x1|?tpUB!Ujjme$yyV^fWzBO~S*-jZOC2?t z-JzC@;ix$HW&zFZ-q?T0rNr%vc4f^ojJRxZ=&UIQTg&JFu=ZFobCdR0d$muA^p+xx zXhM^*1S~H)&VWwwf!<1LS_MqpMq@*CAcq3hkkji}BW7boVL}P^O=$4yI;2?j8X=;a z5b1BAe`u-GiU2-z>wpfH&j1#sNG??j{pmQ-vO55x3i^r9V;c2AMdNdGgu9hJk?Z< ze|bl2?EJkp>B8@~Kz@HpjpPF}Vrdp{zTBm4U5QwGJS+E;8qE@W5rTFbB!i0n#ef+4 zTVy0HMQ+hO6%v%6?`S@67j=+ye8mS=EiJWNV(`Ob^)w9(!G*VFf3cd{qfzGmRNkrc zU;47~JO73KoBv!Y*)6?y9Dn2$)IYi@xBGdM?5>sj!(0-lNIV7%AvfydYuDz6sS>)D z*yiO9gKa_1gsuaJD%!n-WKgq&CgC`{kh5(Q?J9Ru>%8fvqU}n4z5`<)?k*|{Gt$>ZlyvjTqE}d8smaqRy)N zF11HCHjzB4vELa2JSfNgm%yDvuc}&L)Gxr$a@ycb;%M;v0@RT`Pj9f0%0dSz+d9iK zK}`BP&z3y2B2yYS?pAI>q0`q|%3F^aaB2(U!NRjUuRdJ=xrhZgE+_PCn`SQr+zoYn z4c`w=5kTb7->TF8^F|}c!eE{~)_)oI@(w3FMqGc+)9!7W9xjD-spZ9dfjOaO->Los ziewoa?KN4_<8CI>FGSy=FSqD(!;ZP($5r`oWsEr+vr{3EREBp)3UlTE8`z zGt!oSC9pE#$JcnnwU_X;3^l%}_riYi^WM^1&O#Lhp%c0h8SJsgFL`3)Ur2v7$fc0B zYxLl5TR7sdSokx^-d~%%oWhn${aZSJv^$`Y4GbKOFX>Z2@0ifivTGoX>}IeazvHPy^V?)XcM-^fDxj2m|z;ajT&1r z>0`z>7)Ud-(P+h%7JNy=nVA`-&?U6%9;g58(E^Mne#@n&N#^@Nat8~#KR>JCn(cs_ zSToKR;q#3&B+$(uwTcAoSgZWYK63t4#Op+{#$SnI>6_&Nw78VeUdTSvRlrWmk*Y>w zFld6Qn66wQJ(`RwG-w_#Ccj)m5n+w}CT3b0bb2?o{nOa&hg<>Rer!q*BlT37d_pA}ysGG;Il46j zu0LlGThI-k41&9*F~PF&*8T-p*kh8SaoPrdi3~9Kpm}J*Ms_<&u_>cBK;|+d0xQPktG)>QfZ+60doZSD>D!JpeU25X`obXOX-D z`LusK56V5>fImKlw-m2EvfkS{5w1I0@+#XzxORY4bufTBr6d7 zwRIf5>dK8MzEEp#=M4|Gkgm8uYJYvoYV$zq7K3>gmzQ0a_i|MM+V(3Ba6s$7CCc<#A3XcjB^Urq~Ksy*7l-Pbg5 z95e8}bi{uD!xqSP)0^`ra}PMoLp>->8Q!+D;Tj3>_HF&1@%v_-u_jl4*p-@Tagnv= zgaFK}ozd6Cn&qY+zPnN`;2PE5&-6%^Pwsd{&GoRXQCHcV!ASdVuWh0uJuPY51wm^4 zBRO4cJFn}|bH5m5Em*c7rzeA8%sLkMVytCdWdgdp4dx5O zRE!-}juj-u`qL8BMd?f?FU@Tx3v3U( zOVO_?l=l`cP5CM_+Zg-g*2hGKwVyA%b|^KUH`9(?(?{3H?HELQ45}?lH?FAbzbydq z(d4mnWb>JKvBCG*DY$J^K(CCi)D1k85g zrw7mexWa?D(2{yPbPYRn>sP6@pY%LUqmYm6ccxyox`skmgK=7ji*x z+;VvZV#r>wa8o=V)a_Kz`7dmN+)?&R7LS=LC}=}}F)v)mI_mp|zyga}wql|8UU)C- zs~nslfcCOk3!%uAKov9m*`@{-h-vM)bRyfw!<6@nha*iG|5lWe{(rJ4h3Lz=tx^(_@+5u1DjP zk+nSH&db(C_ZYe7S{<>Q`fG{PK77*hl$*&^{hIPvjcJBx3j8}A)ir!4edo&ER*7Pa zWx*CN!5!4P@dG`6p2=9AZLb3!OYLx%#tfw#kE|cg&T6+a&OoNgQOuQCVvzcr`01*Z z93FR}A&aczysKn1GF6si(N#^@cm2rE{k>P@wyC58%CxT4U6YkU3wN}Y;Ejm(+Ru17 z28$DiyC93ZxZ|IbNGz~(%E>gp!$K+o%35Q%Z`1%e8~kKn>+JQ zLoISt$_2i5z3Q{#$D74k+bb?n1`D?qH(Q>hZBAUyl&*xWdG55XI5J5nv!R;S)GB@& z)-88x>HOp3H`@n-WcnGC&?S0pjh`JB%^Kv$1fD+?J+VI;=aL>5$(bliqp)B-%5tO& zdMb&ehU14cq`L9OWT>bE|H+RwnQYR`cQ$yDq<6(n(da5(jucf=37HNZHJE;=``*xZ zXfPq&$}}6@J#|#1!y@XpT-Epem@`C2{dlCShJ-{%ue?xuigfO=BKJ4$Els)E0vxzVY`e81>g!CNW1LbI2 zTiBI1g!gLh+Y4_R{NyIiKHb4t={vDXmA8t5&rflAon(4?Wx2cQlj`mV<;#@Io)k&< z#|r1%v!9dx(C7(($@>}+EM%B=5%f%kXFPUJgS*<}mde?5l$HUfqOPxs}(QJJVnlE2#D)#Zu(wRQ(kbi%V-y2A( z5XdT>0tgP$(fMu_Ra4kzP%u2Br7-pFZ**Z|Vp?F@wB|VrQ6R|~9r%!qQmip8^<>6< z8;8WwwsDa%sVZyJ(W;vGRJoho$U;TGT&mCS`w9cr5hQ(G%F>fe%{f+4u8WY|E`wJY zLuX#X>;Xeeg7(YUISISIr|qYktJR)g$P7n`4CIHa!hHGg%Hiln#L5hu-nFdkZ_E}2 zUpz_maS)2pS2eJixL0~`_*0x==G0}n=TVd|m|%T+vGa-QQd{I(dH%Fs+cNEpyY1nl z3OB(nXIX|ScqQlbz^|5)E$5Wx(y_`*w&Zy*zj{y~cRR7J z1=>xVEIdy_tFwkw2Knjfhs6RB?nt!eD0WPld_pG1w+=jVWn%@YOlxmH3^CSuR^0j) zLmgwsK}KL2$HA8cx^#ae{E=LRV%w8Nl&bg-Zl4PEpZows@V@{S8qXMhu6$i7Ill5D zKhj`;(?!a{d$T1@`UbiCI+B;)I&jIxS)w7+xIF6PeC(_uTOw_Y0-YY}wRV^Zlf)4H zf{}}Q5Z2}-W`;TWQMI1y#A*AFj#NFuo{J7%KsucXjnj}qSt2N{1{pNB37@^nveCz%IkA!|tN;hwas^t|22jM1M z0b!(mxareellO;0E|FgH%CkUZrZl`7B)l7So92efb>v~3`Wbs_Yh34PS-hh7n-VfP z8%fFtLyhx=W(YH-!dS|i2;Fx{6ue?I8eWxbChs*1#54M7d#9otEm4-PT8xJ8{1RzO zY@dsbOUDMLh}S3@!qf~@Em84@-|8z47)}Hi3a9b}p#13}R8uni7tFh7@Q^O*%R|kT z(hT7XW7H?b-ClczRl8rXd>o1{x%m_4b?VHt(mm&JN}=J#tR@1go3i1|_g(vLV0a2& z=Y&r!)XxkjK9~D+VJSgf^E7)4QkPl=J=>t)ABE<5bq_ja*r}YNXRj!`rDd5(QoSb6 zmL}L}+_J(_6vELqTyc+@-sBMCS?m^VRktxMH@w=wLY>AWr@Y8ZHzA_cmn_})(m+-7 zVHHVR67d)k^N5GyU2`YB=k1ztUkg7T4cEPrIDj%>PbPS`CRe+qoh;55N5slGzx3!F z1l{0+vcEfbJGy4eapzf)Z((UfVM#b8JHmjS`nXV=xcY1Q|0P!^lHu*!vkOzJVXclB?_CYgBLP8T|S#*X_WCv(%fB${$WjC}(br=@we? z-6gy90Xje;=<;!M;xorfqiJng&xSbh3vArvJFg;Hk8@7bpM;Eu)|jPzPUUCiddszq zI+5^+M2F@uIJxw1CAh2-@< z!J7?)VS<$OYjioY`gYTOeUo zUNl{A z>TCp;CA7si4gJKA3|0(tc#vcVxiKDcc0sw$psVckkaa%$PDHu=k>G3omI03k1ZF0c z&Yt%3=wTEfeZ(MV@hNmgB=y1BH7W#wHf}UYDDoWF{gSf1{U)-l8UN7c?+vmW&*vrMlx( zNpmJ3-m>t1#_Jll7nx71@v}@d<64PNL>ElVUlrxFFMC{ntktTS_Tq1KtX@j$8Kk!N z`$U3AU*_4d_+KeU>gXmOZ|lru69~~}d4glpQo}|m5!f0-76Ae<RAla9_HiV1r1(CFlQM^vFrhOIB#>#cX1xIxJSf~!I zv*@9*=Eq+kg5|kiAZ|nOPg^dZ+$urB+5jZ1%jab_cJDHofKzeGGK&LAbCmFvk9fP= z6p}s zAJ)m?)K?>^_KyXlL$X@&9-q*PZs zWe(ZWSbRY9!h%e{fyKnChFe0ODA`Nw7j0hbT=V2OiQj;uXQR?^tx_n>#?Atwhy)F$8nYzmer&{ zVtO6mZcPCY$e$l01Xc?F3j`eaiVOmAG)-z_qZ6I>tw~H!fB)}>HEBkAYA0-{E$ztP zWoQPS^BL}Otdr&UN{oEhOw?boiBmzs|g3$_W^j7ceE zXQddpo(qwX%4BJ3zD_@XnFb-jm6qqKcEXZYld97tP}}hRs{}-|KuJiJ_qvqUW4w>* zAC2Y0%%Q%cOKlZv_fn8+B5!0559P!#xC_<>p_K`PYKfB8o9;ZlBuPw+iL*XqE(h;(I4xqNsMe|sPx z8KRlhC0eB}Lnc7CVXl7$l|CcTq7`mllW;E#p?oKSaQ=5biR-xs0cT7HVQV+!ve|5u z5{GJangzKtD~q&nO~{$T@h&~jcvV60TzDhmd~DwWzSVor$sj(;;{(idCe+?{LNa6q z(?cv*^2iQlcxCYQg*89te}J$Ta=ReaNV*0bGs;qf%&y(1QX@J(st zYUaA9q-vV^x(L-n=H43x#l8uVB7?+@m0mPt6SivS=Y&=DGI^0=-KI2zhk{%xS2I~N z%)}fdqBO54T=^=E0tg#;VJ=E_<#Yv0*50@nmagkjQ0ZYdgz7+V@DmGs<`{QUi+9Gf zE;<-5E70>-X|7l4f5U;*w5Mn>IjH9)J8pwb)PL6{vf9Ud1<98mlv&jvv8Tb~a6l;Q z{8PRMDQSnL%6(Jw0bAK99oS!yp&=@VfSKKm57G(O(9j(qjedB`)b@F<;6tsnIxMOShJ zz9jj8AK3SP3{Z)cN+9~+w|~;tGXLutDlt3IRO}Zr(u!PKaIucs8bfPF(b({##ZH>M zow1%^b{AzveC`4;sy!rQt!O9*;jyPVlAeC^n?Ch|Qg70e=#G^}Oegv!e1*Z-mGc&D zJVqFP-F##PTohQi(X-529NA1tH@!#7 z!{t+(&mO^#tmr*D0Q;m2#K;_kVi+Yl8mx5w)Z73y>SI&hAF-o|S^=Zbk>B-Ag|}B= zq({b;);PdvX`s!r4G+K?#rFMDDf>QYfx^O%QFiWWg_YzMz}+ElKa2uOFWE91y-a&Q zILiFz^XzQ&h>YQQGe!t?-j?;MhRQ|}u0?5x#M%)gxTr#^)aEo}Mbn-$V$$7$Fx_WQm6&#p9QMHUjiX<* zM@<^b*^pU?16{O?aFLGl*-k_nWnC2HNsT_vj|5v+Y(;U>Bu(ZtVcNf=ZVraDvW} zkq2^%0m;-Hn{uUF_S`CtUFD*=J>EmcA{wIaTVUEM?5Uiy6Xzq6UkRQ4{*0$tM zkkWlqR;pN4X6w2~%)uACk7J}naCaSut`a%wx6qelf|r{ z&vJ$xX>$eSP_g1AyQq)(t~{BB*8?y1r*tESY`dc!5ppAZa;Ht~deu6%I||f_?uuZc z;s5k?Fv$4tNxc2j-Z&GBh|Ltb^5H&rzd(lW-;7-hOVQhen?JrW7-^jwP&qEj6qGz1 zRv*@e@f*c+-{Ap1`uB=C^O6n_<@rWkPKtSuYNKFhS|ttq+#ihh;0NSDACLm*1AZml zcDB*K@J4|871AXFQ>6n`kzQ$A@v9Z}FjpX}`wURF=U}7+dBGk%JmkSWt6BFR?4#^< zR~#sAe~^9a7(8U4z#Jms65Rg~IQ+2K*7jmZoJQ-#tJwz&(g*nJ!n^VIv04;wf$`hd zODC|!`zqV2F=Cg}{Z>l-iDA_k(XNzE$5`p6)NUJX<6IQ)waSow8r0MA7_*-KM6FaO zyE7&L{N}TF1#05zkreqw;pp+&5@Dg*N#Or%gatpY!li6v{^S>k`K-AKX^ynl60yV=Z%x4 zI>|HFvy(zHqY_K-UE=#p7@}(ck^q3G2RhC?&$;&aEpUaZ@4g2ZOxjhM9qJk&(1>+- z4s@*l?8-dOv*w?hxCqnkc8Yc`>iSHmI!*CbI6Rj(oeuIojLi&}-qnvMd}+6*Y226` z#WP0LqZMf@CUd=Taf84vtajWqe=j!h8%FS_cdT{aBk%lk^d3**ageJ9fCUFh*k1vN zKQ;a*Fmd1U@htd6!VG+kAO~3P^hJI5{Z!SqHfdYIU;I;|-Q)taq8OSJ6of19mP z0rw8~qzap<*yd5O&k5Gqg*y;en!C~hsywxY;X`8hW!t>v=MYmuYYqaUoG`}Ihd(fT z1UlXOkh!$mB;^AMumLz(j_=-|-bk9Qs~!lRK(ek7wtJG4qL=+Z~}QWD5Guid;p1v3`sYgf(*0DR2=Ht@UKBIap+)>aYI33%fq zfcr_W1Gt}lzng))shBmm_VQkHNOAypmYJWSMhtQh-$lC%`hItf%JfxG<`z&VIBQQS z&C$y*X_{`1yd$7Lc-1FN=X}w#lFjoIku^RY6T$9MQWj4}$ZZnx{l*9>%2cc}*w5zp z#N^jz1O(k+woSsZn^?AMoSzpil@QCm<<1ad)HL;up?1+K!J%o~6X&U8#@dpOH@ZbX znvxv<+M_!~!FuYHP)2GIS#7on`t4qhyo!h&GPj3tq?h=P*3xYLhOtyYjL-!+;R{{( z2>K%=dTKA0if06QvbwVwG1YXbxyX)2*ge=0*vt9Yr!~RtMxUc3SrYv%QN|n-L+GT2 zGsSP7*UHBf^o-;m6Pr9M8K*yVPPg-lBE?yVt9n<}%NfqZpd0OB@Kry8 z+1a2D_(Z&m2d0|v3nclnR*n75oZ(IdinMP*++V&Qq=fztm3;oPAFPtl)uXwTulQ>v zKn1+HS>lel_CvEFD_XaaHzoBVjn5ZnV-U|jRAh;Nz?^4H#kw3!yL5Lopxz8`6Z=70 zga#;VXz+uQe~){29ekDc+sD4@p2VEu1V5-Gh!JEnseqKpaezd)s9b==!er3?fM5gE z@;M4TmGiC=Y2^WI%h6S}o!`Ko)+D^RpI`n?--!z6Z-~0+ME}PgYy0`eN`sOHpz}At z{lHzuc5ZV;p>kzjpTUTq#$0Boun(r!y(n4nda#TRe$OgqA#4TJO)cfn@S-1QEO3(^ z=!xK=!G7PYzxe9*oEfy*k2C!n%LA(cacD|hI@HyHF+o_Gr6-d--%y^ciXle<jCBcki+yki=Gr;fb}3!BAWg%l(U$H%1G0M~f25gJX zH-1IXVofdvteU)6AxKFA9uH&T%f$C4z{JP^bJj76ML|UKW0U|kd2e&r`vh#_6FAsq z6KcRctiYmqbulLj!~5%_CWMUC?y=6`2(^)chbE!&9)5b6<9iZhS-;g)FJK_qacY^? z$c%Sc-LvRIp@;=P&P?TfTl#;t&9D5w+ZCZ~7bDksIV%IT-h4uy{zAXuq(ZV2PD;+W zo52P6D@+LtX)1=g=7XV`=fAS5%<6D3x1fKrDuov}6nC`t<{jIE!&(sWn*LW)cTOUC zzes#wFFl<@98EY zIe2b*VzJ!FOs75E`Hh3&XLE|1*9}U+YW|^X|1Bc<**Lh4;8}>gZgg~mYx!|4a1^=7 z4TS|H2Q|t?_!Pi|oH&5|n$TDsbY-T#=Sx#Cm0%+Q|Gl@nodd!%Vf)ilO2!KI2XEv-7OT=pk{eE| zU;*|KRBvOdes1i*6MnEI?H#ev=3RcRsD%Yr|1`c^LTZvz(7Ljer*^b2>&cbQzpQ(~ zvEU4hs~!t>@BP*5|I!BhY8?NY=l;Q*CTSz;D~`J;8TYM95JI_C4Ggu{P1{d+w$1v| zPxwM+8uEcb@j?J#DvqPf_9qx1X@D?LW;Ce`c-3czNijPYv9R!eule`>{QGSD`@8se wPx!hYS~Ati(C(;=Es_{c-(Az!bS^Z2m60pcTRGXe-#D3$=(%#1>C51M0A`V4@c;k- diff --git a/src/site/antora/modules/ROOT/pages/manual/architecture.adoc b/src/site/antora/modules/ROOT/pages/manual/architecture.adoc index f151b790811..91213fec007 100644 --- a/src/site/antora/modules/ROOT/pages/manual/architecture.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/architecture.adoc @@ -16,469 +16,789 @@ //// = Architecture -== Main Components - -Log4j uses the classes shown in the diagram below. - -image:Log4jClasses.jpg[Log4j 2 Class Relationships,title="Log4j 2 Class Relationships"] - -Applications using the Log4j 2 API will request a Logger with a specific -name from the LogManager. The LogManager will locate the appropriate -LoggerContext and then obtain the Logger from it. If the Logger must be -created it will be associated with the LoggerConfig that contains either -a) the same name as the Logger, b) the name of a parent package, or c) -the root LoggerConfig. LoggerConfig objects are created from Logger -declarations in the configuration. The LoggerConfig is associated with -the Appenders that deliver the LogEvents. - -[id=logger-hierarchy] -=== Logger Hierarchy - -The first and foremost advantage of any logging API over plain -`System.out.println()` resides in its ability to disable certain log -statements while allowing others to print unhindered. This capability -assumes that the logging space, that is, the space of all possible -logging statements, is categorized according to some developer-chosen -criteria. - -In Log4j 1.x the Logger Hierarchy was maintained through a relationship -between Loggers. In Log4j 2 this relationship no longer exists. Instead, -the hierarchy is maintained in the relationship between LoggerConfig -objects. - -Loggers and LoggerConfigs are named entities. Logger names are -case-sensitive and they follow the hierarchical naming rule: - -Named Hierarchy:: -A LoggerConfig is said to be an _ancestor_ of another LoggerConfig if -its name followed by a dot is a prefix of the _descendant_ logger -name. A LoggerConfig is said to be a _parent_ of a _child_ -LoggerConfig if there are no ancestors between itself and the -descendant LoggerConfig. - -For example, the LoggerConfig named `"com.foo"` is a parent of the -LoggerConfig named `"com.foo.Bar"`. Similarly, `"java"` is a parent of -`"java.util"` and an ancestor of `"java.util.Vector"`. This naming -scheme should be familiar to most developers. - -The root LoggerConfig resides at the top of the LoggerConfig hierarchy. -It is exceptional in that it always exists and it is part of every -hierarchy. A Logger that is directly linked to the root LoggerConfig can -be obtained as follows: - -[source,java] ----- -Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME); ----- - -Alternatively, and more simply: - -[source,java] ----- -Logger logger = LogManager.getRootLogger(); ----- - -All other Loggers can be retrieved using the -link:../javadoc/log4j-api/org/apache/logging/log4j/LogManager.html#getLogger(java.lang.String)[`LogManager.getLogger`] -static method by passing the name of the desired Logger. Further -information on the Logging API can be found in the -xref:manual/api.adoc[Log4j API]. - -[#logger-context] -=== LoggerContext +Log4j Core is the reference implementation of xref:manual/api.adoc[] and composed of several components. +In this section we will try to explain major pillars its architecture stands on. +An overview these major classes can be depicted as follows: + +[#architecture-diagram] +.An overview of major classes and their relation +[plantuml] +.... +@startuml + +class LoggerContext { + Configuration config + Logger[] loggers + Logger getLogger(String name) +} + +note left of LoggerContext { + Anchor for the logging system +} + +LoggerContext --> Configuration + +LoggerContext --> "0..*" Logger + +class Configuration { + Appender[] appenders + Filter[] filters + LoggerConfig[] loggerConfigs + LoggerConfig getLoggerConfig(String name) + StrSubstitutor substitutor +} + +note left of Configuration + Encapsulates components compiled + from a user-provided configuration + file (e.g., `log4j2.xml`) +end note + +Configuration --> "0..*" Filter + +Configuration --> "0..*" Appender + +Configuration --> "0..*" LoggerConfig + +Configuration --> StrSubstitutor + +class Appender { + Layout layout + void append(LogEvent) +} + +Appender -[#green,thickness=6]-> Layout + +class Layout { + byte[] encode(LogEvent) +} + +class Filter { + Result filter(LogEvent) +} + +note right of Filter + Note that a `Filter` can + be provided at 4 levels: + 1. `Configuration` + 2. `LoggerConfig` + 3. `AppenderRef` + 4. `AppenderControl` +end note + +class LoggerConfig { + AppenderRef[] appenderRefs + AppenderControl[] appenderControls + Level level + Filter filter + void log(LogEvent) +} + +LoggerConfig --> "0..*" AppenderRef + +LoggerConfig -[#green,thickness=6]-> "0..*" AppenderControl + +LoggerConfig --> Filter + +class AppenderRef { + String appenderName + Level level + Filter filter +} + +note right of AppenderRef + Denotes a user-provided + appender configuration + for a `Logger`, + e.g., ` Filter + +class AppenderControl { + Appender appender + Filter filter + void append(LogEvent) +} + +note right of AppenderControl + Decorates an `Appender` + with a `Filter` +end note + +AppenderControl -[#green,thickness=6]-> Appender + +AppenderControl --> Filter + +class StrSubstitutor { + Interpolator interpolator + String replace(String input) +} + +note right of StrSubstitutor + Responsible for + property substitution + (e.g., `${env:USER}`) +end note + +StrSubstitutor --> Interpolator + +class Interpolator { + StrLookup[] lookups + String lookup(String input) +} + +Interpolator --> "0..*" StrLookup + +class StrLookup { + String lookup(String input) +} + +class Logger { + void log(Level level, Message message) +} + +note right of Logger + The main API entry point + users interact with +end note + +Logger -[#green,thickness=6]-> LoggerConfig : delegates `log()` + +@enduml +.... + +At a really high level, + +* A <>, the composition anchor, gets created in combination with a <>. +Both can be created either directly (i.e., programmatically) or indirectly at first interaction with Log4j. +* `LoggerContext` creates <>s that users interact with for logging purposes. +* <> delivers a link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html[`LogEvent`] to a target (file, socket, database, etc.) and typically uses a <> to encode log events. +* <> encapsulates configuration for a `Logger`, as `AppenderControl` and `AppenderRef` for ``Appender``s. +* <> is equipped with <> to allow property substitution in `String`-typed values. +* A typical `log()` call triggers a chain of invocations through classes `Logger`, `LoggerConfig`, `AppenderControl`, `Appender`, and `Layout` in order – this is depicted using green arrows in xref:architecture-diagram[xrefstyle=short]. + +Following sections examine this interplay in detail. + +[#LoggerContext] +== `LoggerContext` The link:../javadoc/log4j-api/org/apache/logging/log4j/spi/LoggerContext.html[`LoggerContext`] acts as the anchor point for the logging system. -It is primarily responsible for instantiating <>s. +It is associated with an active <> and is primarily responsible for instantiating <>s. + +[#LoggerContext-diagram] +.`LoggerContext` and other directly related classes +[plantuml] +.... +@startuml + +class LoggerContext #line.bold { + Configuration config + Logger[] loggers + Logger getLogger(String name) +} + +LoggerContext --> Configuration + +LoggerContext --> "0..*" Logger + +class Configuration { + Appender[] appenders + Filter[] filters + LoggerConfig[] loggerConfigs + LoggerConfig getLoggerConfig(String name) + StrSubstitutor substitutor +} + +class Logger { + void log(Level level, Message message) +} + +@enduml +.... In most cases, applications have a single global `LoggerContext`. Though in certain cases (e.g., Java EE applications), Log4j can be configured to accommodate multiple ``LoggerContext``s. Refer to xref:manual/logsep.adoc[] for details. -=== Configuration - -Every LoggerContext has an active -link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`]. -The Configuration contains all the Appenders, context-wide Filters, -LoggerConfigs and contains the reference to the StrSubstitutor. -During reconfiguration, two Configuration objects will exist. Once all Loggers -have been redirected to the new Configuration, the old Configuration -will be stopped and discarded. - -[#logger] -=== Logger - -As stated previously, Loggers are created by calling -link:../javadoc/log4j-api/org/apache/logging/log4j/LogManager.html#getLogger(java.lang.String)[`LogManager.getLogger`]. -The Logger itself performs no direct actions. It simply has a name and -is associated with a LoggerConfig. It extends -link:../javadoc/log4j-api/org/apache/logging/log4j/spi/AbstractLogger.html[`AbstractLogger`] -and implements the required methods. As the configuration is modified -Loggers may become associated with a different LoggerConfig, thus -causing their behavior to be modified. - -Retrieving Loggers - -Calling the `LogManager.getLogger` method with the same name will always -return a reference to the same Logger object. - -For example, in - -[source,java] ----- -Logger x = LogManager.getLogger("wombat"); -Logger y = LogManager.getLogger("wombat"); ----- - -`x` and `y` refer to _exactly_ the same Logger object. - -Configuration of the log4j environment is typically done at application -initialization. The preferred way is by reading a configuration file. -This is discussed in xref:manual/configuration.adoc[Configuration]. - -Log4j makes it easy to name Loggers by _software component_. This can be -accomplished by instantiating a Logger in each class, with the logger -name equal to the fully qualified name of the class. This is a useful -and straightforward method of defining loggers. As the log output bears -the name of the generating Logger, this naming strategy makes it easy to -identify the origin of a log message. However, this is only one -possible, albeit common, strategy for naming loggers. Log4j does not -restrict the possible set of loggers. The developer is free to name the -loggers as desired. - -Since naming Loggers after their owning class is such a common idiom, -the convenience method `LogManager.getLogger()` is provided to -automatically use the calling class's fully qualified class name as the -Logger name. - -Nevertheless, naming loggers after the class where they are located -seems to be the best strategy known so far. - -[#loggerconfig] -=== LoggerConfig - -link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/LoggerConfig.html[`LoggerConfig`] -objects are created when Loggers are declared in the logging -configuration. The LoggerConfig contains a set of Filters that must -allow the LogEvent to pass before it will be passed to any Appenders. It -contains references to the set of Appenders that should be used to -process the event. - -==== Log Levels - -LoggerConfigs will be assigned a Log -link:../javadoc/log4j-api/org/apache/logging/log4j/Level.html[`Level`]. -The set of built-in levels includes ALL, TRACE, DEBUG, INFO, WARN, ERROR, -FATAL, and OFF. Log4j 2 also supports xref:manual/customloglevels.adoc[custom log -levels]. Another mechanism for getting more granularity is to use -xref:manual/markers.adoc[markers] instead. The OFF and ALL -levels are not intended to be used on calls to the logging API. -Specifying OFF in the configuration implies no logging events should -match while specifying ALL would mean all events match, including custom -events. However, OFF can be used on logging API calls in special cases -where the event should always be logged regardless of the configuration. -However, it is generally recommended that a Marker with a corresponding -global Marker Filter be used instead. - -{logging-services-url}/log4j/1.x/manual.html[Log4j 1] and -{logback-url}/manual/architecture.html#effectiveLevel[Logback] -both have the concept of "Level Inheritance". In Log4j 2, Loggers and -LoggerConfigs are two different objects so this concept is implemented -differently. Each Logger references the appropriate LoggerConfig which -in turn can reference its parent, thus achieving the same effect. - -Below are five tables with various assigned level values and the -resulting levels that will be associated with each Logger. Note that in -all these cases if the root LoggerConfig is not configured a default -Level will be assigned to it. - -.Example 1 -[cols=",,,",options="header",] -|==================================================================== -|Logger Name |Assigned LoggerConfig |LoggerConfig Level |Logger Level +[#Configuration] +== `Configuration` + +Every <> is associated with an active link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`]. +It models the configuration of all appenders, layouts, filters, loggers, and contains the reference to <>. + +[#Configuration-diagram] +.`Configuration` and other directly related classes +[plantuml] +.... +@startuml + +class LoggerContext { + Configuration config + Logger[] loggers + Logger getLogger(String name) +} + +LoggerContext --> Configuration + +class Configuration #line.bold { + Appender[] appenders + Filter[] filters + LoggerConfig[] loggerConfigs + LoggerConfig getLoggerConfig(String name) + StrSubstitutor substitutor +} + +Configuration --> "0..*" Filter + +Configuration --> "0..*" Appender + +Configuration --> "0..*" LoggerConfig + +Configuration --> StrSubstitutor + +class Appender { + Layout layout + void append(LogEvent) +} + +class Filter { + Result filter(LogEvent) +} + +class LoggerConfig { + AppenderRef[] appenderRefs + AppenderControl[] appenderControls + Level level + Filter filter + void log(LogEvent) +} + +class StrSubstitutor { + Interpolator interpolator + String replace(String input) +} +@enduml +.... + +During reconfiguration, two `Configuration` instances will be present. +Once all ``Logger``s have been redirected to the new `Configuration`, the old one will be stopped and discarded. + +Configuration of Log4j Core is typically done at application initialization. +The preferred way is by reading a xref:manual/configuration.adoc[configuration file], but it can also be done xref:manual/customconfig.adoc[programmatically]. +This is further discussed in xref:manual/config-intro.adoc[]. + +[#Logger] +== `Logger` + +link:../javadoc/log4j-api/org/apache/logging/log4j/Logger.html[`Logger`]s are the primary user entry point for logging. +They are created by calling one of the `getLogger()` methods of link:../javadoc/log4j-api/org/apache/logging/log4j/LogManager.html[`LogManager`] – this is further documented in xref:manual/api.adoc[]. +The `Logger` itself performs no direct actions. +It simply has a name and is associated with a <>. + +[#Logger-diagram] +.`Logger` and other directly related classes +[plantuml] +.... +@startuml + +class LoggerContext { + Configuration config + Logger[] loggers + Logger getLogger(String name) +} + +LoggerContext --> "0..*" Logger + +class LoggerConfig { + AppenderRef[] appenderRefs + AppenderControl[] appenderControls + Level level + Filter filter + void log(LogEvent) +} + +class Logger #line.bold { + void log(Level level, Message message) +} + +Logger -[#green,thickness=6]-> LoggerConfig : delegates `log()` + +@enduml +.... + +The hierarchy between <>s, implies the very same hierarchy between ``Logger``s too. +You can use `LogManager.getRootLogger()` to get the root logger. +Note that Log4j API has no assumptions on a `Logger` hierarchy – this is a feature implemented by Log4j Core. + +When the <> is modified, ``Logger``s may become associated with a different `LoggerConfig`, thus causing their behavior to be modified. +Refer to xref:manual/configuration.adoc#configuring-loggers[configuring ``Logger``s] for further information. + +[#LoggerConfig] +== `LoggerConfig` + +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/LoggerConfig.html[`LoggerConfig`] binds <> definitions to their associated components (appenders, filters, etc.) as declared in the active <>. +The details of mapping a `Configuration` to ``LoggerConfig``s is explained xref:manual/configuration.adoc#configuring-loggers[here]. +``Logger``s effectively interact with appenders, filters, etc. through corresponding ``LoggerConfig``s. +A `LoggerConfig` essentially contains + +* A reference to its parent (except if it is the root logger) +* A xref:manual/customloglevels.adoc[level] denoting the severity of messages that are accepted (defaults to `ERROR`) +* <>s that must allow the `LogEvent` to pass before it will be passed to any <>s +* References to <>s that should be used to process the event + +[#LoggerConfig-diagram] +.`LoggerConfig` and other directly related classes +[plantuml] +.... +@startuml + +class Configuration { + Appender[] appenders + Filter[] filters + LoggerConfig[] loggerConfigs + LoggerConfig getLoggerConfig(String name) + StrSubstitutor substitutor +} + +Configuration --> "0..*" LoggerConfig + +class Filter { + Result filter(LogEvent) +} + +class LoggerConfig #line.bold { + AppenderRef[] appenderRefs + AppenderControl[] appenderControls + Level level + Filter filter + void log(LogEvent) +} + +LoggerConfig --> "0..*" AppenderRef + +LoggerConfig -[#green,thickness=6]-> "0..*" AppenderControl + +LoggerConfig --> Filter + +class AppenderRef { + String appenderName + Level level + Filter filter +} + +class AppenderControl { + Appender appender + Filter filter + void append(LogEvent) +} + +class Logger { + void log(Level level, Message message) +} + +Logger -[#green,thickness=6]-> LoggerConfig : delegates `log()` + +@enduml +.... + +[#logger-hiearchy] +=== Logger hierarchy + +Log4j Core has a *hierarchical* model of ``LoggerConfig``s, and hence ``Logger``s. +A `LoggerConfig` called `child` is said to be parented by `parent`, if `parent` has the _longest prefix match_ on name. +This match is case-sensitive and performed after tokenizing the name by splitting it from `.` (dot) characters. +For a positive name match, tokens must match exhaustively. +See xref:#logger-hiearchy-diagram[xrefstyle=short] for an example. + +[#logger-hiearchy-diagram] +.Example hierarchy of loggers named `X`, `X.Y`, `X.Y.Z`, and `X.YZ` +[plantuml] +.... +@startmindmap +* root +** X +*** X.Y +**** X.Y.Z +*** X.YZ +@endmindmap +.... + +If a `LoggerConfig` is not provided an explicit level, it will be inherited from its parent. +Similarly, if a user programmatically requests a `Logger` with a name that doesn't have a directly corresponding `LoggerConfig` configuration entry with its name, the `LoggerConfig` of the parent will be used. + +.Click for examples on `LoggerConfig` hierarchy +[%collapsible] +==== +Below we demonstrate the `LoggerConfig` hierarchy by means of _level inheritance_. +That is, we will examine the effective level of a `Logger` in various `LoggerConfig` settings. + +.Only the root logger is configured with a level, and it is `DEBUG` +[%header,cols="1m,1m,1m,1m"] +|=== +|Logger name |Assigned `LoggerConfig` name |Configured level |Effective level |root |root |DEBUG |DEBUG -|X |root |DEBUG |DEBUG -|X.Y |root |DEBUG |DEBUG -|X.Y.Z |root |DEBUG |DEBUG -|==================================================================== - -In example 1 above, only the root logger is configured and has a Log -Level. All the other Loggers reference the root LoggerConfig and use its -Level. - -.Example 2 -[cols=",,,",options="header",] -|============================================================= -|Logger Name |Assigned LoggerConfig |LoggerConfig Level |Level +|X |root | |DEBUG +|X.Y |root | |DEBUG +|X.Y.Z |root | |DEBUG +|=== + +.All loggers are configured with a level +[%header,cols="1m,1m,1m,1m"] +|=== +|Logger name |Assigned `LoggerConfig` |Configured level |Effective level |root |root |DEBUG |DEBUG |X |X |ERROR |ERROR |X.Y |X.Y |INFO |INFO |X.Y.Z |X.Y.Z |WARN |WARN -|============================================================= - -In example 2, all loggers have a configured LoggerConfig and obtain -their Level from it. +|=== -.Example 3 -[cols=",,,",options="header",] -|============================================================= -|Logger Name |Assigned LoggerConfig |LoggerConfig Level |Level +.All loggers are configured with a level, except the logger `X.Y` +[%header,cols="1m,1m,1m,1m"] +|=== +|Logger name |Assigned `LoggerConfig` |Configured level |Effective level |root |root |DEBUG |DEBUG |X |X |ERROR |ERROR -|X.Y |X |ERROR |ERROR +|X.Y |X | |ERROR |X.Y.Z |X.Y.Z |WARN |WARN -|============================================================= - -In example 3, the loggers`root`, `X` and `X.Y.Z` each have a configured -LoggerConfig with the same name. The Logger `X.Y` does not have a -configured LoggerConfig with a matching name so uses the configuration -of LoggerConfig `X` since that is the LoggerConfig whose name has the -the longest match to the start of the Logger's name. - -.Example 4 -[cols=",,,",options="header",] -|============================================================= -|Logger Name |Assigned LoggerConfig |LoggerConfig Level |level -|root |root |DEBUG |DEBUG -|X |X |ERROR |ERROR -|X.Y |X |ERROR |ERROR -|X.Y.Z |X |ERROR |ERROR -|============================================================= - -In example 4, the loggers `root` and `X` each have a Configured -LoggerConfig with the same name. The loggers `X.Y` and `X.Y.Z` do not -have configured LoggerConfigs and so get their Level from the -LoggerConfig assigned to them, `X`, since it is the LoggerConfig whose -name has the longest match to the start of the Logger's name. - -.Example 5 -[cols=",,,",options="header",] -|============================================================= -|Logger Name |Assigned LoggerConfig |LoggerConfig Level |level +|=== + +.All loggers are configured with a level, except loggers `X.Y` and `X.Y.Z` +[%header,cols="1m,1m,1m,1m"] +|=== +|Logger name |Assigned `LoggerConfig` |Configured level |Effective level |root |root |DEBUG |DEBUG |X |X |ERROR |ERROR -|X.Y |X.Y |INFO |INFO -|X.YZ |X |ERROR |ERROR -|============================================================= - -In example 5, the loggers `root`.`X`, and `X.Y` each has a configured -LoggerConfig with the same name. The logger `X.YZ` does not have -configured LoggerConfig and so gets its Level from the LoggerConfig -assigned to it, `X`, since it is the LoggerConfig whose name has the -longest match to the start of the Logger's name. It is not associated -with LoggerConfig `X.Y` since tokens after periods must match exactly. - -.Example 6 -[cols=4*,options="header"] +|X.Y |X | |ERROR +|X.Y.Z |X | |ERROR +|=== + +.All loggers are configured with a level, except the logger `X.YZ` +[%header,cols="1m,1m,1m,1m"] |=== -|Logger Name |Assigned LoggerConfig |LoggerConfig Level |Level +|Logger name |Assigned `LoggerConfig` |Configured level |Effective level |root |root |DEBUG |DEBUG |X |X |ERROR |ERROR -|X.Y |X.Y | |ERROR -|X.Y.Z |X.Y | |ERROR +|X.Y |X.Y |INFO |INFO +|X.YZ |X | |ERROR |=== +==== -In example 6, LoggerConfig X.Y has no configured level so it inherits -its level from LoggerConfig X. Logger X.Y.Z uses LoggerConfig X.Y since -it doesn't have a LoggerConfig with a name that exactly matches. It too -inherits its logging level from LoggerConfig X. +For further information on log levels and using them for filtering purposes in a configuration, see xref:manual/customloglevels.adoc[]. -The table below illustrates how Level filtering works. In the table, -the vertical header shows the Level of the LogEvent, while the horizontal -header shows the Level associated with the appropriate LoggerConfig. The -intersection identifies whether the LogEvent would be allowed to pass -for further processing (Yes) or discarded (No). +[#Filter] +== `Filter` -[cols=8*,options="header"] -|=== -|Event Level -7+|LoggerConfig Level +In addition to <>, Log4j provides link:../javadoc/log4j-core/org/apache/logging/log4j/core/Filter.html[`Filter`]s to evaluate the parameters of a logging call (i.e., context-wide filter) or a log event, and decide if it should be processed further in the pipeline. -|  |`TRACE` |`DEBUG` |`INFO` |`WARN` |`ERROR` |`FATAL` |`OFF` +[#Filter-diagram] +.`Filter` and other directly related classes +[plantuml] +.... +@startuml -|`ALL` |❌ |❌ |❌ |❌ |❌ |❌ |❌ +class Configuration { + Appender[] appenders + Filter[] filters + LoggerConfig[] loggerConfigs + LoggerConfig getLoggerConfig(String name) + StrSubstitutor substitutor +} -|`TRACE` |✅ |❌ |❌ |❌ |❌ |❌ |❌ +Configuration --> "0..*" Filter -|`DEBUG` |✅ |✅ |❌ |❌ |❌ |❌ |❌ +Configuration --> "0..*" LoggerConfig -|`INFO` |✅ |✅ |✅ |❌ |❌ |❌ |❌ +class Filter #line.bold { + Result filter(LogEvent) +} -|`WARN` |✅ |✅ |✅ |✅ |❌ |❌ |❌ +class LoggerConfig { + AppenderRef[] appenderRefs + AppenderControl[] appenderControls + Level level + Filter filter + void log(LogEvent) +} -|`ERROR` |✅ |✅ |✅ |✅ |✅ |❌ |❌ +LoggerConfig --> "0..*" AppenderRef -|`FATAL` |✅ |✅ |✅ |✅ |✅ |✅ |❌ +LoggerConfig -[#green,thickness=6]-> "0..*" AppenderControl -|`OFF` |✅ |✅ |✅ |✅ |✅ |✅ |✅ -|=== +LoggerConfig --> Filter + +class AppenderRef { + String appenderName + Level level + Filter filter +} + +AppenderRef --> Filter + +AppenderControl --> Filter + +@enduml +.... + +Refer to xref:manual/filters.adoc[] for further information. + +[#Appender] +== `Appender` + +link:../javadoc/log4j-core/org/apache/logging/log4j/core/Appender.html[`Appender`]s are responsible for delivering a link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html[`LogEvent`] to a certain target; console, file, database, etc. +While doing so, they typically use <>s to encode the log event. +See xref:manual/appenders.adoc[] for the complete guide. + +[#Appender-diagram] +.`Appender` and other directly related classes +[plantuml] +.... +@startuml + +class Configuration { + Appender[] appenders + Filter[] filters + LoggerConfig[] loggerConfigs + LoggerConfig getLoggerConfig(String name) + StrSubstitutor substitutor +} -=== Filter - -In addition to the automatic log Level filtering that takes place as -described in the previous section, Log4j provides -link:../javadoc/log4j-core/org/apache/logging/log4j/core/Filter.html[`Filter`]s -that can be applied before control is passed to any LoggerConfig, after -control is passed to a LoggerConfig but before calling any Appenders, -after control is passed to a LoggerConfig but before calling a specific -Appender, and on each Appender. In a manner very similar to firewall -filters, each Filter can return one of three results, `Accept`, `Deny` -or `Neutral`. A response of `Accept` means that no other Filters should -be called and the event should progress. A response of `Deny` means the -event should be immediately ignored and control should be returned to -the caller. A response of `Neutral` indicates the event should be passed -to other Filters. If there are no other Filters the event will be -processed. - -Although an event may be accepted by a Filter the event still might not -be logged. This can happen when the event is accepted by the -pre-LoggerConfig Filter but is then denied by a LoggerConfig filter or -is denied by all Appenders. - -=== Appender - -The ability to selectively enable or disable logging requests based on -their logger is only part of the picture. Log4j allows logging requests -to print to multiple destinations. In log4j speak, an output destination -is called an -link:../javadoc/log4j-core/org/apache/logging/log4j/core/Appender.html[`Appender`]. -Currently, appenders exist for the console, files, remote socket -servers, Apache Flume, remote UNIX Syslog daemons, and various -database APIs. See the section on xref:manual/appenders.adoc[Appenders] for -more details on the various types available. More than one Appender can -be attached to a Logger. - -An Appender can be added to a Logger by calling the -link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html#addLoggerAppender(org.apache.logging.log4j.core.Logger,%20org.apache.logging.log4j.core.Appender)[`addLoggerAppender`] -method of the current Configuration. If a LoggerConfig matching the name -of the Logger does not exist, one will be created, and the Appender will be -attached to it and then all Loggers will be notified to update their -LoggerConfig references. - -*Each enabled logging request for a given logger will be forwarded to -all the appenders in that Logger's LoggerConfig as well as the Appenders -of the LoggerConfig's parents.* In other words, Appenders are inherited -additively from the LoggerConfig hierarchy. For example, if a console -appender is added to the root logger, then all enabled logging requests -will at least print on the console. If in addition a file appender is -added to a LoggerConfig, say _C_, then enabled logging requests for _C_ -and _C_'s children will print in a file _and_ on the console. It is -possible to override this default behavior so that Appender accumulation -is no longer additive by setting `additivity="false"` on the Logger -declaration in the configuration file. - -The rules governing appender additivity are summarized below. - -Appender Additivity:: -The output of a log statement of Logger _L_ will go to all the -Appenders in the LoggerConfig associated with _L_ and the ancestors of -that LoggerConfig. This is the meaning of the term "appender -additivity". -+ -However, if an ancestor of the LoggerConfig associated with Logger -_L_, say _P_, has the additivity flag set to `false`, then _L_'s -output will be directed to all the appenders in _L_'s LoggerConfig and -it's ancestors up to and including _P_ but not the Appenders in any of -the ancestors of _P_. -+ -Loggers have their additivity flag set to `true` by default. - -The table below shows an example: +Configuration --> "0..*" Filter +Configuration --> "0..*" Appender + +Configuration --> "0..*" LoggerConfig + +class Appender #line.bold { + Layout layout + void append(LogEvent) +} + +Appender -[#green,thickness=6]-> Layout + +class Layout { + byte[] encode(LogEvent) +} + +class Filter { + Result filter(LogEvent) +} + +class LoggerConfig { + AppenderRef[] appenderRefs + AppenderControl[] appenderControls + Level level + Filter filter + void log(LogEvent) +} + +LoggerConfig --> "0..*" AppenderRef + +LoggerConfig -[#green,thickness=6]-> "0..*" AppenderControl + +LoggerConfig --> Filter + +class AppenderRef { + String appenderName + Level level + Filter filter +} + +AppenderRef --> Filter + +class AppenderControl { + Appender appender + Filter filter + void append(LogEvent) +} + +AppenderControl -[#green,thickness=6]-> Appender + +AppenderControl --> Filter + +@enduml +.... + +An `Appender` can be added to a <> by calling the link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html#addLoggerAppender(org.apache.logging.log4j.core.Logger,%20org.apache.logging.log4j.core.Appender)[`addLoggerAppender()`] method of the current <>. +If a <> matching the name of the `Logger` does not exist, one will be created, and the `Appender` will be attached to it, and then all ``Logger``s will be notified to update their `LoggerConfig` references. + +[#appender-additivity] +=== Appender additivity + +Each enabled logging request for a given logger will be forwarded to all the appenders in the corresponding ``Logger``'s `LoggerConfig`, as well as to the ``Appender``s of the ``LoggerConfig``'s parents. +In other words, ``Appender``s are inherited *additively* from the `LoggerConfig` hierarchy. +For example, if a console appender is added to the root logger, then all enabled logging requests will at least print on the console. +If in addition a file appender is added to a `LoggerConfig`, say `LC`, then enabled logging requests for `LC` and ``LC``'s children will print in a file _and_ on the console. +It is possible to override this default behavior so that appender accumulation is no longer additive by setting `additivity` attribute to `false` on xref:manual/configuration.adoc#configuring-loggers[the `Logger` declaration in the configuration file]. + +The output of a log statement of `Logger` `L` will go to all the appenders in the `LoggerConfig` associated with `L` and the ancestors of that `LoggerConfig`. +However, if an ancestor of the `LoggerConfig` associated with `Logger` +`L`, say `P`, has the additivity flag set to `false`, then ``L``'s output will be directed to all the appenders in ``L``'s `LoggerConfig` and it's ancestors up to and including `P` but not the appenders in any of the ancestors of `P`. + +.Click for an example on appender additivity +[%collapsible] +==== +[#appender-additivity-diagram] +.Example hierarchy of logger configurations to demonstrate appender additivity +[plantuml] +.... +@startmindmap +* root +** A +*** A.B1 (additivity=false) +**** A.B1.C +***** A.B1.C.D +*** A.B2.C +**** A.B2.C.D (additivity=false) +@endmindmap +.... + +In xref:#appender-additivity-diagram[xrefstyle=short], the effective appenders for each logger configuration are as follows: + +.Effective appenders of logger configurations in xref:#appender-additivity-diagram[xrefstyle=short] +[cols="1c,1c,1c,1c,1c,1c,1c"] |=== -|Logger Name |Added Appenders |Additivity Flag |Output Targets |Comment - -|root -|A1 -|not applicable -|A1 -|The root logger has no parent so additivity does not apply to it. - -|x -|A-x1, A-x2 -|true -|A1, A-x1, A-x2 -|Appenders of "x" and root. - -|x.y -|none -|true -|A1, A-x1, A-x2 -|Appenders of "x" and root. It would not be typical to configure a Logger with no Appenders. - -|x.y.z -|A-xyz1 -|true -|A1, A-x1, A-x2, A-xyz1 -|Appenders in "x.y.z", "x" and root. - -|security -|A-sec -|false -|A-sec -|No appender accumulation since the additivity flag is set to `false`. - -|security.access -|none -|true -|A-sec -|Only appenders of "security" because the additivity flag in "security" is set to `false`. +.2+^.^h| Appender +6+^.h|Logger configuration + +| `A` +| `A.B1` +| `A.B1.C` +| `A.B1.C.D` +| `A.B2.C` +| `A.B2.C.D` + +| `root` +| ✅ +| ✅ +| ✅ +| ✅ +| ✅ +| ❌ + +| `A` +| ✅ +| ❌ +| ❌ +| ❌ +| ✅ +| ❌ + +| `A.B1` +| - +| ✅ +| ✅ +| ✅ +| - +| - + +| `A.B1.C` +| - +| - +| ✅ +| ✅ +| - +| - + +| `A.B1.C.D` +| - +| - +| - +| ✅ +| - +| - + +| `A.B2.C` +| - +| - +| - +| - +| ✅ +| ❌ + +| `A.B2.C.D` +| - +| - +| - +| - +| - +| ✅ |=== +==== + +[#Layout] +== `Layout` -=== Layout +An <> uses a *layout* to encode a link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html[`LogEvent`] into a form that meets the needs of whatever will be consuming the log event. -More often than not, users wish to customize not only the output -destination but also the output format. This is accomplished by -associating a -link:../javadoc/log4j-core/org/apache/logging/log4j/core/Layout.html[`Layout`] -with an Appender. The Layout is responsible for formatting the LogEvent -according to the user's wishes, whereas an appender takes care of -sending the formatted output to its destination. The -link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/PatternLayout.html[`PatternLayout`], -part of the standard log4j distribution, lets the user specify the -output format according to conversion patterns similar to the C language -`printf()` function. +[#Layout-diagram] +.`Layout` and other directly related classes +[plantuml] +.... +@startuml -For example, the PatternLayout with the conversion pattern "%r [%t] %-5p -%c - %m%n" will output something akin to: +class Appender { + Layout layout + void append(LogEvent) +} +Appender -[#green,thickness=6]-> Layout + +class Layout #line.bold { + byte[] encode(LogEvent) +} + +@enduml .... -176 [main] INFO org.foo.Bar - Located nearest gas station. + +Refer to xref:manual/layouts.adoc[] for details. + +[#StrSubstitutor] +== `StrSubstitutor` et al. + +link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/StrSubstitutor.html[`StrSubstitutor`] is a `String` interpolation tool that can be used in both configurations and components (e.g., appenders, layouts). +It accepts an link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/Interpolator.html[`Interpolator`] to determine if a key maps to a certain value. +`Interpolator` is essentially a facade delegating to multiple link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/StrLookup.html[`StrLookup`] (aka. _lookup_) implementations. + +[#StrSubstitutor-diagram] +.`StrSubstitutor` et al. and other directly related classes +[plantuml] +.... +@startuml + +class Configuration { + Appender[] appenders + Filter[] filters + LoggerConfig[] loggerConfigs + LoggerConfig getLoggerConfig(String name) + StrSubstitutor substitutor +} + +Configuration --> StrSubstitutor + +class StrSubstitutor #line.bold { + Interpolator interpolator + String replace(String input) +} + +StrSubstitutor --> Interpolator + +class Interpolator { + StrLookup[] lookups + String lookup(String input) +} + +Interpolator --> "0..*" StrLookup + +class StrLookup { + String lookup(String input) +} + +@enduml .... -The first field is the number of milliseconds elapsed since the start of -the program. The second field is the thread making the log request. The -third field is the level of the log statement. The fourth field is the -name of the logger associated with the log request. The text after the -'-' is the message of the statement. - -Log4j comes with many different xref:manual/layouts.adoc[Layouts] for various -use cases such as JSON, XML, HTML, and Syslog (including the new RFC -5424 version). Other appenders such as the database connectors fill in -specified fields instead of a particular textual layout. - -Just as importantly, log4j will render the content of the log message -according to user-specified criteria. For example, if you frequently -need to log `Oranges`, an object type used in your current project, then -you can create an OrangeMessage that accepts an Orange instance and pass -that to Log4j so that the Orange object can be formatted into an -appropriate byte array when required. - -=== StrSubstitutor and StrLookup - -The -link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/StrSubstitutor.html[`StrSubstitutor`] -class and -link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/StrLookup.html[`StrLookup`] -interface was borrowed from -https://commons.apache.org/proper/commons-lang/[Apache Commons Lang] and -then modified to support evaluating LogEvents. In addition the -link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/Interpolator.html[`Interpolator`] -class was borrowed from Apache Commons Configuration to allow the -StrSubstitutor to evaluate variables from multiple StrLookups. It -too was modified to support evaluating LogEvents. Together these provide -a mechanism to allow the configuration to reference variables coming -from System Properties, the configuration file, the ThreadContext Map, -StructuredData in the LogEvent. The variables can either be resolved -when the configuration is processed or as each event is processed if -the component is capable of handling it. See xref:manual/lookups.adoc[Lookups] -for more information. +See xref:manual/configuration.adoc#property-substitution[how property substitution works] and xref:manual/lookups.adoc[the predefined lookups] for further information. diff --git a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc index 5d9b94627b9..58d8e0dacce 100644 --- a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc @@ -438,7 +438,7 @@ Other logger configurations are optional. Every link:../javadoc/log4j-api/org/apache/logging/log4j/Logger.html[`Logger`] in your application is assigned to one of these logger configurations (see -xref:manual/architecture.adoc#loggerconfig[architecture]), which determines the events that will be logged and those that won't. +xref:manual/architecture.adoc#LoggerConfig[architecture]), which determines the events that will be logged and those that won't. Let's start with an example of logger configuration: diff --git a/src/site/antora/modules/ROOT/pages/manual/customloglevels.adoc b/src/site/antora/modules/ROOT/pages/manual/customloglevels.adoc index 2c98e3b478d..42423e751ad 100644 --- a/src/site/antora/modules/ROOT/pages/manual/customloglevels.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/customloglevels.adoc @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. //// + = Levels Log levels are used to categorize log events by severity and control the verbosity of the logs. @@ -44,6 +45,14 @@ Priority can be used in several contexts to express a filtering capability, for The entry point to log levels are through link:../javadoc/log4j-api/org/apache/logging/log4j/Level.html[`Level`]. Predefined levels are available for Log4j API integrators through link:../javadoc/log4j-api/org/apache/logging/log4j/spi/StandardLevel.html[`StandardLevel`]. +[NOTE] +==== +The `OFF` and `ALL` levels are not intended to be used on calls to the logging API. +Specifying `OFF` in the configuration implies no logging events should match, while specifying `ALL` means all events match, including custom events. +`OFF` can be used on logging API calls in special cases where the event should always be logged regardless of the configuration. +However, it is generally recommended that a xref:manual/markers.adoc[marker] with a corresponding global xref:manual/filters.adoc#MarkerFilter[marker filter] be used instead. +==== + [#usage] == [[StandardLoggerInterface]] Usage @@ -57,39 +66,8 @@ LOGGER.log(Level.INFO, "Hello, {}!", userName); // <2> <1> Using `Logger#info()` to log a message at `INFO` level <2> Using `Logger#log()` to log a message and specifying the `INFO` level explicitly -There are several ways levels can be employed in the Log4j configuration file for filtering purposes. -Filtering on levels for loggers is a pretty common example: -[source,xml] ----- - - - - - - - - - - - - - - - - - ----- -<1> Logs of level `INFO` or higher severity (i.e., `WARN`, `ERROR`, `FATAL`) are allowed for `com.mycompany` package -<2> Logs of level `ERROR` or higher severity (i.e., `FATAL`) are allowed for the rest - -[[top]] - [#DefiningLevelsInCode] -== Defining custom log levels programmatically +=== Custom log levels Users can programmatically define custom levels using link:../javadoc/log4j-api/org/apache/logging/log4j/Level.html#forName(java.lang.String,int)[the `Level.forName()` method]: @@ -122,45 +100,113 @@ public class PurchaseOrder { ---- <1> Logging with the created custom level +[#config] +== Configuration + +Levels can be used in various ways in a configuration file. +We will explain some important ones from multiple logging implementations. + +[#config-core] +=== Log4j Core + +Log4j Core, the reference implementation of Log4j API, has extensive support for levels. +It allows both <> and <>. + +[#config-core-filter] +==== Basic filtering + +Filtering on levels for loggers is a pretty common example: + +[tabs] +==== +XML:: ++ +.Snippet from an example {antora-examples-url}/manual/customloglevels/filtering/log4j2.xml[`log4j2.xml`] configuring level-filtered loggers +[source,xml] +---- +include::example$manual/customloglevels/filtering/log4j2.xml[lines=14..19,indent=0] +---- + +JSON:: ++ +.Snippet from an example {antora-examples-url}/manual/customloglevels/filtering/log4j2.json[`log4j2.json`] configuring level-filtered loggers +[source,xml] +---- +include::example$manual/customloglevels/filtering/log4j2.json[lines=9..22,indent=0] +---- + +YAML:: ++ +.Snippet from an example {antora-examples-url}/manual/customloglevels/filtering/log4j2.yaml[`log4j2.yaml`] configuring level-filtered loggers +[source,xml] +---- +include::example$manual/customloglevels/filtering/log4j2.yaml[lines=24..31,indent=0] +---- + +Properties:: ++ +.Snippet from an example {antora-examples-url}/manual/customloglevels/filtering/log4j2.properties[`log4j2.properties`] configuring level-filtered loggers +[source,xml] +---- +include::example$manual/customloglevels/filtering/log4j2.properties[lines=21..24] +---- +==== +<1> Logs of level `INFO` or higher severity (i.e., `WARN`, `ERROR`, `FATAL`) are allowed for `com.mycompany` package +<2> Logs of level `ERROR` or higher severity (i.e., `FATAL`) are allowed for the rest + [#DefiningLevelsInConfiguration] -== Defining custom log levels in configuration +==== Custom log levels -Similar to defining log levels programmatically, a custom level must be defined first, before it can be used in a configuration file. +Log4j Core, the reference implementation of Log4j API, requires a custom level to be defined first, before it can be used in a configuration file. To facilitate this, the `CustomLevel` configuration element is used to define a custom level. Internally it calls the same `Level.forName()` method discussed in <>. The following example shows a configuration that defines the `VERBOSE` custom log level and uses it to filter log events sent to the console. +[tabs] +==== +XML:: ++ +.Snippet from an example {antora-examples-url}/manual/customloglevels/custom/log4j2.xml[`log4j2.xml`] configuring custom log levels +[source,xml] +---- +include::example$manual/customloglevels/custom/log4j2.xml[lines=17..26,indent=0] +---- + +JSON:: ++ +.Snippet from an example {antora-examples-url}/manual/customloglevels/custom/log4j2.json[`log4j2.json`] configuring custom log levels [source,xml] ---- - - - - - - - - - - - - - - - - - - - - - - - - +include::example$manual/customloglevels/custom/log4j2.json[lines=13..32,indent=0] ---- + +YAML:: ++ +.Snippet from an example {antora-examples-url}/manual/customloglevels/custom/log4j2.yaml[`log4j2.yaml`] configuring custom log levels +[source,xml] +---- +include::example$manual/customloglevels/custom/log4j2.yaml[lines=27..37,indent=0] +---- + +Properties:: ++ +.Snippet from an example {antora-examples-url}/manual/customloglevels/custom/log4j2.properties[`log4j2.properties`] configuring custom log levels +[source,xml] +---- +include::example$manual/customloglevels/custom/log4j2.properties[lines=24..30] +---- +==== <1> Defining the `VERBOSE` custom log level <2> Only events of `VERBOSE` level or higher severity are sent to the console + +[#config-logback] +=== Logback + +{logback-url}[Logback] can be configured as follows to define level-filtered loggers: + +.Snippet from an example {antora-examples-url}/manual/customloglevels/filtering/logback.xml[`logback.xml`] configuring level-filtered loggers +[source,xml] +---- +include::example$manual/customloglevels/filtering/logback.xml[lines=29..32,indent=0] +---- diff --git a/src/site/antora/modules/ROOT/pages/manual/extending.adoc b/src/site/antora/modules/ROOT/pages/manual/extending.adoc index 679aabb23b0..463269e16ff 100644 --- a/src/site/antora/modules/ROOT/pages/manual/extending.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/extending.adoc @@ -91,7 +91,7 @@ Yet when this happens, you can use xref:manual/systemproperties.adoc#log4j2.prov [#LoggerContextFactory] === `LoggerContextFactory` -link:../javadoc/log4j-api/org/apache/logging/log4j/spi/LoggerContextFactory.html[`LoggerContextFactory`] is the factory class used by Log4j API implementations to create xref:manual/architecture.adoc#logger-context[`LoggerContext`]s. +link:../javadoc/log4j-api/org/apache/logging/log4j/spi/LoggerContextFactory.html[`LoggerContextFactory`] is the factory class used by Log4j API implementations to create xref:manual/architecture.adoc#LoggerContext[`LoggerContext`]s. If you are using Log4j Core, you can provide a custom implementation using xref:manual/systemproperties.adoc#log4j2.loggerContextFactory[the `log4j2.loggerContextFactory` property]. Another way to provide a custom `LoggerContextFactory` is to <>. But this is generally discouraged, since it requires a more complicated setup and is intended mostly to be used by Log4j API implementations. @@ -105,7 +105,7 @@ It can be configured using xref:manual/systemproperties.adoc#log4j2.contextSelec [#ConfigurationFactory] === `ConfigurationFactory` -link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFactory.html[`ConfigurationFactory`] is the factory class used by Log4j Core to create link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`] instances given a xref:manual/architecture.adoc#logger-context[`LoggerContext`] and a link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationSource.html[`ConfigurationSource`]. +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFactory.html[`ConfigurationFactory`] is the factory class used by Log4j Core to create link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`] instances given a xref:manual/architecture.adoc#LoggerContext[`LoggerContext`] and a link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationSource.html[`ConfigurationSource`]. You can provide a custom `ConfigurationFactory` in the form of a <>. For example, see {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfigurationFactory.java[`XmlConfigurationFactory.java`] and {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfiguration.java[`XmlConfiguration.java`] of Log4j Core. diff --git a/src/site/antora/modules/ROOT/pages/manual/markers.adoc b/src/site/antora/modules/ROOT/pages/manual/markers.adoc index 0cc8f583f1e..a52e36decb5 100644 --- a/src/site/antora/modules/ROOT/pages/manual/markers.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/markers.adoc @@ -100,9 +100,7 @@ complex hierarchies of markers where possible. == Configuring filtering Developers can use markers to filter the log statements delivered to log files. -Marker processing is supported at least by -https://logback.qos.ch/manual/filters.html[Logback] -and the Log4j Core logging backends. +Marker processing is supported at least by {logback-url}/manual/filters.html[Logback] and the Log4j Core logging implementations. We will provide a sample configuration for both these backends. [#log4j-core] @@ -154,7 +152,7 @@ include::example$manual/markers/log4j2.properties[lines=17..-1] Logback differentiates two kinds of filters: ``TurboFilter``s, which are applied before a log event is created, and ``Filter``s, which are applied only when a log event reaches an appender. -See https://logback.qos.ch/manual/filters.html[Logback filters] for more information. +See {logback-url}/manual/filters.html[Logback filters] for more information. You can use a combination of `MarkerFilter`, `EvaluatorFilter` and `OnMarkerEvaluator` to redirect all messages marked with `SQL` to a specific appender, regardless of their level. In order to do that, you can use a configuration as below: From e4c028b749f8a723ed70f9893452f5b2a809ae96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Tue, 25 Jun 2024 15:43:06 +0200 Subject: [PATCH 19/53] Replace Ditaa with PlantUML Editing a Ditaa source requires significant Ascii art skills, which makes it difficult to modify. This contradicts with our initial motivation to move away from images. Switching to PlantUML instead, which is a no-brainer to use and adapt. --- .../modules/ROOT/partials/concepts.adoc | 101 ++++++++++-------- 1 file changed, 55 insertions(+), 46 deletions(-) diff --git a/src/site/antora/modules/ROOT/partials/concepts.adoc b/src/site/antora/modules/ROOT/partials/concepts.adoc index 04ef812bab7..57c3c4b42bc 100644 --- a/src/site/antora/modules/ROOT/partials/concepts.adoc +++ b/src/site/antora/modules/ROOT/partials/concepts.adoc @@ -79,53 +79,62 @@ In this case, you need to install `log4j-core` and `log4j-slf4j2-impl`. To make things a little bit more tangible, consider the following visualization of a typical Log4j Core installation with bridges for an application: -.Visualization of a typical Log4j Core installation with SLF4J, JUL, and JPL bridges. -[ditaa] +.Visualization of a typical Log4j Core installation with SLF4J, JUL, and JPL bridges +[plantuml] .... -/-----------------------------------------------------------------------------------\ -| | -| +------------+ +----------+ +----------+ | -| | | | | | | | -| | v | v | v | -| +-----+-----+ +---------+ +---------+ +----+----+ +-------+ +----+----+ +-------+ | -| | | |{d}c1FF | | | | | |{d}c1FF| | | |{d}c1FF| | -| |Application| |Log4j API| |Library 1| |Library 2| | SLF4J | |Library 3| | JUL | | -| | | | | | | | | | | | | | | | -| +-----------+ +--+------+ +----+----+ +---------+ +---+---+ +---------+ +---+---+ | -| : ^ | : : | -| | | | | | | -| Compile time | | | | | | -\------------------|---|---------|----------------------|---------------------|-----/ - | | | | | - | | | /-----/ /-----------/ - | | | | | - /----------/ \------+--|----------+-----|---------\ | - | | | | | | | -/-------|---------------------|--|----------|-----|---------|-----|-----------------\ -| | | | | | | | Runtime | -| v : | : v : v | -| +----------+ +-----+------+ +----+---------+ +---+--------+ | -| |cGRE | |cYEL | |cYEL | |cYEL | | -| |Log4j Core| |JPL to Log4j| |SLF4J to Log4j| |JUL to Log4j| | -| | | | | | | | | | -| +----------+ +------------+ +--------------+ +------------+ | -| |log4j2.xml| ^ | | -| +----------+ | | | -| | | | -| | | | -| /-------------+--|--/ | -| | | | | -| +----------+-------------|--|---------------------------------------------------+ | -| |JRE v : | | -| | +----+--+ | | -| | |{d}c1FF| | | -| | | JPL | | | -| | | | | | -| | +-------+ | | -| | | | -| +-------------------------------------------------------------------------------+ | -| | -\-----------------------------------------------------------------------------------/ +@startuml + +frame "Compile time" { + [Application] --> [Log4j API] : logs to + + [Log4j API] #Cyan + + [SLF4J] #Cyan + + [Library 1] --> [SLF4J] : logs to + [Application] --> [Library 1] : uses + [Application] --> [Library 2] : uses + [Application] --> [Library 3] : uses +} + +frame Runtime { + + [Log4j Core] <.. [Log4j API] : is implemented by + [Log4j Core] <.. (log4j2.xml) : is provided to + [Log4j Core] #LightGreen + + [JPL-to-Log4j] ..> [Log4j Core] : forwards to + [JPL-to-Log4j] #Yellow + + [SLF4J-to-Log4j] ..> [Log4j Core] : forwards to + [SLF4J-to-Log4j] #Yellow + + [JUL-to-Log4j] ..> [Log4j Core] : forwards to + [JUL-to-Log4j] #Yellow + + frame JRE { + [JPL] #Cyan + [JUL] #Cyan + } + +} + +[Library 2] --> [JUL] : logs to +[Library 3] --> [JPL] : logs to + +[JPL] ..> [JPL-to-Log4j] : is implemented by +[JUL] ..> [JUL-to-Log4j] : is implemented by +[SLF4J] ..> [SLF4J-to-Log4j] : is implemented by + +legend top right + | <#LightGreen> | Logging implementation | + | <#Yellow> | Logging bridge | + | <#Cyan> | Logging API | + | | Compile-time usage | + | | Runtime usage | +endlegend + +@enduml .... // end::visual[] From 4e18097e95da0cfd2c388fe58c3bc7511e3ba888 Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Tue, 25 Jun 2024 16:15:47 +0200 Subject: [PATCH 20/53] Revamp the `Web Applications` page (part 1) We update the `Web Applications` page by: * merging the `webapp.html`, `log4j-web.html`, `log4j-jakarta-web.xml` and `log4j-taglib.html` pages, * providing a table with all the initialization parameters, * moving the description of the web lookup and servlet appender to the right pages. --- log4j-jakarta-web/pom.xml | 1 + .../manual/appenders/servlet-appender.json | 23 + .../appenders/servlet-appender.properties | 28 ++ .../manual/appenders/servlet-appender.xml | 34 ++ .../manual/appenders/servlet-appender.yaml | 30 ++ .../examples/manual/webapp/AsyncServlet.java | 59 +++ .../ROOT/examples/manual/webapp/jndi.xml | 37 ++ .../ROOT/examples/manual/webapp/web.xml | 50 +++ src/site/antora/modules/ROOT/nav.adoc | 1 + .../antora/modules/ROOT/pages/javadoc.adoc | 3 + .../modules/ROOT/pages/log4j-jakarta-web.adoc | 27 -- .../antora/modules/ROOT/pages/log4j-web.adoc | 27 -- .../modules/ROOT/pages/manual/appenders.adoc | 91 +++- .../ROOT/pages/manual/configuration.adoc | 8 +- .../modules/ROOT/pages/manual/lookups.adoc | 77 ++-- .../modules/ROOT/pages/manual/webapp.adoc | 400 +++++++----------- .../manual/dependencies-log4j-1.2-api.adoc | 2 + .../dependencies-log4j-jakarta-web.adoc | 69 +++ src/site/resources/.htaccess | 3 +- 19 files changed, 616 insertions(+), 354 deletions(-) create mode 100644 src/site/antora/modules/ROOT/examples/manual/appenders/servlet-appender.json create mode 100644 src/site/antora/modules/ROOT/examples/manual/appenders/servlet-appender.properties create mode 100644 src/site/antora/modules/ROOT/examples/manual/appenders/servlet-appender.xml create mode 100644 src/site/antora/modules/ROOT/examples/manual/appenders/servlet-appender.yaml create mode 100644 src/site/antora/modules/ROOT/examples/manual/webapp/AsyncServlet.java create mode 100644 src/site/antora/modules/ROOT/examples/manual/webapp/jndi.xml create mode 100644 src/site/antora/modules/ROOT/examples/manual/webapp/web.xml delete mode 100644 src/site/antora/modules/ROOT/pages/log4j-jakarta-web.adoc delete mode 100644 src/site/antora/modules/ROOT/pages/log4j-web.adoc create mode 100644 src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-jakarta-web.adoc diff --git a/log4j-jakarta-web/pom.xml b/log4j-jakarta-web/pom.xml index f3434f9f855..9dc66676119 100644 --- a/log4j-jakarta-web/pom.xml +++ b/log4j-jakarta-web/pom.xml @@ -32,6 +32,7 @@ The Apache Log4j support for Jakarta EE 9+ web servlet containers + false + + + + + + + + + + + + + + diff --git a/src/site/antora/modules/ROOT/examples/manual/appenders/servlet-appender.yaml b/src/site/antora/modules/ROOT/examples/manual/appenders/servlet-appender.yaml new file mode 100644 index 00000000000..892fc58a358 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/appenders/servlet-appender.yaml @@ -0,0 +1,30 @@ +# +# 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. +# +Configuration: + Appenders: + # tag::servlet[] + Servlet: + name: "SERVLET" + PatternLayout: + pattern: "%m%n" + alwaysWriteExceptions: false # <1> + # end::servlet[] + Loggers: + Root: + level: "INFO" + AppenderRef: + ref: "SERVLET" diff --git a/src/site/antora/modules/ROOT/examples/manual/webapp/AsyncServlet.java b/src/site/antora/modules/ROOT/examples/manual/webapp/AsyncServlet.java new file mode 100644 index 00000000000..57fb03475fb --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/webapp/AsyncServlet.java @@ -0,0 +1,59 @@ +/* + * 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. + */ +package example; + +import javax.servlet.AsyncContext; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.web.Log4jWebSupport; +import org.apache.logging.log4j.web.WebLoggerContextUtils; + +@WebServlet(urlPatterns = "/async/*", asyncSupported = true) +public class AsyncServlet extends HttpServlet { + + private final Logger logger = LogManager.getLogger(); + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + // tag::manual[] + AsyncContext asyncContext = req.startAsync(); + Log4jWebSupport webSupport = WebLoggerContextUtils.getWebLifeCycle(getServletContext()); + asyncContext.start(() -> { + try { + webSupport.setLoggerContext(); + // Put your logic here + } finally { + webSupport.clearLoggerContext(); + } + }); + // end::manual[] + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) { + // tag::automatic[] + AsyncContext asyncContext = req.startAsync(); + asyncContext.start(WebLoggerContextUtils.wrapExecutionContext(getServletContext(), () -> { + // Put your logic here + })); + // end::automatic[] + } +} diff --git a/src/site/antora/modules/ROOT/examples/manual/webapp/jndi.xml b/src/site/antora/modules/ROOT/examples/manual/webapp/jndi.xml new file mode 100644 index 00000000000..10a71fb3c97 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/webapp/jndi.xml @@ -0,0 +1,37 @@ + + + + + + isLog4jContextSelectorNamed + true + + + log4jContextName + your_application_name + + + log4j/context-name + your_application_name + java.lang.String + + + diff --git a/src/site/antora/modules/ROOT/examples/manual/webapp/web.xml b/src/site/antora/modules/ROOT/examples/manual/webapp/web.xml new file mode 100644 index 00000000000..9c4fdf6db93 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/webapp/web.xml @@ -0,0 +1,50 @@ + + + + + + Handles Log4j Core lifecycle + + org.apache.logging.log4j.web.Log4jServletContextListener + + + + + + + Adds Log4j Core specific attributes to each request + log4jServletFilter + org.apache.logging.log4j.web.Log4jServletFilter + + + log4jServletFilter + /* + REQUEST + FORWARD + INCLUDE + ERROR + + + + diff --git a/src/site/antora/modules/ROOT/nav.adoc b/src/site/antora/modules/ROOT/nav.adoc index a003d7061a9..9bca0372ae5 100644 --- a/src/site/antora/modules/ROOT/nav.adoc +++ b/src/site/antora/modules/ROOT/nav.adoc @@ -66,6 +66,7 @@ * xref:migrate-from-logback.adoc[] * xref:migrate-from-slf4j.adoc[] * xref:hibernate.adoc[] +* xref:manual/webapp.adoc[] * xref:manual/cloud.adoc[] * xref:development.adoc[] diff --git a/src/site/antora/modules/ROOT/pages/javadoc.adoc b/src/site/antora/modules/ROOT/pages/javadoc.adoc index b871f40c86f..bb2b3c01988 100644 --- a/src/site/antora/modules/ROOT/pages/javadoc.adoc +++ b/src/site/antora/modules/ROOT/pages/javadoc.adoc @@ -26,4 +26,7 @@ The table below contains links to the Javadoc API Documentation for the componen | link:javadoc/log4j-core/index.html[Implementation] | The standard implementation, also called the Log4j 2 Core, that contains Appenders, Filters, and more. + +| link:javadoc/log4j-jakarta-web/index.html[Log4j Web] +| Tools to use Log4j in Jakarta EE applications. |=== diff --git a/src/site/antora/modules/ROOT/pages/log4j-jakarta-web.adoc b/src/site/antora/modules/ROOT/pages/log4j-jakarta-web.adoc deleted file mode 100644 index 8384e864edf..00000000000 --- a/src/site/antora/modules/ROOT/pages/log4j-jakarta-web.adoc +++ /dev/null @@ -1,27 +0,0 @@ -//// -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. -//// - -// TODO: turn this into a velocity template for all the version numbers -= Web Servlet Containers - -The Web module provides support for automatically enabling Log4j in Servlet containers. - -See the user manual page on xref:manual/webapp.adoc[Web Applications and JSPs] for details on using Log4j 2 in Web Applications. - -== Requirements - -The Web module requires Servlet 5.0 at minimum and is dependent on the Log4j 2 API and implementation. diff --git a/src/site/antora/modules/ROOT/pages/log4j-web.adoc b/src/site/antora/modules/ROOT/pages/log4j-web.adoc deleted file mode 100644 index be17df43524..00000000000 --- a/src/site/antora/modules/ROOT/pages/log4j-web.adoc +++ /dev/null @@ -1,27 +0,0 @@ -//// -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. -//// - -// TODO: turn this into a velocity template for all the version numbers -= Web Servlet Containers - -The Web module provides support for automatically enabling Log4j in Servlet containers. - -See the user manual page on xref:manual/webapp.adoc[Web Applications and JSPs] for details on using Log4j 2 in Web Applications. - -== Requirements - -The Web module requires Servlet 2.5 at minimum and is dependent on the Log4j 2 API and implementation. diff --git a/src/site/antora/modules/ROOT/pages/manual/appenders.adoc b/src/site/antora/modules/ROOT/pages/manual/appenders.adoc index 484b4ff38eb..a5e42037e5a 100644 --- a/src/site/antora/modules/ROOT/pages/manual/appenders.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/appenders.adoc @@ -2600,9 +2600,9 @@ file attribute view. |======================================================================= [#TriggeringPolicies] -== TriggeringPolicies +=== TriggeringPolicies -=== Composite Triggering Policy +==== Composite Triggering Policy The `CompositeTriggeringPolicy` combines multiple triggering policies and returns true if any of the configured policies return true. The @@ -2619,7 +2619,7 @@ For example, the following XML fragment defines policies that rollover the log w ---- -=== Cron Triggering Policy +==== Cron Triggering Policy The `CronTriggeringPolicy` triggers rollover based on a cron expression. This policy is controlled by a timer and is asynchronous in processing log events, so it is possible that log events from the previous or next period may appear at the beginning or end of the log file. @@ -2640,7 +2640,7 @@ expression indicates a rollover should have occurred between that time and the current time the file will be immediately rolled over. |======================================================================= -=== OnStartup Triggering Policy +==== OnStartup Triggering Policy The `OnStartupTriggeringPolicy` policy causes a rollover if the log file is older than the current JVM's start time and the minimum file size is met or exceeded. @@ -2660,7 +2660,7 @@ When running in Google App Engine, the OnStartup policy causes a rollover if the and falls back to Log4J initialization time instead.) [#sizebased-triggering-policy] -=== SizeBased Triggering Policy +==== SizeBased Triggering Policy The `SizeBasedTriggeringPolicy` causes a rollover once the file has reached the specified size. The size can be specified in bytes, with the suffix KB, MB, GB, or TB for example `20MB`. @@ -2672,7 +2672,7 @@ Otherwise, the target file will be overwritten on every rollover as the SizeBase When used without a time-based triggering policy the SizeBased Triggering Policy will cause the timestamp value to change. [#timebased-triggering-policy] -=== TimeBased Triggering Policy +==== TimeBased Triggering Policy The `TimeBasedTriggeringPolicy` causes a rollover once the date/time pattern no longer applies to the active file. This policy accepts an @@ -2701,10 +2701,10 @@ load of doing so across time. |======================================================================= [#RolloverStrategies] -== RolloverStrategies +=== RolloverStrategies [#DefaultRolloverStrategy] -=== DefaultRolloverStrategy +==== DefaultRolloverStrategy Default Rollover Strategy @@ -2798,7 +2798,7 @@ archived log file during compression. |======================================================================= [#DirectWriteRolloverStrategy] -=== DirectWriteRolloverStrategy +==== DirectWriteRolloverStrategy DirectWrite Rollover Strategy @@ -2959,7 +2959,7 @@ This sample configuration is the same as the previous one but limits the number ---- [#CustomDeleteOnRollover] -=== CustomDeleteOnRollover +==== CustomDeleteOnRollover Log Archive Retention Policy: Delete on Rollover @@ -3045,7 +3045,7 @@ and must return a list with the paths to delete. |======================================================================= [#DeleteIfFileName] -== DeleteIfFileName +=== DeleteIfFileName .IfFileName Condition Parameters [cols="20%,20%,60%",options="header",] @@ -3071,7 +3071,7 @@ the path name matches). |======================================================================= [#DeleteIfLastModified] -== DeleteIfLastModified +=== DeleteIfLastModified .IfLastModified Condition Parameters [cols="20%,20%,60%",options="header",] @@ -3090,7 +3090,7 @@ the file is old enough). |======================================================================= [#DeleteIfAccumulatedFileCount] -== DeleteIfAccumulatedFileCount +=== DeleteIfAccumulatedFileCount .IfAccumulatedFileCount Condition Parameters [cols="20%,20%,60%",options="header",] @@ -3107,7 +3107,7 @@ the threshold count has been exceeded). |======================================================================= [#DeleteIfAccumulatedFileSize] -== DeleteIfAccumulatedFileSize +=== DeleteIfAccumulatedFileSize .IfAccumulatedFileSize Condition Parameters [cols="20%,20%,60%",options="header",] @@ -3201,7 +3201,7 @@ During every rollover, this configuration will delete files that match "*/app-*. ---- [#ScriptCondition] -== ScriptCondition +=== ScriptCondition .ScriptCondition Parameters [cols="20%,20%,60%",options="header",] @@ -3216,7 +3216,7 @@ how ScriptFiles and ScriptRefs can be configured. |======================================================================= [#ScriptParameters] -== ScriptParameters +=== ScriptParameters .Script Parameters [cols="20%,20%,60%",options="header",] @@ -3306,7 +3306,7 @@ The Delete action will delete all files returned by the script. ---- [#CustomPosixViewAttributeOnRollover] -== CustomPosixViewAttributeOnRollover +=== CustomPosixViewAttributeOnRollover Log Archive File Attribute View Policy: Custom file attribute on Rollover @@ -3804,6 +3804,61 @@ Note that the AuditAppender was predefined while the RollingFileAppenders are cr ---- +[#servlet-appender] +== Servlet appender + +The servlet appender allows users to forward all logging calls to the +https://jakarta.ee/specifications/servlet/5.0/apidocs/jakarta/servlet/servletcontext#log(java.lang.String,java.lang.Throwable)[`ServletContext.log(String, Throwable)`] +methods. +You can use it by declaring an appender of type `Servlet` in your configuration file: + +[tabs] +==== +XML:: ++ +[source,xml,indent=0] +---- +include::example$manual/appenders/servlet-appender.xml[tag=servlet] +---- + +JSON:: ++ +[source,xml,indent=0] +---- +include::example$manual/appenders/servlet-appender.json[tag=servlet] +---- + +YAML:: ++ +[source,xml,indent=0] +---- +include::example$manual/appenders/servlet-appender.yaml[tag=servlet] +---- + +Properties:: ++ +[source,xml,indent=0] +---- +include::example$manual/appenders/servlet-appender.properties[tag=servlet] +---- +==== + +<1> The appender passes the exception using the `Throwable` parameter of the +https://jakarta.ee/specifications/servlet/5.0/apidocs/jakarta/servlet/servletcontext#log(java.lang.String,java.lang.Throwable)[`ServletContext.log(String, Throwable)`] method. +Setting `alwaysWriteExceptions` to `false` prevents exceptions from appearing in the log file twice. + +Additional runtime dependencies are required for using the servlet appender: + +include::partial$manual/dependencies-log4j-jakarta-web.adoc[] + +See xref:manual/webapp.adoc[] for more information. + +[CAUTION] +==== +`ServletContext.log(String, Throwable)` is a very primitive logging interface. +Modern application servers usually forward its calls to a proper logging framework and use a **fixed** logging level for all the events. +==== + [[SMTPAppender]] == SMTPAppender @@ -3914,7 +3969,7 @@ As with other Appenders, the formatting can be controlled by specifying a Layout ---- [#ScriptAppenderSelector] -== ScriptAppenderSelector +=== ScriptAppenderSelector When the configuration is built, the `ScriptAppenderSelector` appender calls a `ScriptPlugin` to compute an appender name. Log4j then creates one of the appender named listed under `AppenderSet` using the name of the diff --git a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc index 5d9b94627b9..d6545fb7b79 100644 --- a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc @@ -18,7 +18,7 @@ = Configuration file Users can configure Log4j Core using different file formats. -The `log4j-core` artifact includes XML, JSON, YAML, and Java properties formats factories. +The `log4j-core` artifact includes XML, JSON, YAML, and Java properties formats factories. As detailed in the table below, some configuration formats require additional dependencies on the classpath. @@ -67,8 +67,7 @@ The `` and `` placeholders above have the following mean * for standalone Java SE applications, it is a random identifier, * for web applications, it is derived from the application descriptor. -See xref:manual/webapp.adoc#configuration[Log4j - Web application configuration] for more details. +See xref:manual/webapp.adoc#log4jContextName[Log4j Web application configuration] for more details. :: must be one of the file extensions assigned to a configuration file format: + @@ -139,7 +138,7 @@ Since XML was the original configuration format developed, the mapping from conf + [NOTE] ==== -There is an alternative XML configuration format called "XML strict format" that is activated +There is an alternative XML configuration format called "XML strict format" that is activated by setting the `strict` attribute of the main `` element to `true`. It allows users to use any tag names as long as they provide the plugin type using a `type` property. @@ -801,6 +800,7 @@ The same rule applies to the `name` parameter: if it contains a `${` sequence, i ===== If your configuration file contains the following definitions: + [tabs] ==== XML:: diff --git a/src/site/antora/modules/ROOT/pages/manual/lookups.adoc b/src/site/antora/modules/ROOT/pages/manual/lookups.adoc index b417b5cd9e0..72a037776e2 100644 --- a/src/site/antora/modules/ROOT/pages/manual/lookups.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/lookups.adoc @@ -72,6 +72,7 @@ be formatted as specified. The DockerLookup can be used to lookup attributes from the Docker container the application is running in. Log4j Docker provides access to the following container attributes: + [cols="1m,4a"] |=== |Key |Description @@ -136,6 +137,7 @@ when the `USER` environment variable is undefined, the default value ---- + [#EventLookup] == Event Lookup @@ -173,6 +175,7 @@ The EventLookup provides access to fields within the log event from the configur In this example the RoutingAppender picks a route based on the presence of a Marker named "AUDIT" being present in the log event. + [source,xml] ---- @@ -432,9 +435,9 @@ Example usage: The MapLookup serves several purposes. -1. Provide the base for Properties declared in the configuration file. -2. Retrieve values from MapMessages in LogEvents. -3. Retrieve values set with +1. Provide the base for Properties declared in the configuration file. +2. Retrieve values from MapMessages in LogEvents. +3. Retrieve values set with link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/MapLookup.html#setMainArguments%28java.lang.String%5B%5D%29[`MapLookup.setMainArguments(String[])`] The first item simply means that the MapLookup is used to substitute @@ -540,9 +543,10 @@ check for the existence of a marker where `name` is the marker name. If the marker exists, the expression returns the name, otherwise `null`. == Resource Bundle Lookup + The resource bundle lookup retrieves values from Resource Bundles (see Java documentation). The format is `${bundle:BundleName:BundleKey}`. The bundle name follows package naming conventions, for example: `${bundle:com.domain.Messages:MyKey}`. -[source, xml] +[source,xml] ---- @@ -552,11 +556,12 @@ The resource bundle lookup retrieves values from Resource Bundles (see Java docu ---- == Spring Boot Lookup + The Spring Boot Lookup retrieves the values of Spring properties from the Spring configuration as well as values of the active and default profiles. Specifying a key of "profiles.active" will return the active profiles while a key of "profiles.default" will return the default profiles. The default and active profiles can be an array. If more than one profile is present they will be returned as a comma separated list. To retrieve a single item from the array append "[\{index}]" to the key. For example, to return the first active profile in the list specify "profiles.active[0]". This Lookup will return null values until Spring Boot initializes application logging. The Spring Boot Lookup requires the log4j-spring-boot jar be included as a dependency. -[source, xml] +[source,xml] ---- @@ -641,61 +646,61 @@ result of a nested lookup. [#WebLookup] == Web Lookup -The WebLookup allows applications to retrieve variables that are -associated with the ServletContext. In addition to being able to -retrieve various fields in the ServletContext, WebLookup supports -looking up values stored as attributes or configured as initialization -parameters. The following table lists various keys that can be -retrieved: +The web lookup allows applications that run in a Jakarta EE environment to retrieve variables associated with a +https://jakarta.ee/specifications/servlet/5.0/apidocs/jakarta/servlet/servletcontext[`ServletContext`]. +See xref:manual/webapp.adoc[] for more information. -[cols="1m,4"] +.Web lookup supported keys +[cols="1m,5",options="header"] |=== -|Key |Description -|attr._name_ -|Returns the ServletContext attribute with the specified name +|Key +|Description -|contextPath -|The context path of the web application +|attr. +|Value of the ` servlet context attribute. |contextPathName -|The first token in the context path of the web application splitting on "/" characters. +|The **first** fragment of the servlet context path. + +|contextPath +|The **entire** servlet context path. |effectiveMajorVersion -|Gets the major version of the Servlet specification that the application -represented by this ServletContext is based on. +|The major version of the Servlet specification supported by the application. |effectiveMinorVersion -|Gets the minor version of the Servlet specification that the application -represented by this ServletContext is based on. +|The minor version of the Servlet specification supported by the application. -|initParam._name_ -|Returns the ServletContext initialization parameter with the specified name +|initParam. +|Value of the `` servlet context initialization parameter. |majorVersion -|Returns the major version of the Servlet API that this servlet container supports. +|The major version of the Servlet specification supported by the server. |minorVersion -|Returns the minor version of the Servlet API that this servlet container supports. +|The minor version of the Servlet specification supported by the server. |rootDir -|Returns the result of calling getRealPath with a value of "/". +|The root directory of the servlet context. |serverInfo -|Returns the name and version of the servlet container on which the servlet is running. +|The server info. |servletContextName -|Returns the name of the web application as defined in the display-name element of the deployment descriptor +|The servlet context name. + +| +|The value of `` is searched as context attribute or context initialization parameter. |=== -Any other key names specified will first be checked to see if a -ServletContext attribute exists with that name and then will be checked -to see if an initialization parameter of that name exists. If the key is -located then the corresponding value will be returned. +The web lookup can be used, for example, to provide a different log file for each application: [source,xml] ---- - - - + ---- + +Additional runtime dependencies are required for using web lookup: + +include::partial$manual/dependencies-log4j-jakarta-web.adoc[] diff --git a/src/site/antora/modules/ROOT/pages/manual/webapp.adoc b/src/site/antora/modules/ROOT/pages/manual/webapp.adoc index cdb0f780407..70851def827 100644 --- a/src/site/antora/modules/ROOT/pages/manual/webapp.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/webapp.adoc @@ -15,311 +15,231 @@ limitations under the License. //// -= Log4j Web Applications += Using Log4j in a Jakarta EE environment -== Using Log4j in Web Applications +[#web-applications] +== Integrating with Web Applications -You must take particular care when using Log4j or any other logging framework within a Java EE web application. It's important for logging resources to be properly cleaned up (database connections closed, files closed, etc.) when the container shuts down or the web application is undeployed. Because of the nature of class loaders within web applications, Log4j resources cannot be cleaned up through normal means. Log4j must be "started" when the web application deploys and "shut down" when the web application undeploys. How this works varies depending on whether your application is a <> or <> web application. - -Due to the namespace change from `javax` to `jakarta`, you need to use `log4j-jakarta-web` instead of `log4j-web` for Servlet 5.0 or newer. +[NOTE] +==== +To avoid problems, some Log4j API and Log4j Core features are automatically disabled, when running in a Jakarta EE environment. Most notably: -In either case, you'll need to add the `log4j-web` module to your deployment. +- the usage of `ThreadLocal` for object pooling is disabled, +- a web-safe implementation of +link:../javadoc/log4j-api/org/apache/logging/log4j/spi/ThreadContextMap.html[`ThreadContextMap`] is used, +- JMX notifications are sent synchronously, +- the JVM shutdown hook is disabled. -[.note] -==== -To avoid problems, the Log4j shutdown hook will automatically be disabled when the log4j-web jar is included. +See xref:manual/systemproperties.adoc#log4j2.isWebapp[`log4j2.isWebapp`] for more details. ==== -[id=configuration] -=== Configuration +Using a logging implementation like **Log4j Core** in a Jakarta EE application requires particular care. +Since the lifecycle of a container or web application is independent of the lifecycle of the JVM, it's important for logging resources to be properly cleaned up (database connections closed, files closed, etc.) when the container or web application shuts down. -Log4j allows the configuration file to be specified in web.xml using the `log4jConfiguration` context parameter. Log4j will search for configuration files by: +To properly synchronize the lifecycles of Log4j Core and Jakarta EE applications an additional **Log4j Web** artifact is provided. -1. If a location is provided it will be searched for as a servlet context resource. For example, if `log4jConfiguration` contains "logging.xml" then Log4j will look for a file with that name in the root directory of the web application. -2. If no location is defined Log4j will search for a file that starts with "log4j2" in the WEB-INF directory. If more than one file is found, and if a file that starts with "log4j2-\{name}" is present, where \{name} is the name of the web application, then it will be used. Otherwise the first file will be used. -3. The "normal" search sequence using the classpath and file URLs will be used to locate the configuration file. +[#log4j-jakarta-web-installation] +=== Installation -[#Servlet-3-0] -=== Servlet 3.0 and Newer Web Applications +In order to install Log4j Web in your Web application, you need to add it as runtime dependency: -A Servlet 3.0 or newer web application is any `` whose `version` attribute has a value of "3.0" or higher. Of course, the application must also be running in a compatible web container. +include::partial$manual/dependencies-log4j-jakarta-web.adoc[] -Some examples are: +If you are writing a Servlet 3.0 or later application, Apache Log4j Web will register a +https://jakarta.ee/specifications/servlet/5.0/apidocs/jakarta/servlet/servletcontainerinitializer[`ServletContainerInitializer`] +that takes care of configuring the Log4j lifecycle for you. Under the hood this will: -* Tomcat 7.0 and higher -* GlassFish 3.0 and higher -* JBoss 7.0 and higher -* Oracle WebLogic 12c and higher -* IBM WebSphere 8.0 and higher +* initialize Log4j Core with the correct configuration file, +* register a `Log4jServletContextListener` to automatically shut down Log4j Core, when the application shuts down, +* register a `Log4jServletFilter` to enable the xref:manual/lookups.adoc#WebLookup[web lookup]. -==== The Short Story +[WARNING] +==== +While the Servlet Specification allows web fragments to automatically add context listeners, it does not give any guarantees regarding the order in which those listeners are executed +(cf. https://jakarta.ee/specifications/servlet/5.0/jakarta-servlet-spec-5.0#Assembling_the_descriptor[section 8.2.3]). -Log4j 2 "just works" in Servlet 3.0 and newer web applications. It is capable of automatically starting when the application deploys and shutting down when the application undeploys. Thanks to the https://docs.oracle.com/javaee/6/api/javax/servlet/ServletContainerInitializer.html[ServletContainerInitializer] API added to Servlet 3.0, the relevant `Filter` and `ServletContextListener` classes can be registered dynamically on web application startup. +If other context listeners in your application use logging, you need to make sure that `Log4jServletContextListener` is the last listener to be executed at shutdown. +In order to do it you must create a `web.xml` descriptor and add the `Log4jServletContextListener` explicitly as **first** context listener: -[warning] -==== -Important Note! -For performance reasons, containers often ignore certain JARs known not to contain TLDs or `ServletContainerInitializer`s and do not scan them for web-fragments and initializers. Importantly, Tomcat 7 <7.0.43 ignores all JAR files named log4j*.jar, which prevents this feature from working. This has been fixed in Tomcat 7.0.43, Tomcat 8, and later. In Tomcat 7 <7.0.43 you will need to change `catalina.properties` and remove "log4j*.jar" from the `jarsToSkip` property. You may need to do something similar on other containers if they skip scanning Log4j JAR files. +[source,xml,indent=0] +---- +include::example$manual/webapp/web.xml[tag=context-listener] +---- ==== -==== The Long Story - -The Log4j 2 Web JAR file is a web-fragment configured to order before any other web fragments in your application. It contains a `ServletContainerInitializer` (`Log4jServletContainerInitializer`) that the container automatically discovers and initializes. This adds the `Log4jServletContextListener` and `Log4jServletFilter` to the `ServletContext`. These classes properly initialize and deinitialize the Log4j configuration. +==== Manual installation -For some users, automatically starting Log4j is problematic or undesirable. You can easily disable this feature using the `isLog4jAutoInitializationDisabled` context parameter. Simply add it to your deployment descriptor with the value "true" to disable auto-initialization. You _must_ define the context parameter in `web.xml`. If you set in programmatically, it will be too late for Log4j to detect the setting. +If you are maintaining an older Servlet 2.5 (or earlier) application or if you disabled -[source,xml] +[source,xml,indent=0] ---- - - isLog4jAutoInitializationDisabled - true - +include::example$manual/webapp/web.xml[tags=context-listener;filter] ---- -Once you disable auto-initialization, you must initialize Log4j as you would a <>. You must do so in a way that this initialization happens before any other application code (such as Spring Framework startup code) executes. +[#configuration] +=== Configuration -You can customize the behavior of the listener and filter using the `log4jContextName`, `log4jConfiguration`, and/or `isLog4jContextSelectorNamed` context parameters. Read more about this in the <> section below. You _must not_ manually configure the `Log4jServletContextListener` or `Log4jServletFilter` in your deployment descriptor (`web.xml`) or in another initializer or listener in a Servlet 3.0 or newer application _unless you disable auto-initialization_ with `isLog4jAutoInitializationDisabled`. Doing so will result in startup errors and unspecified erroneous behavior. +Log4j Web provides many configuration options to finely tune its installation. +These configuration options should be specified as +https://jakarta.ee/specifications/servlet/5.0/jakarta-servlet-spec-5.0#initialization-parameters[servlet context initialization parameters]. -[#Servlet-2-5] -=== Servlet 2.5 Web Applications +[id=isLog4jAutoInitializationDisabled] +==== `isLog4jAutoInitializationDisabled` -A Servlet 2.5 web application is any `` whose `version` attribute has a value of "2.5." The `version` attribute is the only thing that matters; even if the web application is running in a Servlet 3.0 or newer container, it is a Servlet 2.5 web application if the `version` attribute is "2.5." Note that Log4j 2 does not support Servlet 2.4 and older web applications. +[cols="1h,5"] +|=== +| Type | `boolean` +| Default value | `false` +|=== -If you are using Log4j in a Servlet 2.5 web application, or if you have disabled auto-initialization with the isLog4jAutoInitializationDisabled context parameter, you must configure the Log4jServletContextListener and Log4jServletFilter in the deployment descriptor or programmatically. The filter should match all requests of any type. The listener should be the very first listener defined in your application, and the filter should be the very first filter defined and mapped in your application. This is easily accomplished using the following web.xml code: +If set to `true`, the `Log4jServletContainerInitializer` will be disabled, which prevents the automatic registration of both the `Log4jServletContextListener` and `Log4jServletFilter`. -[source,xml] ----- - - org.apache.logging.log4j.web.Log4jServletContextListener - - - - log4jServletFilter - org.apache.logging.log4j.web.Log4jServletFilter - - - log4jServletFilter - /* - REQUEST - FORWARD - INCLUDE - ERROR - ASYNC - ----- +[id=isLog4jAutoShutdownDisabled] +==== `isLog4jAutoShutdownDisabled` -You can customize the behavior of the listener and filter using the log4jContextName, log4jConfiguration, and/or isLog4jContextSelectorNamed context parameters. Read more about this in the Context Parameters section below. +[cols="1h,5"] +|=== +| Type | `boolean` +| Default value | `false` +|=== -[#ContextParams] -=== Context Parameters +If set to `true`, the `Log4jServletContextListener` will not register a `Log4jServletContextListener` to handle the web application shut down. -By default, Log4j 2 uses the ServletContext's context name as the LoggerContext name and uses the standard pattern for locating the Log4j configuration file. There are three context parameters that you can use to control this behavior. The first, isLog4jContextSelectorNamed, specifies whether the context should be selected using the JndiContextSelector. If isLog4jContextSelectorNamed is not specified or is anything other than true, it is assumed to be false. +[id=log4j.stop.timeout.timeunit] +==== `log4j.stop.timeout.timeunit` -If isLog4jContextSelectorNamed is true, log4jContextName must be specified or display-name must be specified in web.xml; otherwise, the application will fail to start with an exception. log4jConfiguration should also be specified in this case, and must be a valid URI for the configuration file; however, this parameter is not required. +[cols="1h,5"] +|=== +| Type +| https://docs.oracle.com/javase/{java-target-version}/docs/api/java/util/concurrent/TimeUnit.html[`TimeUnit`] -If isLog4jContextSelectorNamed is not true, log4jConfiguration may optionally be specified and must be a valid URI or path to a configuration file or start with "classpath:" to denote a configuration file that can be found on the classpath. Without this parameter, Log4j will use the standard mechanisms for locating the configuration file. +| Default value +| https://docs.oracle.com/javase/{java-target-version}/docs/api/java/util/concurrent/TimeUnit.html#SECONDS[`SECONDS`] +|=== -When specifying these context parameters, you must specify them in the deployment descriptor (web.xml) even in a Servlet 3.0 or never application. If you add them to the ServletContext within a listener, Log4j will initialize before the context parameters are available and they will have no effect. Here are some sample uses of these context parameters. +The `TimeUnit` to use for the shut-down delay. -==== Set the Logging Context Name to "myApplication" +[id=log4j.stop.timeout] +==== `log4j.stop.timeout` -[source,xml] ----- - - log4jContextName - myApplication - ----- +[cols="1h,5"] +|=== +| Type +| `long` -==== Set the Configuration Path/File/URI to "/etc/myApp/myLogging.xml" +| Default value +| `30` +|=== -[source,xml] ----- - - log4jConfiguration - file:///etc/myApp/myLogging.xml - ----- +[id=log4jContextName] +==== `log4jContextName` -[#use-jndi-context-selector] -==== Use the JndiContextSelector +[cols="1h,5"] +|=== +| Type +| `String` -[source,xml] ----- - - isLog4jContextSelectorNamed - true - - - log4jContextName - appWithJndiSelector - - - log4jConfiguration - file:///D:/conf/myLogging.xml - ----- +| Default value +| _automatically computed_ +|=== -Note that in this case you must also set the "Log4jContextSelector" system property to "org.apache.logging.log4j.core.selector.JndiContextSelector". +Specifies the name of the logger context to use. -For security reasons, from Log4j 2.17.0, JNDI must be enabled by setting system property log4j2.enableJndiContextSelector=true +If <> is used, this parameter **must** be explicitly provided. Otherwise, the default value is: -== Using Web Application Information During the Configuration +. the servlet context name, if present, +. the servlet context path, including the leading `/`, otherwise. -You may want to use information about the web application during configuration. For example, you could embed the web application's context path in the name of a Rolling File Appender. See WebLookup in Lookups for more information. +[id=isLog4jContextSelectorNamed] +==== `isLog4jContextSelectorNamed` -=== JavaServer Pages Logging +[cols="1h,5"] +|=== +| Type +| `boolean` -You may use Log4j 2 within JSPs just as you would within any other Java code. Simply obtain a Logger and call its methods to log events. However, this requires you to use Java code within your JSPs, and some development teams rightly are not comfortable doing this. If you have a dedicated user interface development team that is not familiar with using Java, you may even have Java code disabled in your JSPs. +| Default value +| `false` +|=== -For this reason, Log4j 2 provides a JSP Tag Library that enables you to log events without using any Java code. To read more about using this tag library, xref:log4j-taglib.adoc[read the Log4j Tag Library documentation]. +Must be set to `true` to use the <>. -Important Note! As noted above, containers often ignore certain JARs known not to contain TLDs and do not scan them for TLD files. Importantly, Tomcat 7 <7.0.43 ignores all JAR files named log4j*.jar, which prevents the JSP tag library from being automatically discovered. This does not affect Tomcat 6.x and has been fixed in Tomcat 7.0.43, Tomcat 8, and later. In Tomcat 7 <7.0.43 you will need to change catalina.properties and remove "log4j*.jar" from the jarsToSkip property. You may need to do something similar on other containers if they skip scanning Log4j JAR files. +[id=log4jConfiguration] +==== `log4jConfiguration` -=== Asynchronous Requests and Threads +[cols="1h,5"] +|=== +| Type +| https://docs.oracle.com/javase/8/docs/api/java/net/URI.html[`URI`] -The handling of asynchronous requests is tricky, and regardless of Servlet container version or configuration Log4j cannot handle everything automatically. When standard requests, forwards, includes, and error resources are processed, the Log4jServletFilter binds the LoggerContext to the thread handling the request. After request processing completes, the filter unbinds the LoggerContext from the thread. +| Default value +| `false` +|=== -Similarly, when an internal request is dispatched using a javax.servlet.AsyncContext, the Log4jServletFilter also binds the LoggerContext to the thread handling the request and unbinds it when request processing completes. However, this only happens for requests dispatched through the AsyncContext. There are other asynchronous activities that can take place other than internal dispatched requests. +The location of a Log4j Core configuration file. +If the provided value is not an **absolute** URI, Log4j interprets it as: -For example, after starting an AsyncContext you could start up a separate thread to process the request in the background, possibly writing the response with the ServletOutputStream. Filters cannot intercept the execution of this thread. Filters also cannot intercept threads that you start in the background during non-asynchronous requests. This is true whether you use a brand-new thread or a thread borrowed from a thread pool. So what can you do for these special threads? +. path to a +https://jakarta.ee/specifications/servlet/5.0/apidocs/jakarta/servlet/servletcontext#getResource(java.lang.String)[servlet context resource], if it exists, +. path to a file, if it exists, +. path to a +https://docs.oracle.com/javase/8/docs/api/java/lang/ClassLoader.html#getResource-java.lang.String-[classpath resource], otherwise. -You may not need to do anything. If you didn't use the isLog4jContextSelectorNamed context parameter, there is no need to bind the LoggerContext to the thread. Log4j can safely locate the LoggerContext on its own. In these cases, the filter provides only very modest performance gains, and only when creating new Loggers. However, if you did specify the isLog4jContextSelectorNamed context parameter with the value "true", you will need to manually bind the LoggerContext to asynchronous threads. Otherwise, Log4j will not be able to locate it. +If no value is provided: -Thankfully, Log4j provides a simple mechanism for binding the LoggerContext to asynchronous threads in these special circumstances. The simplest way to do this is to wrap the Runnable instance that is passed to the AsyncContext.start() method. +. Log4j Web looks for a servlet context resource named `/WEB-INF/log4j2-.`, where `` is the name of the logger context, +. if no such file exists it looks for a servlet context resource named `/WEB-INF/log4j2.`, +. otherwise it searches for a configuration file on the classpath using the usual https://logging.apache.org/log4j/2.x/manual/configuration.html#automatic-configuration[automatic configuration procedure]. -[source,java] ----- -import java.io.IOException; -import javax.servlet.AsyncContext; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.web.WebLoggerContextUtils; - -public class TestAsyncServlet extends HttpServlet { - - @Override - protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { - final AsyncContext asyncContext = req.startAsync(); - asyncContext.start(WebLoggerContextUtils.wrapExecutionContext(this.getServletContext(), new Runnable() { - @Override - public void run() { - final Logger logger = LogManager.getLogger(TestAsyncServlet.class); - logger.info("Hello, servlet!"); - } - })); - } - - @Override - protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { - final AsyncContext asyncContext = req.startAsync(); - asyncContext.start(new Runnable() { - @Override - public void run() { - final Log4jWebSupport webSupport = - WebLoggerContextUtils.getWebLifeCycle(TestAsyncServlet.this.getServletContext()); - webSupport.setLoggerContext(); - // do stuff - webSupport.clearLoggerContext(); - } - }); - } -} ----- +[#async] +=== Asynchronous Requests and Threads + +In order for the +xref:manual/lookups.adoc#WebLookup[web lookup] +to work correctly, Log4j must be able to always identify the `ServletContext` used by the current thread. +When standard requests, forwards, includes, and error resources are processed, the `Log4jServletFilter` binds the `LoggerContext` to the thread handling the request, and you don't have to do anything. -This can be slightly more convenient when using Java 1.8 and lambda functions as demonstrated below. +The handling of asynchronous requests is however more tricky, since it allows you to execute code on threads that were not prepared by `Log4jServletFilter`. +Such a situation occurs for example, if your code was started using the +https://jakarta.ee/specifications/servlet/5.0/apidocs/jakarta/servlet/asynccontext#start(java.lang.Runnable)[`AsyncContext.start(Runnable)`] +method. -[source,java] +To successfully propagate the logger context along asynchronous calls, the +link:../javadoc/log4j-jakarta-web/org/apache/logging/log4j/web/WebLoggerContextUtils.html[`WebLoggerContextUtils`] +helper class is made available. +Using this class you can either decorate a `Runnable` with method calls that bind the appropriate logger context to the thread: + +[source,java,indent=0] ---- -import java.io.IOException; -import javax.servlet.AsyncContext; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.web.WebLoggerContextUtils; - -public class TestAsyncServlet extends HttpServlet { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - final AsyncContext asyncContext = req.startAsync(); - asyncContext.start(WebLoggerContextUtils.wrapExecutionContext(this.getServletContext(), () -> { - final Logger logger = LogManager.getLogger(TestAsyncServlet.class); - logger.info("Hello, servlet!"); - })); - } -} +include::example$manual/webapp/AsyncServlet.java[tag=automatic] ---- -Alternatively, you can obtain the `Log4jWebLifeCycle` instance from the ServletContext attributes, call its setLoggerContext method as the very first line of code in your asynchronous thread, and call its clearLoggerContext method as the very last line of code in your asynchronous thread. The following code demonstrates this. It uses the container thread pool to execute asynchronous request processing, passing an anonymous inner Runnable to the start method. +or, if more flexibility is required, you can apply the same logic by using +link:../javadoc/log4j-jakarta-web/org/apache/logging/log4j/web/Log4jWebSupport.html[`Log4jWebSupport`]: -[source,java] +[source,java,indent=0] ---- -import java.io.IOException; -import javax.servlet.AsyncContext; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.web.Log4jWebLifeCycle; -import org.apache.logging.log4j.web.WebLoggerContextUtils; - -public class TestAsyncServlet extends HttpServlet { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - final AsyncContext asyncContext = req.startAsync(); - asyncContext.start(new Runnable() { - @Override - public void run() { - final Log4jWebLifeCycle webLifeCycle = - WebLoggerContextUtils.getWebLifeCycle(TestAsyncServlet.this.getServletContext()); - webLifeCycle.setLoggerContext(); - try { - final Logger logger = LogManager.getLogger(TestAsyncServlet.class); - logger.info("Hello, servlet!"); - } finally { - webLifeCycle.clearLoggerContext(); - } - } - }); - } -} +include::example$manual/webapp/AsyncServlet.java[tag=manual] ---- -Note that you must call clearLoggerContext once your thread is finished processing. Failing to do so will result in memory leaks. If using a thread pool, it can even disrupt the logging of other web applications in your container. For that reason, the example here shows clearing the context in a finally block, which will always execute. +[#application-server] +== Configuring an application server -== Using the Servlet Appender +[#jndi-configuration] +=== Using JNDI selectors -Log4j provides a Servlet Appender that uses the servlet context as the log target. For example: +Log4j Core allows the usage of JNDI to coordinate the usage of logger contexts in a Jakarta EE application server. +In order to use this feature, you need to: -[source,xml] +. Set the +xref:manual/systemproperties.adoc#log4j2.contextSelector[`log4j2.contextSelector`] +Log4j configuration property to ``org.apache.logging.log4j.core.selector.JndiContextSelector``, +. For security reasons you need to enable the selector, by setting the +xref:manual/systemproperties.adoc#log4j2.enableJndiContextSelector[`log4j2.enableJndiContextSelector`] +Log4j configuration property to `true`, +. Each web application needs to configure the servlet context parameter `isLog4jContextSelectorNamed` to `true` and provide a value for the `log4jContextName` servlet context parameter and `java:comp/env/log4j/context-name` JNDI environment entry: ++ +[source,xml,indent=0] ---- - - - - - - - - - - - - - - - +include::example$manual/webapp/jndi.xml[tag=jndi] ---- - -To avoid double logging of exceptions to the servlet context, you must use %ex\{none} in your PatternLayout as shown in the example. The exception will be omitted from the message text but it is passed to the servlet context as the actual Throwable object. diff --git a/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-1.2-api.adoc b/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-1.2-api.adoc index 349feafc4ca..71d36a44057 100644 --- a/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-1.2-api.adoc +++ b/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-1.2-api.adoc @@ -21,6 +21,7 @@ Maven:: + [source,xml,subs="+attributes"] ---- + org.apache.logging.log4j log4j-1.2-api @@ -33,6 +34,7 @@ Gradle:: + [source,groovy,subs="+attributes"] ---- +// We assume you use `log4j-bom` for dependency management runtimeOnly 'org.apache.logging.log4j:log4j-1.2-api:{log4j-core-version}' ---- ==== \ No newline at end of file diff --git a/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-jakarta-web.adoc b/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-jakarta-web.adoc new file mode 100644 index 00000000000..39e10d936db --- /dev/null +++ b/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-jakarta-web.adoc @@ -0,0 +1,69 @@ +//// + 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. +//// + +[tabs] +==== +Maven:: ++ +[source,xml,subs="+attributes"] +---- + + + org.apache.logging.log4j + log4j-jakarta-web + runtime + +---- + +Gradle:: ++ +[source,groovy,subs="+attributes"] +---- +// We assume you use `log4j-bom` for dependency management +runtimeOnly 'org.apache.logging.log4j:log4j-jakarta-web' +---- +==== + +.Click here if you are you using Jakarta EE 8 or any version of Java EE? +[%collapsible] +===== +Jakarta EE 8 and all Java EE applications servers use the legacy `javax` package prefix instead of `jakarta`. +If you are using those application servers, you should replace the dependencies above with: + +[tabs] +==== +Maven:: ++ +[source,xml,subs="+attributes"] +---- + + + org.apache.logging.log4j + log4j-web + runtime + +---- + +Gradle:: ++ +[source,groovy,subs="+attributes"] +---- +// We assume you use `log4j-bom` for dependency management +runtimeOnly 'org.apache.logging.log4j:log4j-web' +---- +==== +===== \ No newline at end of file diff --git a/src/site/resources/.htaccess b/src/site/resources/.htaccess index 308ff31b1cd..d54394a584c 100644 --- a/src/site/resources/.htaccess +++ b/src/site/resources/.htaccess @@ -37,6 +37,7 @@ RewriteRule "^log4j-1\.2-api(/index)?\.html$" "manual/migration.html" [R=permane RewriteRule "^log4j-api/apidocs(.*)$" "javadoc/log4j-api$1" [R=permanent] RewriteRule "^log4j-api(/index)?\.html$" "manual/api.html" [R=permanent] RewriteRule "^log4j-core/apidocs(.*)$" "javadoc/log4j-core$1" [R=permanent] +RewriteRule "^log4j(-jakarta)?-web(/index)?\.html$" "manual/webapp.html" [R=permanent] RewriteRule "^log4j-jcl(/index)?\.html$" "manual/installation.html#impl-core-bridge-jcl" [R=permanent,NE] RewriteRule "^log4j-jmx-gui(/index)?\.html$" "/log4j/jmx-gui/latest/index.html" [R=permanent] RewriteRule "^log4j-jpl(/index)?\.html$" "manual/installation.html#impl-core-bridge-jpl" [R=permanent,NE] @@ -102,12 +103,10 @@ RewriteRule "^log4j-couchdb\.html/index\.html$" "log4j-couchdb.html" [R=permanen RewriteRule "^log4j-docker\.html/index\.html$" "log4j-docker.html" [R=permanent] RewriteRule "^log4j-flume-ng\.html/index\.html$" "log4j-flume-ng.html" [R=permanent] RewriteRule "^log4j-iostreams\.html/index\.html$" "log4j-iostreams.html" [R=permanent] -RewriteRule "^log4j-jakarta-web\.html/index\.html$" "log4j-jakarta-web.html" [R=permanent] RewriteRule "^log4j-jul\.html/index\.html$" "log4j-jul.html" [R=permanent,NE] RewriteRule "^log4j-spring-boot\.html/index\.html$" "log4j-spring-boot.html" [R=permanent] RewriteRule "^log4j-spring-cloud-config\.html/index\.html$" "log4j-spring-cloud-config.html" [R=permanent] RewriteRule "^log4j-spring-cloud-config-client\.html/index\.html$" "log4j-spring-cloud-config-client.html" [R=permanent] RewriteRule "^log4j-taglib\.html/index\.html$" "log4j-taglib.html" [R=permanent] RewriteRule "^log4j-to-jul\.html/index\.html$" "log4j-to-jul.html" [R=permanent] -RewriteRule "^log4j-web\.html/index\.html$" "log4j-web.html" [R=permanent] RewriteRule "^release-notes\.html/index\.html$" "release-notes.html" [R=permanent] From c6dce1f584af13c1ffa4f9cedf1e546cac4a7c62 Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Tue, 25 Jun 2024 22:32:40 +0200 Subject: [PATCH 21/53] Add JSP documentation --- .../ROOT/examples/manual/webapp/taglib.jsp | 73 +++++++++++++++++ .../modules/ROOT/pages/manual/webapp.adoc | 78 ++++++++++++++++++- .../dependencies-log4j-jakarta-web.adoc | 8 +- .../manual/dependencies-log4j-taglib.adoc | 39 ++++++++++ src/site/resources/.htaccess | 2 +- 5 files changed, 192 insertions(+), 8 deletions(-) create mode 100644 src/site/antora/modules/ROOT/examples/manual/webapp/taglib.jsp create mode 100644 src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-taglib.adoc diff --git a/src/site/antora/modules/ROOT/examples/manual/webapp/taglib.jsp b/src/site/antora/modules/ROOT/examples/manual/webapp/taglib.jsp new file mode 100644 index 00000000000..fc447c16096 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/webapp/taglib.jsp @@ -0,0 +1,73 @@ +<%@ page contentType="text/html;charset=UTF-8" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + +<%@ taglib prefix="log4j" uri="http://logging.apache.org/log4j/tld/log" %> + + + + + + + Log4j taglib example + + +

This page presents several examples of Log4j Taglib usage:

+
    +
  • + Simple messages: + + + +
  • +
  • + Parameterized messages: + + + +
  • +
  • + Markers: + + + +
  • +
  • + Catching/throwing: + + + <%= 5 / 0 %> + + + + + +
  • +
  • + Testing for the current log level: + + + INFO is enabled. + + +
  • +
  • + Setting the logger name: + + + +
  • +
  • + Dumping JSP scopes as HTML: + + + +
  • +
+ + + + + + + + \ No newline at end of file diff --git a/src/site/antora/modules/ROOT/pages/manual/webapp.adoc b/src/site/antora/modules/ROOT/pages/manual/webapp.adoc index 70851def827..ca3259f100b 100644 --- a/src/site/antora/modules/ROOT/pages/manual/webapp.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/webapp.adoc @@ -145,7 +145,7 @@ The `TimeUnit` to use for the shut-down delay. Specifies the name of the logger context to use. -If <> is used, this parameter **must** be explicitly provided. Otherwise, the default value is: +If <> is used, this parameter **must** be explicitly provided. Otherwise, the default value is: . the servlet context name, if present, . the servlet context path, including the leading `/`, otherwise. @@ -162,7 +162,7 @@ If <> is used, this | `false` |=== -Must be set to `true` to use the <>. +Must be set to `true` to use the <>. [id=log4jConfiguration] ==== `log4jConfiguration` @@ -192,7 +192,7 @@ If no value is provided: . otherwise it searches for a configuration file on the classpath using the usual https://logging.apache.org/log4j/2.x/manual/configuration.html#automatic-configuration[automatic configuration procedure]. [#async] -=== Asynchronous Requests and Threads +=== Asynchronous requests and threads In order for the xref:manual/lookups.adoc#WebLookup[web lookup] @@ -222,6 +222,78 @@ link:../javadoc/log4j-jakarta-web/org/apache/logging/log4j/web/Log4jWebSupport.h include::example$manual/webapp/AsyncServlet.java[tag=manual] ---- +[#log4j-taglib] +=== Logging in JavaServer Pages + +To help users add logging statements to JavaServer Pages, Log4j provides a JSP tag library modeled after the +https://web.archive.org/web/20140215182415/http://jakarta.apache.org/taglibs/log/[Jakarta Commons Log Tag library]. +In order to use it, you need to add the following runtime dependency to your web application project: + +include::partial$manual/dependencies-log4j-taglib.adoc[] + +and add the following declaration to your JSP pages: + +[source,jsp] +---- +include::example$manual/webapp/taglib.jsp[tag=declaration] +---- + +[WARNING] +==== +The Log4j Taglib component is deprecated and is scheduled for removal in Log4j 3. + +Currently, it only works with JavaServer Pages 2.3 or older and no version compatible with Jakarta Server Pages 3.0 is available. +==== + +The Log4j Taglib library defines a tag for most +link:../javadoc/log4j-api/org/apache/logging/log4j/Logger.html[`Logger`] +methods, including: + +* simple and parameterized log statements: ++ +[source,xml,indent=0] +---- +include::example$manual/webapp/taglib.jsp[tag=simple] +---- + +* flow tracing statements: ++ +[source,xml,indent=0] +---- +include::example$manual/webapp/taglib.jsp[tag=entry-exit] +---- + +* catching and throwing statements: ++ +[source,xml,indent=0] +---- +include::example$manual/webapp/taglib.jsp[tag=catching] +---- + +* tags to test the current log level: ++ +[source,xml,indent=0] +---- +include::example$manual/webapp/taglib.jsp[tag=if-enabled] +---- + +* tags to set the name of the logger used: ++ +[source,xml,indent=0] +---- +include::example$manual/webapp/taglib.jsp[tag=set-logger] +---- + +* a `dump` tag that prints the contents of a JSP scope: ++ +[source,xml,indent=0] +---- +include::example$manual/webapp/taglib.jsp[tag=dump] +---- + +A complete JSP page example is available +{antora-examples-url}/manual/webapp/taglib.jsp[on GitHub]. + [#application-server] == Configuring an application server diff --git a/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-jakarta-web.adoc b/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-jakarta-web.adoc index 39e10d936db..125b944b96d 100644 --- a/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-jakarta-web.adoc +++ b/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-jakarta-web.adoc @@ -19,7 +19,7 @@ ==== Maven:: + -[source,xml,subs="+attributes"] +[source,xml] ---- @@ -31,7 +31,7 @@ Maven:: Gradle:: + -[source,groovy,subs="+attributes"] +[source,groovy] ---- // We assume you use `log4j-bom` for dependency management runtimeOnly 'org.apache.logging.log4j:log4j-jakarta-web' @@ -48,7 +48,7 @@ If you are using those application servers, you should replace the dependencies ==== Maven:: + -[source,xml,subs="+attributes"] +[source,xml] ---- @@ -60,7 +60,7 @@ Maven:: Gradle:: + -[source,groovy,subs="+attributes"] +[source,groovy] ---- // We assume you use `log4j-bom` for dependency management runtimeOnly 'org.apache.logging.log4j:log4j-web' diff --git a/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-taglib.adoc b/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-taglib.adoc new file mode 100644 index 00000000000..7e8c4a48ac6 --- /dev/null +++ b/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-taglib.adoc @@ -0,0 +1,39 @@ +//// + 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. +//// + +[tabs] +==== +Maven:: ++ +[source,xml] +---- + + + org.apache.logging.log4j + log4j-taglib + runtime + +---- + +Gradle:: ++ +[source,groovy] +---- +// We assume you use `log4j-bom` for dependency management +runtimeOnly 'org.apache.logging.log4j:log4j-taglib' +---- +==== diff --git a/src/site/resources/.htaccess b/src/site/resources/.htaccess index d54394a584c..1faf36dcc87 100644 --- a/src/site/resources/.htaccess +++ b/src/site/resources/.htaccess @@ -44,6 +44,7 @@ RewriteRule "^log4j-jpl(/index)?\.html$" "manual/installation.html#impl-core-bri RewriteRule "^log4j-mongodb3(/index)?\.html$" "manual/appenders.html#NoSQLAppenderMongoDB" [R=permanent,NE] RewriteRule "^log4j-mongodb4(/index)?\.html$" "manual/appenders.html#log4j-mongodb4" [R=permanent,NE] RewriteRule "^log4j-slf4j2?-impl(/index)?\.html$" "manual/installation.html#impl-core-bridge-slf4j" [R=permanent,NE] +RewriteRule "^log4j-taglib(/index)?\.html$" "manual/webapp.html#log4j-taglib" [R=permanent,NE] RewriteRule "^manual/api-separation\.html$" "manual/api.html" [R=permanent] RewriteRule "^manual/extending\.html#Layouts$" "manual/layouts.html#extending" [R=permanent] RewriteRule "^manual/extending\.html#PatternConverters$" "manual/pattern-layout.html#extending-converters" [R=permanent] @@ -107,6 +108,5 @@ RewriteRule "^log4j-jul\.html/index\.html$" "log4j-jul.html" [R=permanent,NE] RewriteRule "^log4j-spring-boot\.html/index\.html$" "log4j-spring-boot.html" [R=permanent] RewriteRule "^log4j-spring-cloud-config\.html/index\.html$" "log4j-spring-cloud-config.html" [R=permanent] RewriteRule "^log4j-spring-cloud-config-client\.html/index\.html$" "log4j-spring-cloud-config-client.html" [R=permanent] -RewriteRule "^log4j-taglib\.html/index\.html$" "log4j-taglib.html" [R=permanent] RewriteRule "^log4j-to-jul\.html/index\.html$" "log4j-to-jul.html" [R=permanent] RewriteRule "^release-notes\.html/index\.html$" "release-notes.html" [R=permanent] From 3d2feab27bf4557a9bfbd113edf651cda914885a Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Tue, 25 Jun 2024 22:37:32 +0200 Subject: [PATCH 22/53] Fix RAT failure --- .../ROOT/examples/manual/webapp/taglib.jsp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/site/antora/modules/ROOT/examples/manual/webapp/taglib.jsp b/src/site/antora/modules/ROOT/examples/manual/webapp/taglib.jsp index fc447c16096..83d72dc7fc4 100644 --- a/src/site/antora/modules/ROOT/examples/manual/webapp/taglib.jsp +++ b/src/site/antora/modules/ROOT/examples/manual/webapp/taglib.jsp @@ -1,3 +1,19 @@ + <%@ page contentType="text/html;charset=UTF-8" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> From 4c7414c1212b2f70e20645aea5b444947ea98667 Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Wed, 26 Jun 2024 14:22:28 +0200 Subject: [PATCH 23/53] Add information about reconfiguration --- .../ROOT/pages/manual/architecture.adoc | 256 +++++++++++------- 1 file changed, 162 insertions(+), 94 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/architecture.adoc b/src/site/antora/modules/ROOT/pages/manual/architecture.adoc index 91213fec007..3c9b032d251 100644 --- a/src/site/antora/modules/ROOT/pages/manual/architecture.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/architecture.adoc @@ -36,127 +36,112 @@ note left of LoggerContext { Anchor for the logging system } -LoggerContext --> Configuration - LoggerContext --> "0..*" Logger -class Configuration { - Appender[] appenders - Filter[] filters - LoggerConfig[] loggerConfigs - LoggerConfig getLoggerConfig(String name) - StrSubstitutor substitutor -} +package "Configuration" as c { -note left of Configuration - Encapsulates components compiled - from a user-provided configuration - file (e.g., `log4j2.xml`) -end note + class Configuration { + Appender[] appenders + Filter[] filters + LoggerConfig[] loggerConfigs + LoggerConfig getLoggerConfig(String name) + StrSubstitutor substitutor + } -Configuration --> "0..*" Filter + note left of Configuration + Encapsulates components compiled + from a user-provided configuration + file (e.g., `log4j2.xml`) + end note -Configuration --> "0..*" Appender + Configuration --> Filter -Configuration --> "0..*" LoggerConfig + Configuration --> "0..*" Appender -Configuration --> StrSubstitutor + Configuration --> "0..*" LoggerConfig -class Appender { - Layout layout - void append(LogEvent) -} + Configuration --> StrSubstitutor -Appender -[#green,thickness=6]-> Layout + class Appender { + AbstractManager manager + Layout layout + Filter filter + void append(LogEvent) + } -class Layout { - byte[] encode(LogEvent) -} + Appender --> Layout -class Filter { - Result filter(LogEvent) -} + Appender --> Filter -note right of Filter - Note that a `Filter` can - be provided at 4 levels: - 1. `Configuration` - 2. `LoggerConfig` - 3. `AppenderRef` - 4. `AppenderControl` -end note + class Layout { + byte[] encode(LogEvent) + } -class LoggerConfig { - AppenderRef[] appenderRefs - AppenderControl[] appenderControls - Level level - Filter filter - void log(LogEvent) -} + class Filter { + Result filter(LogEvent) + } -LoggerConfig --> "0..*" AppenderRef + note right of Filter + Note that a `Filter` can + be provided at 4 levels: + 1. `Configuration` + 2. `LoggerConfig` + 3. `AppenderControl` + 4. `Appender` + end note -LoggerConfig -[#green,thickness=6]-> "0..*" AppenderControl + class LoggerConfig { + AppenderControl[] appenderControls + Level level + Filter filter + void log(LogEvent) + } -LoggerConfig --> Filter + LoggerConfig -[#green,thickness=6]-> "0..*" AppenderControl -class AppenderRef { - String appenderName - Level level - Filter filter -} + LoggerConfig --> Filter -note right of AppenderRef - Denotes a user-provided - appender configuration - for a `Logger`, - e.g., ` Filter + AppenderControl -[#green,thickness=6]-> Appender -class AppenderControl { - Appender appender - Filter filter - void append(LogEvent) -} + AppenderControl --> Filter -note right of AppenderControl - Decorates an `Appender` - with a `Filter` -end note - -AppenderControl -[#green,thickness=6]-> Appender + class StrSubstitutor { + Interpolator interpolator + String replace(String input) + } -AppenderControl --> Filter + note right of StrSubstitutor + Responsible for + property substitution + (e.g., `${env:USER}`) + end note -class StrSubstitutor { - Interpolator interpolator - String replace(String input) -} + StrSubstitutor --> Interpolator -note right of StrSubstitutor - Responsible for - property substitution - (e.g., `${env:USER}`) -end note + class Interpolator { + StrLookup[] lookups + String lookup(String input) + } -StrSubstitutor --> Interpolator + Interpolator --> "0..*" StrLookup -class Interpolator { - StrLookup[] lookups - String lookup(String input) + class StrLookup { + String lookup(String input) + } } -Interpolator --> "0..*" StrLookup - -class StrLookup { - String lookup(String input) -} +LoggerContext --> Configuration class Logger { void log(Level level, Message message) @@ -169,6 +154,11 @@ end note Logger -[#green,thickness=6]-> LoggerConfig : delegates `log()` +class AbstractManager { +} + +Appender -[#green,thickness=6]-> AbstractManager + @enduml .... @@ -177,13 +167,70 @@ At a really high level, * A <>, the composition anchor, gets created in combination with a <>. Both can be created either directly (i.e., programmatically) or indirectly at first interaction with Log4j. * `LoggerContext` creates <>s that users interact with for logging purposes. -* <> delivers a link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html[`LogEvent`] to a target (file, socket, database, etc.) and typically uses a <> to encode log events. +* <> delivers a +link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html[`LogEvent`] +to a target (file, socket, database, etc.) and typically uses a <> to encode log events and an +link:../javadoc/log4j-core/org/apache/logging/log4j/core/appender/AbstractManager.html[`AbstractManager`] +to handle the lifecycle of the target resource. * <> encapsulates configuration for a `Logger`, as `AppenderControl` and `AppenderRef` for ``Appender``s. * <> is equipped with <> to allow property substitution in `String`-typed values. -* A typical `log()` call triggers a chain of invocations through classes `Logger`, `LoggerConfig`, `AppenderControl`, `Appender`, and `Layout` in order – this is depicted using green arrows in xref:architecture-diagram[xrefstyle=short]. +* A typical `log()` call triggers a chain of invocations through classes `Logger`, `LoggerConfig`, `AppenderControl`, `Appender`, and `AbstractManager` in order – this is depicted using green arrows in xref:architecture-diagram[xrefstyle=short]. Following sections examine this interplay in detail. +[#reconfiguration] +== Reconfiguration reliability + +The main motivation for such an architecture is reliability to configuration changes. +When a reconfiguration event occurs, two `Configuration` instances are active at the same time. +Threads that already started processing a log event will either: + +* continue logging to the old configuration, if execution already reached the `LoggerConfig` class, +* or switch to the new configuration. + +The service that manages the reconfiguration process is called +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ReliabilityStrategy.html[`ReliabilityStrategy`] +and it decides: + +* when should ``Logger``s switch to the new configuration, +* when should the old configuration be stopped. + +.Overview of the reconfiguration process +[plantuml] +.... +@startuml +left to right direction + +package LoggerContext { + object Logger + + package "New Configuration" as c2 { + object "LoggerConfig" as lc2 + object "AppenderControl" as ac2 + object "Appender" as app2 + } + + package "Old Configuration" as c1 { + object "LoggerConfig" as lc1 + object "AppenderControl" as ac1 + object "Appender" as app1 + } +} + +object AbstractManager + +Logger ..> lc1 +lc1 --> ac1 +ac1 --> app1 +app1 --> AbstractManager + +Logger --> lc2 +lc2 --> ac2 +ac2 --> app2 +app2 --> AbstractManager +@enduml +.... + [#LoggerContext] == `LoggerContext` @@ -730,6 +777,27 @@ In xref:#appender-additivity-diagram[xrefstyle=short], the effective appenders f |=== ==== +[#AbstractManager] +=== `AbstractManager` + +To multiplex the access to external resources (files, network connections, etc.), most appenders are split into an +link:../javadoc/log4j-core/org/apache/logging/log4j/core/appender/AbstractManager.html[`AbstractManager`] +that handles the low-level access to the external resource and an `Appender` that transforms log events into a format that the manager can handle. + +Managers that share the same resource are shared between appenders regardless of the `Configuration` or `LoggerContext` of the appenders. +For example +xref:manual/appenders.adoc#FileAppender[`FileAppender`]s +with the same `fileName` attribute all share the same +link:../javadoc/log4j-core/org/apache/logging/log4j/core/appender/FileManager.html[`FileManager`]. + +[IMPORTANT] +==== +Due to the manager-sharing feature of many Log4j appenders, it is not possible to configure multiple appenders for the same resource that only differ in the way the underlying resource is configured. + +For example, it is not possible to have two file appenders (even in different logger contexts) that use the same file, but a different value of the `append` option. +Since during a <> multiple instances of the same appender exists, it is also not possible to toggle the value of the `append` option through reconfiguration. +==== + [#Layout] == `Layout` From ae730af21a8a49c9d31e13402ee1e6d7939ab2ba Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Fri, 28 Jun 2024 13:06:13 +0200 Subject: [PATCH 24/53] Add and extend `log4j-appserver.adoc` and `logsep.adoc` --- .../examples/manual/webapp/log4j2-single.json | 48 +++ .../manual/webapp/log4j2-single.properties | 41 +++ .../examples/manual/webapp/log4j2-single.xml | 49 +++ .../examples/manual/webapp/log4j2-single.yaml | 44 +++ src/site/antora/modules/ROOT/nav.adoc | 7 +- .../modules/ROOT/pages/log4j-appserver.adoc | 50 --- .../modules/ROOT/pages/log4j-cassandra.adoc | 6 +- .../modules/ROOT/pages/log4j-taglib.adoc | 55 --- .../modules/ROOT/pages/manual/webapp.adoc | 327 +++++++++++++++++- src/site/resources/.htaccess | 2 +- 10 files changed, 512 insertions(+), 117 deletions(-) create mode 100644 src/site/antora/modules/ROOT/examples/manual/webapp/log4j2-single.json create mode 100644 src/site/antora/modules/ROOT/examples/manual/webapp/log4j2-single.properties create mode 100644 src/site/antora/modules/ROOT/examples/manual/webapp/log4j2-single.xml create mode 100644 src/site/antora/modules/ROOT/examples/manual/webapp/log4j2-single.yaml delete mode 100644 src/site/antora/modules/ROOT/pages/log4j-appserver.adoc delete mode 100644 src/site/antora/modules/ROOT/pages/log4j-taglib.adoc diff --git a/src/site/antora/modules/ROOT/examples/manual/webapp/log4j2-single.json b/src/site/antora/modules/ROOT/examples/manual/webapp/log4j2-single.json new file mode 100644 index 00000000000..6871604fb49 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/webapp/log4j2-single.json @@ -0,0 +1,48 @@ +{ + "Configuration": { + "Appenders": { + // tag::global[] + "File": { + "name": "GLOBAL", + "fileName": "logs/global.log", + "JsonTemplateLayout": { + "EventTemplateAdditionalField": { + "key": "contextName", + "value": "$${web:contextName}" + } + } + }, + // end::global[] + // tag::routing[] + "Routing": { + "name": "ROUTING", + "Routes": { + "pattern": "$${web:contextName:-common}", + "Route": { + "File": { + "name": "${web:contextName:-common}", + "fileName": "logs/${web:contextName:-common}.log", + "PatternLayout": { + "pattern": "d [%t] %-5p %c - %m%n" + } + } + } + } + } + // end::routing[] + }, + "Loggers": { + "Route": { + "level": "INFO", + "AppenderRef": [ + { + "ref": "GLOBAL" + }, + { + "ref": "ROUTING" + } + ] + } + } + } +} \ No newline at end of file diff --git a/src/site/antora/modules/ROOT/examples/manual/webapp/log4j2-single.properties b/src/site/antora/modules/ROOT/examples/manual/webapp/log4j2-single.properties new file mode 100644 index 00000000000..74d9236cc6a --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/webapp/log4j2-single.properties @@ -0,0 +1,41 @@ +# +# 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. +# +## +# tag::global[] +appender.0.type = File +appender.0.name = GLOBAL +appender.0.fileName = logs/global +appender.0.layout.type = JsonTemplateLayout +appender.0.layout.0.type = EventTemplateAdditionalField +appender.0.layout.0.key = contextName +appender.0.layout.0.value = $${web:contextName} +# end::global[] +# tag::routing[] +appender.1.type = Routing +appender.1.name = ROUTING +appender.1.route.type = Routes +appender.1.route.pattern = $${web:contextName:-common} +appender.1.route.0.type = Route +appender.1.route.0.appender.type = File +appender.1.route.0.appender.name = ${web:contextName:-common} +appender.1.route.0.appender.fileName = logs/${web:contextName:-common}.log +appender.1.route.0.appender.layout.type = PatternLayout +appender.1.route.0.appender.layout.pattern = %d [%t] %-5p %c - %m%n +# end::routing[] +rootLogger.level = INFO +rootLogger.appenderRef.0.ref = GLOBAL +rootLogger.appenderRef.1.ref = ROUTING diff --git a/src/site/antora/modules/ROOT/examples/manual/webapp/log4j2-single.xml b/src/site/antora/modules/ROOT/examples/manual/webapp/log4j2-single.xml new file mode 100644 index 00000000000..15bc4701cef --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/webapp/log4j2-single.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/site/antora/modules/ROOT/examples/manual/webapp/log4j2-single.yaml b/src/site/antora/modules/ROOT/examples/manual/webapp/log4j2-single.yaml new file mode 100644 index 00000000000..4f78d9964ef --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/webapp/log4j2-single.yaml @@ -0,0 +1,44 @@ +# +# 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. +# +Configuration: + Appenders: + # tag::global[] + File: + name: "GLOBAL" + fileName: "logs/global.log" + JsonTemplateLayout: + EventTemplateAdditionalField: + key: "contextName", + value: "$${web:contextName" + # end::global[] + # tag::routing[] + Routing: + name: "ROUTING" + Routes: + pattern: "$${web:contextName:-common}" + File: + name: "${web:contextName:-common}" + fileName: "logs/${web:contextName:-common}.log" + PatternLayout: + pattern: "%d [%t] %-5p %c - %m%n" + # end::routing[] + Loggers: + Root: + level: "INFO" + AppenderRef: + - ref: "GLOBAL" + - ref: "ROUTING" diff --git a/src/site/antora/modules/ROOT/nav.adoc b/src/site/antora/modules/ROOT/nav.adoc index 9bca0372ae5..88dd9776123 100644 --- a/src/site/antora/modules/ROOT/nav.adoc +++ b/src/site/antora/modules/ROOT/nav.adoc @@ -71,13 +71,18 @@ * xref:development.adoc[] .Components +* xref:log4j-cassandra.adoc[] +* xref:log4j-couchdb.adoc[] +* xref:log4j-docker.adoc[] * xref:log4j-flume-ng.adoc[] * xref:log4j-iostreams.adoc[] -* xref:log4j-docker.adoc[] +* xref:log4j-spring-boot.adoc[] +* xref:log4j-spring-cloud-config.adoc[] * xref:log4j-spring-cloud-config-client.adoc[] * xref:log4j-jul.adoc[] * xref:log4j-to-jul.adoc[] + .Related projects * {logging-services-url}/log4j/jakarta[Log4j Jakarta EE] * {logging-services-url}/log4j/jmx-gui[Log4j JMX GUI] diff --git a/src/site/antora/modules/ROOT/pages/log4j-appserver.adoc b/src/site/antora/modules/ROOT/pages/log4j-appserver.adoc deleted file mode 100644 index 1d96f95711e..00000000000 --- a/src/site/antora/modules/ROOT/pages/log4j-appserver.adoc +++ /dev/null @@ -1,50 +0,0 @@ -//// -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. -//// - -// TODO: turn this into a velocity template for all the version numbers - -#set($h1 = '#') #set($h2 = '##') #set($h3 = '###') #set($h4 = '####') - -$h1 Application Server Integration - -The Application Server module provides support for integrating Log4j into various Java Application Servers. - -$h2 Apache Tomcat - -Log4j may be used as the logging framework for Apache Tomcat. -This support is implemented automatically by including the log4j-api, log4j-core, and log4j-appserver jars in the boot classpath. -A file named log4j2-tomcat.xml, log4j2-tomcat.json, log4j2-tomcat.yaml, log4j2-tomcat.yml, or log4j2-tomcat.properties must also be placed in the boot classpath. -This is most easily done by: - -. Creating a set of directories in catalina home named log4j2/lib and log4j2/conf. -. Placing log4j2-api-$\{Log4jReleaseVersion}.jar, log4j2-core-$\{Log4jReleaseVersion}.jar, and log4j2-appserver-$\{Log4jReleaseVersion}.jar in the log4j2/lib directory. -. Creating a file named log4j2-tomcat.xml, log4j2-tomcat.json, log4j2-tomcat.yaml, log4j2-tomcat.yml, or log4j2-tomcat.properties in the log4j2/conf directory. -. Create or modify setenv.sh in the tomcat bin directory to include `CLASSPATH=$CATALINA_HOME/log4j2/lib/*:$CATALINA_HOME/log4j2/conf` - -$h3 Requirements - -Requires Tomcat 8.5 or later. - -$h2 Eclipse Jetty - -Log4j may be used as the logging framework for Eclipse Jetty. - -To direct Jetty to use this class, set the system property `org.eclipse.jetty.util.log.class` to `org.apache.logging.log4j.appserver.jetty.Log4j2Logger`. - -From the command line with: `-Dorg.eclipse.jetty.util.log.class = org.apache.logging.log4j.appserver.jetty.Log4j2Logger` - -Programmatically with: `System.setProperty("org.eclipse.jetty.util.log.class", "org.apache.logging.log4j.appserver.jetty.Log4j2Logger");` diff --git a/src/site/antora/modules/ROOT/pages/log4j-cassandra.adoc b/src/site/antora/modules/ROOT/pages/log4j-cassandra.adoc index 37846d689f2..292fb6d5d10 100644 --- a/src/site/antora/modules/ROOT/pages/log4j-cassandra.adoc +++ b/src/site/antora/modules/ROOT/pages/log4j-cassandra.adoc @@ -15,10 +15,6 @@ Licensed to the Apache Software Foundation (ASF) under one or more limitations under the License. //// -#set($h1='#') #set($h2='##') - -== TODO: use properties for dynamic dependency versions - -$h1 Cassandra Appender += Cassandra Appender The Cassandra Appender allow applications to send events to Apache Cassandra repositories. diff --git a/src/site/antora/modules/ROOT/pages/log4j-taglib.adoc b/src/site/antora/modules/ROOT/pages/log4j-taglib.adoc deleted file mode 100644 index e232964e509..00000000000 --- a/src/site/antora/modules/ROOT/pages/log4j-taglib.adoc +++ /dev/null @@ -1,55 +0,0 @@ -//// -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. -//// -= Log4j Tag Library - -The Log4j Log Tag Library creates the capability of inserting log statements in JSPs without the use of Java scripting. -It uses the standard Log4j 2 API to log messages according to your Log4j configuration. - -This tag library is based on the http://jakarta.apache.org/taglibs/log/[Jakarta Commons Log Taglib] by Joseph Ottinger and James Strachan. -For the most part, logging tags written against Jakarta Commons Log Taglib should work against this library as well. -However, the "category" attribute from Jakarta has become the "logger" attribute in this library. - -== Requirements - -The Log4j Tag Library requires at least Servlet 2.5 (or Java EE 5), at least JSP 2.1 (or Java EE 5), and is dependent on the Log4j 2 API. - -[IMPORTANT] -==== -For performance reasons, containers often ignore certain JARs known not to contain TLDs and do not scan them for TLD files. -Importantly, Tomcat 7 <7.0.43 ignores all JAR files named ``log4j\__.jar``, which prevents the JSP tag library from being automatically discovered. -This does not affect Tomcat 6.x and has been fixed in Tomcat 7.0.43, Tomcat 8, and later. -In Tomcat 7 <7.0.43 you will need to change `catalina.properties` and remove ``log4j__.jar`` from the `jarsToSkip` property. -You may need to do something similar on other containers if they skip scanning Log4j JAR files. -==== - -== Usage - -In accordance with the link:javadoc/log4j-api/org/apache/logging/log4j/Logger.html[`Logger`] API, this tag library has tags to support the following logging calls: "catching", "entry", "exit", "log", "trace", "debug", "info", "warn", "error", and "fatal". -The "trace" tag was not supported in Jakarta Commons Log Taglib. -The "setLogger", "catching", "entry", and "trace" tags are new to this library. -This tag library also supports the conditional tag "ifEnabled" (new) and troubleshooting tag "dump" (existed in Jakarta Commons Log Taglib). - -By default, this tag library uses a different Logger for each JSP named after the JSP ID. -You can customize the Logger in any of the logging tags or the "ifEnabled" tag with the "logger" attribute. -You can also use the "setLogger" tag to specify the Logger that should apply for the rest of a JSP's execution. -If the "setLogger" tag comes before any other logging tags in a JSP, the default Logger for that JSP will not be created, but instead the specified logger is the only one that will be used. - -Every effort was made to optimize these tags and ensure decent performance, but users of this library should keep in mind that the creation and execution of JSP tags adds significant overhead to the standard Log4j method calls. -While the "trace", "debug", and "info" options are available in this library, their uses are not nano-second order of magnitude with logging off that users may be used to with standard Log4j method calls. -Therefore, they should be used sparingly. -Use of the "ifEnabled" tag does not improve this performance; -this tag is available to conditionally evaluate other actions in addition to logging actions. diff --git a/src/site/antora/modules/ROOT/pages/manual/webapp.adoc b/src/site/antora/modules/ROOT/pages/manual/webapp.adoc index ca3259f100b..87391a9e3d5 100644 --- a/src/site/antora/modules/ROOT/pages/manual/webapp.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/webapp.adoc @@ -16,8 +16,30 @@ //// = Using Log4j in a Jakarta EE environment +:jetty-latest-url: https://jetty.org/docs/jetty/12 +:tomcat-latest-url: https://tomcat.apache.org/tomcat-11.0-doc +:wildfly-latest-url: https://docs.wildfly.org/32 -[#web-applications] +In a Jakarta EE environment, there are two possible approaches to logging: + +. Each application can use their own copy of Log4j Core and include `log4j-core` in the WAR or EAR archive. +. Applications can also use a single copy of Log4j Core that must be installed globally on the application server. + +While the first approach is the easiest to implement, it has some limitations: + +* Log events emitted by each application and the libraries bundled with it will be handled by Log4j Core, +but events related to the application emitted by a **shared** library (e.g. JPA implementation) will be handled by the application server. +To diagnose a problem with the application you might need to look into multiple log files. +* Each application must use a **different** log file to prevent problems with concurrent access to the same file by multiple applications. +Problems may arise especially if xref:manual/appenders.adoc#rollingfileappender[rolling file appender] is used. +* Web applications have a different lifecycle from the application server. +Additional care is required to stop Log4j Core when the application is stopped. +See <> for more details. + +The second approach is more complex and requires changes to the configuration of the application server. +See <> for more details. + +[#log4j-jakarta-web] == Integrating with Web Applications [NOTE] @@ -53,6 +75,8 @@ that takes care of configuring the Log4j lifecycle for you. Under the hood this * register a `Log4jServletContextListener` to automatically shut down Log4j Core, when the application shuts down, * register a `Log4jServletFilter` to enable the xref:manual/lookups.adoc#WebLookup[web lookup]. +See also <>. + [WARNING] ==== While the Servlet Specification allows web fragments to automatically add context listeners, it does not give any guarantees regarding the order in which those listeners are executed @@ -294,12 +318,249 @@ include::example$manual/webapp/taglib.jsp[tag=dump] A complete JSP page example is available {antora-examples-url}/manual/webapp/taglib.jsp[on GitHub]. -[#application-server] -== Configuring an application server +[#web-application-specific] +=== Application server specific notes + +[#web-application-wildfly] +WildFly:: ++ +WildFly implicitly adds a shared copy of `log4j-api` to each web application deployment. +This copy of `log4j-api` is configured to forward all events to WildFly's centralized logging system and does **not** use the copy of Log4j Core bundled with the web application. ++ +In order to use Log4j Core, you need to set the `add-logging-api-dependencies` attribute of the logging subsystem to `false`. +See {wildfly-latest-url}/Admin_Guide.html#logging-attributes[WildFly documentation] for more details. + +[#sharing] +== Sharing Log4j Core between Web Applications + +Since Log4j Core supports multiple xref:manual/architecture.adoc#LoggerContext[logger contexts], it is possible to share a single instance of Log4j Core +without losing the ability to configure logging for each application separately. + +Sharing Log4j Core has two main advantages: + +* You can send log statements from multiple applications to the same log file. +Under the hood Log4j Core will use a single +xref:manual/architecture.adoc#AbstractManager[manager] +per file, which will serialize concurrent access from multiple applications. +* You can capture log statements issued by other shared libraries, so you don't have to look for them in the global application server log. + +[#sharing-setup] +=== Setup + +In order to share Log4j Core between applications, you need to share at least these two JAR files: + +* https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-api/{log4j-api-version}[`log4j-api`] +* https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-core/{log4j-core-version}[`log4j-core`] +* https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-jakarta-web/{log4j-core-version}[`log4j-jakarta-web`] +(or +https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-web/{log4j-core-version}[`log4j-web`] +if you use a Java EE application server) + +Since sharing libraries between applications is not part of the Jakarta EE standard, the instructions are specific to each application server: + +GlassFish:: +In GlassFish you can add those libraries to the _common classloader_. +See https://glassfish.org/docs/latest/application-development-guide.html#circumventing-class-loader-isolation[GlassFish documentation] for more details. + +Jetty:: +Recent versions of Jetty have a `logging-log4j2` module that can be easily enabled to share Log4j Core between applications and to use Log4j Core for the Jetty server itself. +See {jetty-latest-url}/operations-guide/modules/index.html[Jetty Modules documentation] for more details. + +OpenLiberty:: +In OpenLiberty, you can add Log4j as a _global library_. +See https://openliberty.io/docs/latest/class-loader-library-config.html#_configure_global_libraries[OpenLiberty documentation] for more details. + +Tomcat:: +In Tomcat, you can use the _common classloader_. +See {tomcat-latest-url}/class-loader-howto.html[Tomcat classloader documentation] for more details. + +WildFly:: +You can install Log4j as a _global module_ or in a _global directory_. +See {wildfly-latest-url}/Admin_Guide.html#EE_Application_Deployment_Configuration[WildFly EE Application Deployment documentation] for more details. ++ +Check also the <>. + +[WARNING] +==== +Web application classloaders (see +https://jakarta.ee/specifications/servlet/6.0/jakarta-servlet-spec-6.0#web-application-class-loader[Servlet Specification 10.7.2] +) use a _"parent last"_ delegation strategy, but prevent application from overriding implementation classes provided by the container. + +If you share Log4j between applications and the applications themselves contain Log4j Core, the logging behavior depends on the application server. +Some application servers will use the shared instance (e.g. WildFly), while others will use the application instance (e.g. Tomcat). +There are two solutions to this problem: + +* you can remove Log4j from the WAR or EAR archive: + +Maven:: +If you use Maven, you can declare the +https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope[scope] +of all Log4j libraries as `provided`. + +Gradle:: +If you use Gradle, you can add `log4j-api` to the `providedCompile` configuration, while `log4j-core` to the `providedRuntime` configuration. +See https://docs.gradle.org/current/userguide/war_plugin.html#sec:war_dependency_management[Gradle WAR plugin] for more details. + +* you can use an application server specific configuration option to delegate the loading of Log4j API to the parent classloader. +==== + +[#log-separation] +=== Log separation + +When using a shared instance of Log4j Core, you might be interested to identify the application associated to a given log event. +Log4j Core provides a mechanism to split all +link:../javadoc/log4j-api/org/apache/logging/log4j/Logger.html[`Logger`] +instances into logging domains called +xref:manual/architecture.adoc#LoggerContext[`LoggerContext`]s. +You have therefore two ways to separate log events: + +. You can create a separate logger context for each web application and one for the common libraries. +See <> for more details. +. You can also use a **single** logger context for all log events, but use +xref:manual/lookups.adoc[lookups] to add context data to your log events. +See <> for more details. + +[IMPORTANT] +==== +These two approaches deliver similar results for log events generates by the web applications themselves or the libraries bundled in the WAR or EAR archive. + +Differences between these approaches appear in the handling of **shared** libraries. +There are two kinds of shared libraries: + +. Shared libraries that use **static** `Logger` fields. +These libraries will always use the same logger context, which will not be one of the per-application contexts. ++ +This kind includes all the shared libraries, which were not written with Jakarta EE in mind. + +. Shared libraries that use **instance** `Logger` fields. +These libraries will use the logger context associated to the web application that uses them. ++ +Application server implementation usually use instance `Logger` fields. + +Since the first kind of libraries is more common, counterintuitively the <> approach will usually give better results than the <> approach. +==== + +[#single-context] +==== Single logger context + +By default, Log4j Core creates a separate logger context per classloader. +In order to use a single logger context you need to set the +xref:manual/systemproperties.adoc#log4j2.contextSelector[`log4j2.contextSelector`] +system property to: + +* either +link:../javadoc/log4j-core/org/apache/logging/log4j/core/selector/BasicContextSelector.html[`org.apache.logging.log4j.core.selector.BasicContextSelector`] +to use synchronous loggers, +* or +link:../javadoc/log4j-core/org/apache/logging/log4j/core/async/BasicAsyncLoggerContextSelector.html[`org.apache.logging.log4j.core.async.BasicAsyncLoggerContextSelector`] +to use asynchronous loggers. + +In this approach you must use xref:manual/lookups.adoc[lookups] to register the application that generated a log event. +The most useful lookups in this case are: + +Web lookup:: +It does not require any setup, but it is available only after `Log4jServletFilter` has been executed. +Some log events pertinent to a web application can be unmarked. +See xref:manual/lookups.adoc#WebLookup[web lookup] for more information. + +JNDI lookup:: +It covers a larger part of the handling of a request, but it requires additional setup to export the name of the application via JNDI. +See xref:manual/lookups.adoc#JndiLookup[JNDI lookup] for more information. + +When using a single logger context you choose between: + +* logging all events to a single appender. +You'll need a tool like Elastic Search to separate log events. +We recommend the usage of a structured layout like +xref:manual/json-template-layout.adoc[] +in this case: ++ +[tabs] +==== +XML:: ++ +[source,xml,indent=0] +---- +include::example$manual/webapp/log4j2-single.xml[tag=global] +---- + +JSON:: ++ +[source,json,indent=0] +---- +include::example$manual/webapp/log4j2-single.json[tag=global] +---- + +YAML:: ++ +[source,yaml,indent=0] +---- +include::example$manual/webapp/log4j2-single.yaml[tag=global] +---- + +Properties:: ++ +[source,properties,indent=0] +---- +include::example$manual/webapp/log4j2-single.properties[tag=global] +---- +==== + +* logging events to a separate appender for each application. +In this case you can use +xref:manual/appenders.adoc#RoutingAppender[routing appender] +to separate the events: ++ +[tabs] +==== +XML:: ++ +[source,xml,indent=0] +---- +include::example$manual/webapp/log4j2-single.xml[tag=routing] +---- + +JSON:: ++ +[source,json,indent=0] +---- +include::example$manual/webapp/log4j2-single.json[tag=routing] +---- + +YAML:: ++ +[source,yaml,indent=0] +---- +include::example$manual/webapp/log4j2-single.yaml[tag=routing] +---- + +Properties:: ++ +[source,properties,indent=0] +---- +include::example$manual/webapp/log4j2-single.properties[tag=routing] +---- +==== + +[#multiple-contexts] +==== Multiple logger contexts + +Since Log4j Core uses +link:../javadoc/log4j-core/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.html[`ClassLoaderContextSelector`] +by default, no configuration is needed to achieve multiple logger contexts in your application server: +the classes of each classloader will use the logger context associated to the classloader. + +[TIP] +==== +To provide a different configuration file for each logger context, you can add files named `log4j2.xml` to the classpath of your application server. + +See <> and <> for more details. +==== -[#jndi-configuration] -=== Using JNDI selectors +Associating logger contexts to classloaders has however some limitations: **shared** libraries will not be able to use the per-application logger contexts. +To overcome this limitation Log4j Core provides an alternative algorithm to determine the right logger context to choose: JNDI lookups. +Application servers set up the correct JNDI context as soon as they determine, which application will handle a request. Log4j Core allows the usage of JNDI to coordinate the usage of logger contexts in a Jakarta EE application server. In order to use this feature, you need to: @@ -315,3 +576,59 @@ Log4j configuration property to `true`, ---- include::example$manual/webapp/jndi.xml[tag=jndi] ---- + +[#replace] +== Replacing application server logging subsystem + +Some application servers allow administrators to replace the default logging subsystem of the application server with Log4j Core. +Known instructions are listed in the section. +If your application server is not listed here, check the documentation of the application server. + +[#replace-tomcat] +=== Tomcat + +Tomcat uses a modified version of Apache Commons Logging called +{tomcat-latest-url}/logging.html[Tomcat JULI] +as internal logging system. +Tomcat JULI uses `java.util.logging` as default logging implementation, but since Tomcat 8.5 you can easily replace it with a different backend. + +In order to use Log4j Core as logging backend, you need to modify the +{tomcat-latest-url}/class-loader-howto.html#Class_Loader_Definitions[system classloader] +of the server. +Assuming `$CATALINA_BASE` is the main directory of your Tomcat instance you need to: + +. Create a `$CATALINA_BASE/log4j` folder to contain Log4j dependencies, +. Download the following JAR files into `$CATALINA_BASE/log4j`: +* https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-appserver/{log4j-api-version}[`log4j-appserver`]: the bridge between Tomcat JULI and Log4j API, +* https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-api/{log4j-api-version}[`log4j-api`], +* https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-core/{log4j-core-version}[`log4j-core`]. +. Add a Log4j Core xref:manual/configuration.adoc[configuration file] called either `log4j2.xml` or `log4j2-tomcat.xml` to the `$CATALINA_BASE/log4j` folder. +. Modify the system classloader classpath to include all the JAR files and the `$CATALINA_BASE/log4j` folder itself. +If you are starting Tomcat using the scripts in `$CATALINA_HOME/bin`, you can do it by creating a `$CATALINA_BASE/bin/setenv.sh` file with content: ++ +[source,shell] +---- +CLASSPATH="$CATALINA_HOME/log4j/*:$CATALINA_HOME/log4j/" +---- ++ +[TIP] +==== +Windows users can modify the classpath using the +https://commons.apache.org/proper/commons-daemon/procrun.html[Procrun monitor application] +GUI application. +The application is traditionally located in `$CATALINA_HOME/bin/tomcatw.exe`, where `` is the major version number of Tomcat. +==== + +[#replace-jetty] +=== Jetty + +In recent Jetty versions you just need to enable the `logging-log4j2` module. +See {jetty-latest-url}/operations-guide/modules/index.html[Jetty Modules documentation] for more details. + +On Jetty 9.x or earlier you need to: + +. Add the following JAR files to Jetty's classpath: +* https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-appserver/{log4j-api-version}[`log4j-appserver`], +* https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-api/{log4j-api-version}[`log4j-api`], +* https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-core/{log4j-core-version}[`log4j-core`]. +. Set the system property `org.eclipse.jetty.util.log.class` to `org.apache.logging.log4j.appserver.jetty.Log4j2Logger` \ No newline at end of file diff --git a/src/site/resources/.htaccess b/src/site/resources/.htaccess index 1faf36dcc87..a184b70b6c7 100644 --- a/src/site/resources/.htaccess +++ b/src/site/resources/.htaccess @@ -36,6 +36,7 @@ RewriteRule "^articles\.html$" "manual/index.html" [R=permanent] RewriteRule "^log4j-1\.2-api(/index)?\.html$" "manual/migration.html" [R=permanent] RewriteRule "^log4j-api/apidocs(.*)$" "javadoc/log4j-api$1" [R=permanent] RewriteRule "^log4j-api(/index)?\.html$" "manual/api.html" [R=permanent] +RewriteRule "^log4j-appserver(/index)\.html$" "manual/webapp.html#replace" [R=permanent,NE] RewriteRule "^log4j-core/apidocs(.*)$" "javadoc/log4j-core$1" [R=permanent] RewriteRule "^log4j(-jakarta)?-web(/index)?\.html$" "manual/webapp.html" [R=permanent] RewriteRule "^log4j-jcl(/index)?\.html$" "manual/installation.html#impl-core-bridge-jcl" [R=permanent,NE] @@ -98,7 +99,6 @@ RewriteRule "^thanks\.html$" "/support.html#sponsors" [R=permanent,NE] # 2. we replaced a directory like `log4j-api/` with a file that has the same prefix like `log4j-api.html`, # # if the URI path is `/log4j/2.x/log4j-api/foo` we must match `log4j-api.html/foo`. -RewriteRule "^log4j-appserver\.html/index\.html$" "log4j-appserver.html" [R=permanent] RewriteRule "^log4j-cassandra\.html/index\.html$" "log4j-cassandra.html" [R=permanent] RewriteRule "^log4j-couchdb\.html/index\.html$" "log4j-couchdb.html" [R=permanent] RewriteRule "^log4j-docker\.html/index\.html$" "log4j-docker.html" [R=permanent] From 0c6eee7c2a65c58f8c7a1ecc924f20678d05f498 Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Fri, 28 Jun 2024 13:12:38 +0200 Subject: [PATCH 25/53] Fix broken links --- src/site/antora/modules/ROOT/pages/manual/webapp.adoc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/webapp.adoc b/src/site/antora/modules/ROOT/pages/manual/webapp.adoc index 87391a9e3d5..da3fd5cb10a 100644 --- a/src/site/antora/modules/ROOT/pages/manual/webapp.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/webapp.adoc @@ -415,7 +415,7 @@ xref:manual/architecture.adoc#LoggerContext[`LoggerContext`]s. You have therefore two ways to separate log events: . You can create a separate logger context for each web application and one for the common libraries. -See <> for more details. +See <> for more details. . You can also use a **single** logger context for all log events, but use xref:manual/lookups.adoc[lookups] to add context data to your log events. See <> for more details. @@ -560,6 +560,9 @@ See <> and <> for more details. Associating logger contexts to classloaders has however some limitations: **shared** libraries will not be able to use the per-application logger contexts. To overcome this limitation Log4j Core provides an alternative algorithm to determine the right logger context to choose: JNDI lookups. +[#jndi-configuration] +==== JNDI context selector + Application servers set up the correct JNDI context as soon as they determine, which application will handle a request. Log4j Core allows the usage of JNDI to coordinate the usage of logger contexts in a Jakarta EE application server. In order to use this feature, you need to: From d0a26f2bc714d5f1c52449e6706d90d6d8022808 Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Fri, 28 Jun 2024 13:44:52 +0200 Subject: [PATCH 26/53] Add link to Payara (CE) classloading documentation --- src/site/antora/modules/ROOT/pages/manual/webapp.adoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/site/antora/modules/ROOT/pages/manual/webapp.adoc b/src/site/antora/modules/ROOT/pages/manual/webapp.adoc index da3fd5cb10a..0e814b1468e 100644 --- a/src/site/antora/modules/ROOT/pages/manual/webapp.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/webapp.adoc @@ -370,6 +370,9 @@ OpenLiberty:: In OpenLiberty, you can add Log4j as a _global library_. See https://openliberty.io/docs/latest/class-loader-library-config.html#_configure_global_libraries[OpenLiberty documentation] for more details. +Payara:: +See https://docs.payara.fish/community/docs/Technical%20Documentation/Payara%20Server%20Documentation/Server%20Configuration%20And%20Management/Classloading/Standard%20Classloading.html#common-libraries[Payara Common Libraries documentation]. + Tomcat:: In Tomcat, you can use the _common classloader_. See {tomcat-latest-url}/class-loader-howto.html[Tomcat classloader documentation] for more details. From 71fbef6b6c23d88952da54f62c32399863aa8188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Wed, 26 Jun 2024 15:56:25 +0200 Subject: [PATCH 27/53] Move `ConfigurationFactory` to `configuration.adoc` --- .../ROOT/pages/manual/config-intro.adoc | 15 +- .../ROOT/pages/manual/configuration.adoc | 153 ++++++++++-------- .../ROOT/pages/manual/customconfig.adoc | 51 ++---- .../configuration-file-format-deps.adoc | 18 +-- src/site/resources/.htaccess | 1 + 5 files changed, 120 insertions(+), 118 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/config-intro.adoc b/src/site/antora/modules/ROOT/pages/manual/config-intro.adoc index 90bf892d869..10a7f64378f 100644 --- a/src/site/antora/modules/ROOT/pages/manual/config-intro.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/config-intro.adoc @@ -17,17 +17,10 @@ [id=configuration] = Configuration -Logging is a standard method for monitoring the health of an application and diagnosing problems that may arise within it. Even moderately sized applications can contain thousands of logging statements. +To decide which of these statements will be logged and where, users need to configure Log4j Core in one of following ways: -To decide which of these statements will be logged and where, users need to configure Log4j Core in one of two ways: +* Through a xref:manual/configuration.adoc[configuration file] +* Through xref:manual/customconfig.adoc[programmatic configuration] -* through a xref:manual/configuration.adoc[]. -Since version 2.0, the configuration file format has been considered part of the public API and has remained stable across significant version upgrades. - -* through xref:manual/customconfig.adoc[Programmatic Configuration], which provides a larger spectrum of possible customizations but might require code changes during version upgrades. - -[NOTE] -==== -To prevent a chicken-and-egg problem, users can only supply some configuration options (e.g., the configuration file location) through xref:manual/systemproperties.adoc[configuration properties]. -==== \ No newline at end of file +Some meta-configuration options (e.g., the configuration file location) are only available through xref:manual/systemproperties.adoc[system properties]. diff --git a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc index 60fc2ca14ac..34ab7d000f5 100644 --- a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc @@ -17,100 +17,91 @@ [#configuration-file] = Configuration file -Users can configure Log4j Core using different file formats. -The `log4j-core` artifact includes XML, JSON, YAML, and Java properties formats factories. +Using a configuration file is the most popular and recommended approach for configuring Log4j Core. +In this page we will examine the composition of a configuration file and how Log4j Core uses it. -As detailed in the table below, some configuration formats require additional dependencies on the classpath. - -include::partial$configuration-file-format-deps.adoc[] - -[WARNING] +[TIP] ==== -The format of the configuration file changed between Log4j{nbsp}1 and Log4j{nbsp}2. -Files in the Log4j{nbsp}1 format are ignored by default. - -To enable partial support for old configuration formats, see xref:manual/migration.adoc#ConfigurationCompatibility[configuration compatibility]. +If you are looking for a quick start on using Log4j in your application or library, please refer to xref:manual/getting-started.adoc[] instead. ==== [id=automatic-configuration] == [[AutomaticConfiguration]] Configuration file location -Upon initialization of a new logger context, Log4j assigns it a context name and scans the following **classpath** locations for a configuration file: +Upon initialization of a new xref:manual/architecture.adoc#LoggerContext[logger context, the anchor of the logging implementation], Log4j Core assigns it a context name and scans the following **classpath** locations for a configuration file in following order: . Files named `log4j2-test.` -. Files named `log4j2-test.`, +. Files named `log4j2-test.` . Files named `log4j2.` -. Files named `log4j2.`, -. If no configuration file is found, Log4j uses the -link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/DefaultConfiguration.html[`DefaultConfiguration`] -and the xref:manual/status-logger.adoc[status logger] prints a warning. -The default configuration prints all messages less specific than -xref:manual/systemproperties.adoc#log4j2.level[`log4j2.level`] -to the console. +. Files named `log4j2.` -[WARNING] -==== -The configuration files prefixed by `log4j2-test` should only be used on the test classpath. +The `` and `` placeholders above have the following meaning -If multiple configuration files in the same category are found, Log4j uses a deterministic order to choose one of them (see -link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Order.html[`@Order`]). +:: +A name derived from the runtime environment: ++ +* For standalone Java SE applications, it is a random identifier. +* For web applications, it is an identifier derived from the application descriptor. +See xref:manual/webapp.adoc#configuration[Log4j Web application configuration] for details. -Nevertheless: +:: +A file extension supported by a `ConfigurationFactory`. +The order in which an extension will be searched for first depends on the order of the associated `ConfigurationFactory`. +See <> for details. -* If you're developing an app, don't use config files with different extensions. -* If you're developing a library, only add config files to your test classpath. -==== +If no configuration file is found, Log4j Core uses the link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/DefaultConfiguration.html[`DefaultConfiguration`] and the xref:manual/status-logger.adoc[status logger] prints a warning. +The default configuration prints all messages less specific than xref:manual/systemproperties.adoc#log4j2.level[`log4j2.level`] to the console. -The `` and `` placeholders above have the following meaning +You can override the location of the configuration file using the xref:manual/systemproperties.adoc#log4j2.configurationFile[the `log4j2.configurationFile` system property]. +In such a case, Log4j Core will guess the configuration file format from the provided file name, or use the default configuration factory if the extension is unknown. -:: depends on the runtime environment in which Log4j runs: +There are certain *best-practices* we strongly recommend you to adapt in your Log4j configuration: -* for standalone Java SE applications, it is a random identifier, -* for web applications, it is derived from the application descriptor. -See xref:manual/webapp.adoc#configuration[Log4j - Web application configuration] for more details. +* Files prefixed by `log4j2-test` should only be used on the test classpath. +* If you are developing an application, don't use multiple Log4j configuration files with same name, but different extensions. +That is, don't provide both `log4j2.xml` and `log4j2.json` files. +* If you are developing a library, only add configuration files to your test classpath. -:: must be one of the file extensions assigned to a configuration file format: -+ -[cols="1,1"] -|=== -| Configuration file format | Extension +[#configuration-factories] +=== Predefined ``ConfigurationFactory`` plugins -| XML -| `xml` +Log4j Core uses plugins extending from link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFactory.html[`ConfigurationFactory`] to determine which configuration file extensions are supported, in which order, and how to read them. +How this works under the hood and how you can introduce your custom implementations is explained in <>. -| JSON -| `json` or `jsn` +.Supported configuration file formats by predefined `ConfigurationFactory` plugins +[%header,cols="1,1m,1"] +|=== +| File format | Extension | Plugin order +| XML | xml | 5 +| JSON | json, jsn | 6 +| YAML | yaml, yml | 7 +| Properties | properties | 8 +|=== -| YAML -| `yaml` or `yml` +Some `ConfigurationFactory` plugins require additional dependencies on the classpath: -|Java properties -| `properties` -|=== +include::partial$configuration-file-format-deps.adoc[] + +[id=configuration-syntax] +== [[ConfigurationSyntax]] Syntax [NOTE] ==== -It is also possible to override the location of the configuration file using the -xref:manual/systemproperties.adoc#log4j2.configurationFile[`log4j2.configurationFile`] -configuration property. -In this case, Log4j will guess the configuration file format from the provided configuration file extension or use the default configuration factory if the extension is unknown. - -See xref:manual/systemproperties.adoc#log4j2.configurationFile[`log4j2.configurationFile`] for details. +Starting with Log4j 2, the configuration file syntax has been considered part of the public API and has remained stable across significant version upgrades. ==== -[id=configuration-syntax] -== [[ConfigurationSyntax]] Syntax +[WARNING] +==== +The syntax of the configuration file changed between Log4j{nbsp}1 and Log4j{nbsp}2. +Files in the Log4j{nbsp}1 syntax are ignored by default. +To enable partial support for old configuration syntax, see xref:manual/migration.adoc#ConfigurationCompatibility[configuration compatibility]. +==== The Log4j runtime is composed of xref:manual/plugins.adoc[plugins], which are like beans in the Spring Framework and Java EE. Appenders, layouts, filters, configuration loaders, and similar components are all accessed as plugins. -All configuration files are represented internally as a tree of -link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Node.html[`Node`]s, -which is translated into a tree of Log4j plugins. -The tree's root creates a -link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`] -object. +All configuration files are represented internally as a tree of link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Node.html[`Node`]s, which is translated into a tree of Log4j plugins. +The tree's root creates a link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`] object. A node is a relatively simple structure representing a single Log4j plugin (see xref:plugin-reference.adoc[] for a complete list), such as an appender, layout, or logger configuration. @@ -1233,3 +1224,39 @@ rootLogger.appenderRef.0.ref = APPENDER . All the keys of the form `logger..appenderRef.`, where `` and `` are arbitrary, are considered appender references. . To add a filter to a component use a `filter.` prefix instead of just ``. + +[#extending] +== Extending + +Log4j Core uses xref:manual/plugins.adoc[plugins] to inject necessary components while reading a configuration file. +In this section, we will explore extension points users can hook into to customize the way Log4j Core reads configuration files. + +[#extending-plugins] +=== Plugin preliminaries + +include::partial$manual/plugin-preliminaries.adoc[] + +[#ConfigurationFactory] +=== Extending `ConfigurationFactory` plugins + +Under the hood, Log4j Core uses plugins extending from link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFactory.html[`ConfigurationFactory`] to load configuration files. +This procedure can be summarized as follows: + +. Load plugins which extend from link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFactory.html[`ConfigurationFactory`] and whose plugin `category` is set to link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFactory.html#CATEGORY[`ConfigurationFactory.CATEGORY`] +. Sort them by link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Order.html[`@Order`] annotation, if present +. Feed <> to `ConfigurationFactory` instances in order, if the file extension is contained by `String[]` returned from link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFactory.html#getSupportedTypes()[`ConfigurationFactory#getSupportedTypes()`] + +If xref:manual/systemproperties.adoc#log4j2.configurationFactory[the `log4j2.configurationFactory` system property] is defined, it will be used before any other factory implementations. + +For an example, see {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfigurationFactory.java[`JsonConfigurationFactory.java`] on how Log4j Core implements JSON-formatted configuration file read. + +[TIP] +==== +Next to introducing new configuration file formats, `ConfigurationFactory` can be used for xref:manual/customconfig.adoc[programmatic configuration] too. +==== + +[#extending-config-file-plugins] +=== Plugins represented in a configuration file + +As explained in <>, a configuration file gets parsed into a tree of plugins. +If your plugin needs to be represented in a configuration file element, some xref:manual/plugins.adoc#core[extra plugin configuration] needs to be administered. diff --git a/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc b/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc index 0662519d88f..3e76fae2ac4 100644 --- a/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc @@ -14,20 +14,26 @@ See the License for the specific language governing permissions and limitations under the License. //// -= Programmatic Configuration += Programmatic configuration -Log4j 2 provides a few ways for applications to create their own -programmatic configuration: +Log4j provides a few ways for applications to configure Log4j Core programmatically: -* Specify a custom `ConfigurationFactory` to start Log4j with a -programmatic configuration -* Use the `Configurator` to replace the configuration after Log4j started -* Initialize Log4j with a combination of a configuration file and -programmatic configuration -* Modify the current `Configuration` after initialization +* Specify a custom <> to initialize Log4j Core with a programmatic configuration +* Use <> to replace the configuration after Log4j Core initialized +* Initialize Log4j Core with a <> +* <> after initialization + +[WARNING] +==== +It is a *bad practice to retrieve the active link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration/.html[`Configuration`], and manually add appenders*, loggers, etc. to it. +Instead, you should either create a new configuration or extend the existing one using <>, and use <> to reconfigure Log4j. +==== [#ConfigurationBuilder] -== The ConfigurationBuilder API +== The `ConfigurationBuilder` API + +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.html[`ConfigurationBuilder`] provides a fluent API to programmatically create link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration/.html[`Configuration`]s. +In essence, it allows you to build a xref:manual/configuration.adoc[configuration file] model in a type-safe way: Starting with release 2.4, Log4j provides a `ConfigurationBuilder` and a set of component builders that allow a `Configuration` to be created @@ -62,31 +68,6 @@ RollingFileAppender you would use builder.newComponent(). Examples of using the `ConfigurationBuilder` API are in the sections that follow. -[#ConfigurationFactory] -== Understanding ConfigurationFactory - -During initialization, Log4j 2 will search for available -xref:manual/extending.adoc#ConfigurationFactory[ConfigurationFactories] and -then select the one to use. The selected `ConfigurationFactory` creates -the `Configuration` that Log4j will use. Here is how Log4j finds the -available ConfigurationFactories: - -1. A system property named `log4j2.configurationFactory` can be set -with the name of the ConfigurationFactory to be used. -2. `ConfigurationFactory.setConfigurationFactory(ConfigurationFactory)` -can be called with the instance of the `ConfigurationFactory` to be used. -This must be called before any other calls to Log4j. -3. A `ConfigurationFactory` implementation can be added to the classpath -and configured as a plugin in the "ConfigurationFactory" category. The -`@Order` annotation can be used to specify the relative priority when -multiple applicable ConfigurationFactories are found. - -ConfigurationFactories have the concept of "supported types", which -basically maps to the file extension of the configuration file that the -ConfigurationFactory can handle. If a configuration file location is -specified, ConfigurationFactories whose supported type does not include -"*" or the matching file extension will not be used. - [#Example] == Initialize Log4j Using ConfigurationBuilder with a Custom ConfigurationFactory diff --git a/src/site/antora/modules/ROOT/partials/configuration-file-format-deps.adoc b/src/site/antora/modules/ROOT/partials/configuration-file-format-deps.adoc index 9b5e4c2ad51..4126c5e3f2c 100644 --- a/src/site/antora/modules/ROOT/partials/configuration-file-format-deps.adoc +++ b/src/site/antora/modules/ROOT/partials/configuration-file-format-deps.adoc @@ -23,11 +23,11 @@ Maven:: [tabs] ===== -log4j2.xml:: +XML:: + No dependency required. -log4j2.json:: +JSON:: + [source,xml,subs="+attributes"] ---- @@ -39,7 +39,7 @@ log4j2.json:: ---- -log4j2.yaml:: +YAML:: + [source,xml,subs="+attributes"] ---- @@ -51,7 +51,7 @@ log4j2.yaml:: ---- -log4j2.properties:: +Properties:: + No dependency required. @@ -62,27 +62,27 @@ Gradle:: [tabs] ===== -log4j2.xml:: +XML:: + No dependency required. -log4j2.json:: +JSON:: + [source,groovy,subs="+attributes"] ---- runtimeOnly 'com.fasterxml.jackson.core:jackson-databind:{jackson-version}' ---- -log4j2.yaml:: +YAML:: + [source,groovy,subs="+attributes"] ---- runtimeOnly 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:{jackson-version}' ---- -log4j2.properties:: +Properties:: + No dependency required. ===== -==== \ No newline at end of file +==== diff --git a/src/site/resources/.htaccess b/src/site/resources/.htaccess index f4aabfd3d08..9114cab475d 100644 --- a/src/site/resources/.htaccess +++ b/src/site/resources/.htaccess @@ -44,6 +44,7 @@ RewriteRule "^log4j-mongodb3(/index)?\.html$" "manual/appenders.html#NoSQLAppend RewriteRule "^log4j-mongodb4(/index)?\.html$" "manual/appenders.html#log4j-mongodb4" [R=permanent,NE] RewriteRule "^log4j-slf4j2?-impl(/index)?\.html$" "manual/installation.html#impl-core-bridge-slf4j" [R=permanent,NE] RewriteRule "^manual/api-separation\.html$" "manual/api.html" [R=permanent] +RewriteRule "^manual/customconfig\.html#ConfigurationFactory$" "manual/configuration.html#ConfigurationFactory" [R=permanent] RewriteRule "^manual/extending\.html#Appenders$" "manual/appenders.html#extending" [R=permanent] RewriteRule "^manual/extending\.html#Filters$" "manual/filters.html#extending" [R=permanent] RewriteRule "^manual/extending\.html#Layouts$" "manual/layouts.html#extending" [R=permanent] From dd7d8bc83c7b444a249146454bd42a63d6162b39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 1 Jul 2024 08:47:24 +0200 Subject: [PATCH 28/53] Rewrite programmatic configuration --- .../ConfigurationBuilder/log4j2.json | 18 + .../ConfigurationBuilder/log4j2.properties | 22 + .../ConfigurationBuilder/log4j2.xml | 36 ++ .../ConfigurationBuilder/log4j2.yaml | 28 ++ .../customconfig/ExampleConfiguration.java | 49 ++ .../ExampleConfigurationFactory.java | 42 ++ .../examples/manual/customconfig/Usage.java | 91 ++++ .../ROOT/pages/manual/configuration.adoc | 39 +- .../ROOT/pages/manual/customconfig.adoc | 466 +++++++----------- .../manual/composite-configuration.adoc | 50 ++ 10 files changed, 514 insertions(+), 327 deletions(-) create mode 100644 src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.json create mode 100644 src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.properties create mode 100644 src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.xml create mode 100644 src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.yaml create mode 100644 src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfiguration.java create mode 100644 src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfigurationFactory.java create mode 100644 src/site/antora/modules/ROOT/examples/manual/customconfig/Usage.java create mode 100644 src/site/antora/modules/ROOT/partials/manual/composite-configuration.adoc diff --git a/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.json b/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.json new file mode 100644 index 00000000000..5ad06b4cd60 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.json @@ -0,0 +1,18 @@ +{ + "Configuration": { + "Appenders": { + "Console": { + "name": "CONSOLE", + "JsonTemplateLayout": {} + } + }, + "Loggers": { + "Root": { + "level": "WARN", + "AppenderRef": { + "ref": "CONSOLE" + } + } + } + } +} diff --git a/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.properties b/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.properties new file mode 100644 index 00000000000..bd45ad2d7b2 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.properties @@ -0,0 +1,22 @@ +# +# 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. +# +appender.0.type = Console +appender.0.name = CONSOLE +appender.0.layout.type = JsonTemplateLayout + +rootLogger.level = WARN +rootLogger.appenderRef.0.ref = CONSOLE diff --git a/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.xml b/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.xml new file mode 100644 index 00000000000..3acc82eb214 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + diff --git a/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.yaml b/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.yaml new file mode 100644 index 00000000000..a46826acd48 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.yaml @@ -0,0 +1,28 @@ +# +# 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. +# +Configuration: + + Appenders: + Console: + name: "CONSOLE" + JsonTemplateLayout: {} + + Loggers: + Root: + level: "WARN" + AppenderRef: + ref: "CONSOLE" diff --git a/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfiguration.java b/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfiguration.java new file mode 100644 index 00000000000..9bd41ac8adf --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfiguration.java @@ -0,0 +1,49 @@ +/* + * 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. + */ +package com.example; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.AppenderRef; +import org.apache.logging.log4j.core.config.ConfigurationSource; +import org.apache.logging.log4j.core.config.LoggerConfig; + +public class ExampleConfiguration extends XmlConfiguration { + + public ExampleConfiguration(LoggerContext loggerContext, ConfigurationSource configSource) { + super(loggerContext, configSource); + } + + @Override + protected void doConfigure() { + Appender appender = createAppender("ExampleAppender"); + appender.start(); + addAppender(appender); + LoggerConfig loggerConfig = LoggerConfig + .newBuilder() + .withConfig(this) + .withAdditivity(false) + .withLevel(Level.INFO) + .withLoggerName("com.example") + .withRefs(new AppenderRef[]{AppenderRef.createAppenderRef("ExampleAppender", null, null)}) + .build(); + loggerConfig.addAppender(appender, null, null); + addLogger("com.example", loggerConfig); + } + +} diff --git a/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfigurationFactory.java b/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfigurationFactory.java new file mode 100644 index 00000000000..ce9e1b46794 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfigurationFactory.java @@ -0,0 +1,42 @@ +/* + * 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. + */ +package com.example; + +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.ConfigurationFactory; +import org.apache.logging.log4j.core.config.ConfigurationSource; +import org.apache.logging.log4j.core.config.Order; +import org.apache.logging.log4j.core.config.plugins.Plugin; + +// tag::class[] +@Order(5) +@Plugin(name = "ExampleConfigurationFactory", category = ConfigurationFactory.CATEGORY) +public class ExampleConfigurationFactory extends ConfigurationFactory { + + @Override + public Configuration getConfiguration(LoggerContext loggerContext, ConfigurationSource source) { + // Return a `Configuration`... + } + + @Override + public String[] getSupportedTypes() { + return new String[] {"*"}; + } + +} +// end::class[] diff --git a/src/site/antora/modules/ROOT/examples/manual/customconfig/Usage.java b/src/site/antora/modules/ROOT/examples/manual/customconfig/Usage.java new file mode 100644 index 00000000000..4b88e562366 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customconfig/Usage.java @@ -0,0 +1,91 @@ +/* + * 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. + */ +package com.example; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.config.AbstractConfiguration; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.ConfigurationFactory; +import org.apache.logging.log4j.core.config.ConfigurationSource; +import org.apache.logging.log4j.core.config.Configurator; +import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; +import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; +import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; +import org.apache.logging.log4j.core.config.composite.CompositeConfiguration; +import org.apache.logging.log4j.core.LoggerContext; + +import java.net.URI; +import java.util.Arrays; + +final class Usage { + + private static Configuration createConfiguration() { + // tag::createConfiguration[] + ConfigurationBuilder configBuilder = + ConfigurationBuilderFactory.newConfigurationBuilder(); //<1> + Configuration configuration = configBuilder + .add(configBuilder //<2> + .newAppender("CONSOLE", "List") + .add(configBuilder.newLayout("JsonTemplateLayout"))) + .add(configBuilder //<3> + .newRootLogger(Level.WARN) + .add(configBuilder.newAppenderRef("CONSOLE"))) + .build(false); //<4> + // end::createConfiguration[] + } + + private static void useConfiguration() { + // tag::useConfiguration[] + Configuration configuration = createConfiguration(); + try (LoggerContext loggerContext = Configurator.initialize(configuration)) { + // Use `LoggerContext`... + } + // end::useConfiguration[] + } + + private static void reconfigureActiveLoggerContext() { + // tag::reconfigureActiveLoggerContext[] + Configuration configuration = createConfiguration(); + Configurator.reconfigure(configuration); + // end::reconfigureActiveLoggerContext[] + } + + private static Configuration loadConfigurationFile() { + // tag::loadConfigurationFile[] + ConfigurationFactory.getInstance().getConfiguration( + null, //<1> + null, //<2> + URI.create("uri://to/my/log4j2.xml")); //<3> + // end::loadConfigurationFile[] + } + + private static Configuration combineConfigurations() { + // tag::combineConfigurations[] + ConfigurationFactory configFactory = ConfigurationFactory.getInstance(); + AbstractConfiguration commonConfig = (AbstractConfiguration) //<2> + configFactory.getConfiguration(null, null, URI.create("classpath:log4j2-common.xml")); //<1> + AbstractConfiguration appConfig = (AbstractConfiguration) //<2> + configFactory.getConfiguration(null, null, URI.create("classpath:log4j2-app.xml")); //<1> + AbstractConfiguration runtimeConfig = ConfigurationBuilderFactory + .newConfigurationBuilder() + // ... + .build(false); //<3> + return new CompositeConfiguration(Arrays.asList(commonConfig, appConfig, runtimeConfig)); //<4> + // end::combineConfigurations[] + } + +} diff --git a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc index 34ab7d000f5..0433b9a3954 100644 --- a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc @@ -1067,40 +1067,17 @@ include::example$manual/configuration/arbiters-select.properties[tag=select] <2> Otherwise, a JSON Template Layout will be used. [#CompositeConfiguration] -== Composite Configuration +== Composite configuration -Log4j allows multiple configuration files to be used at the same time by specifying them as a list of comma-separated file paths or URLs in the -xref:manual/systemproperties.adoc#log4j2.configurationFile[`log4j2.configurationFile`] -configuration property. +include::partial$manual/composite-configuration.adoc[tag=intro] -These configuration files are merged into a single configuration file using -link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/composite/MergeStrategy.html[`MergeStrategy`] -service that can be customized using the -xref:manual/systemproperties.adoc#log4j2.mergeStrategy[`log4j2.mergeStrategy`] -configuration property. +You can provide a list of comma-separated file paths or URLs in xref:manual/systemproperties.adoc#log4j2.configurationFile[the `log4j2.configurationFile` configuration property], where each resource will get read into a `Configuration`, and then eventually combined into a single one using link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/composite/CompositeConfiguration.html[`CompositeConfiguration`]. -The default merge strategy will merge the files using the following rules: - -. <> in later configurations replace those in previous configurations. -+ -The exception is the `monitorInterval` attribute: the lowest positive value from all the configuration files will be used. - -. <> from all configurations are aggregated. -Duplicate properties replace those in previous configurations. - -. xref:manual/filters.adoc[Filters] are aggregated under -xref:manual/filters.adoc#CompositeFilter[CompositeFilter], if more than one filter is defined. - -. xref:manual/scripts.adoc[] are aggregated. -Duplicate definitions replace those in previous configurations. - -. xref:manual/appenders.adoc[Appenders] are aggregated. -Appenders with the same name are **replaced** by those in later configurations, including all their elements. - -. <> are all aggregated. -Logger attributes are individually merged, and those in later configurations replace duplicates. -Appender references on a logger are aggregated, and those in later configurations replace duplicates. -The strategy merges filters on loggers using the rule above. +.How does `CompositeConfiguration` work? +[%collapsible] +==== +include::partial$manual/composite-configuration.adoc[tag=how] +==== [id=format-specific-notes] == Format specific notes diff --git a/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc b/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc index 3e76fae2ac4..fae7af0698d 100644 --- a/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc @@ -16,351 +16,225 @@ //// = Programmatic configuration -Log4j provides a few ways for applications to configure Log4j Core programmatically: +Next to xref:manual/configuration.adoc[configuration files], Log4j Core can be configured programmatically too. +In this page, we will explore utilities helping with programmatic configuration and demonstrate how they can be leveraged for certain use cases. -* Specify a custom <> to initialize Log4j Core with a programmatic configuration -* Use <> to replace the configuration after Log4j Core initialized -* Initialize Log4j Core with a <> -* <> after initialization +[#prelim] +== Preliminaries -[WARNING] +To begin with, we strongly encourage you to check out the xref:manual/architecture.adoc[] page first. +Let's repeat some basic definitions of particular interest: + +xref:manual/architecture.adoc#LoggerContext[`LoggerContext`]:: +It is the anchor of the logging system. +Generally there is one, statically-accessible, global `LoggerContext` for most applications. +But there can be multiple ``LoggerContext``s, for instance, to use in tests, in Java EE web applications, etc. + +xref:manual/architecture.adoc#Configuration[`Configuration`]:: +It encapsulates a Log4j Core configuration (properties, appenders, loggers, etc.) and is associated with a `LoggerContext`. + +[#tooling] +== Tooling + +For programmatic configuration, Log4j Core essentially provides the following tooling: + +<>:: for declaratively creating a `Configuration` + +<>:: for associating a `Configuration` with a `LoggerContext` + +<>:: for registering a `Configuration` factory to xref:manual/configuration.adoc[the configuration file mechanism] + +In short, we will create ``Configuration``s using `ConfigurationBuilder`, and activate them using `Configurator`. + +[#ConfigurationBuilder] +=== `ConfigurationBuilder` + +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.html[`ConfigurationBuilder`] interface models a fluent API to programmatically create link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`]s. +If you have ever created a xref:manual/configuration.adoc[Log4j Core configuration file], consider `ConfigurationBuilder` as a convenience utility to model the very same declarative configuration structure programmatically, in a type-safe way. + +Let's show `ConfigurationBuilder` usage with an example. +Consider the following Log4j Core configuration file: + +[tabs] ==== -It is a *bad practice to retrieve the active link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration/.html[`Configuration`], and manually add appenders*, loggers, etc. to it. -Instead, you should either create a new configuration or extend the existing one using <>, and use <> to reconfigure Log4j. +XML:: ++ +.Snippet from an example {antora-examples-url}/manual/customconfig/ConfigurationBuilder/log4j2.xml[`log4j2.xml`] +[source,xml] +---- +include::example$manual/customconfig/ConfigurationBuilder/log4j2.xml[lines=24..34,indent=0] +---- + +JSON:: ++ +.Snippet from an example {antora-examples-url}/manual/customconfig/ConfigurationBuilder/log4j2.json[`log4j2.json`] +[source,json] +---- +include::example$manual/customconfig/ConfigurationBuilder/log4j2.json[lines=3..16,indent=0] +---- + +YAML:: ++ +.Snippet from an example {antora-examples-url}/manual/customconfig/ConfigurationBuilder/log4j2.yaml[`log4j2.yaml`] +[source,yaml] +---- +include::example$manual/customconfig/ConfigurationBuilder/log4j2.yaml[lines=19..-1,indent=0] +---- + +Properties:: ++ +.Snippet from an example {antora-examples-url}/manual/customconfig/ConfigurationBuilder/log4j2.properties[`log4j2.properties`] +[source,properties] +---- +include::example$manual/customconfig/ConfigurationBuilder/log4j2.properties[lines=17..-1] +---- ==== -[#ConfigurationBuilder] -== The `ConfigurationBuilder` API - -link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.html[`ConfigurationBuilder`] provides a fluent API to programmatically create link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration/.html[`Configuration`]s. -In essence, it allows you to build a xref:manual/configuration.adoc[configuration file] model in a type-safe way: - -Starting with release 2.4, Log4j provides a `ConfigurationBuilder` and a -set of component builders that allow a `Configuration` to be created -fairly easily. Actual configuration objects like `LoggerConfig` or -`Appender` can be unwieldy; they require a lot of knowledge about Log4j -internals which makes them difficult to work with if all you want is to -create a `Configuration`. - -The new `ConfigurationBuilder` API (in the -`org.apache.logging.log4j.core.config.builder.api` package) allows users -to create Configurations in code by constructing component -_definitions_. There is no need to work directly with actual -configuration objects. Component definitions are added to the -`ConfigurationBuilder`, and once all the definitions have been collected -all the actual configuration objects (like Loggers and Appenders) are -constructed. - -`ConfigurationBuilder` has convenience methods for the base components -that can be configured such as Loggers, Appenders, Filter, Properties, -etc. However, Log4j 2's plugin mechanism means that users can create any -number of custom components. As a trade-off, the `ConfigurationBuilder` -API provides only a limited number of "strongly typed" convenience -methods like `newLogger()`, `newLayout()` etc. The generic -`builder.newComponent()` method can be used if no convenience method -exists for the component you want to configure. - -For example, the builder does not know what sub-components can be -configured on specific components such as the RollingFileAppender vs. -the RoutingAppender. To specify a triggering policy on a -RollingFileAppender you would use builder.newComponent(). - -Examples of using the `ConfigurationBuilder` API are in the sections that -follow. - -[#Example] -== Initialize Log4j Using ConfigurationBuilder with a Custom ConfigurationFactory - -One way to programmatically configure Log4j 2 is to create a custom -`ConfigurationFactory` that uses the -<> to create a -Configuration. The below example overrides the `getConfiguration()` -method to return a `Configuration` created by the `ConfigurationBuilder`. -This will cause the `Configuration` to automatically be hooked into Log4j -when the `LoggerContext` is created. In the example below, because it -specifies a supported type of "*" it will override any configuration -files provided. +Above Log4j Core configuration can be programmatically built using `ConfigurationBuilder` as follows: +.Snippet from an example {antora-examples-url}/manual/customconfig/Usage.java[`Usage.java`] [source,java] ---- -@Namespace(ConfigurationFactory.NAMESPACE) -@Plugin -@Order(50) -public class CustomConfigurationFactory extends ConfigurationFactory { - - static Configuration createConfiguration(final String name, ConfigurationBuilder builder) { - builder.setConfigurationName(name); - builder.setStatusLevel(Level.ERROR); - builder.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.NEUTRAL). - addAttribute("level", Level.DEBUG)); - AppenderComponentBuilder appenderBuilder = builder.newAppender("Stdout", "CONSOLE"). - addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT); - appenderBuilder.add(builder.newLayout("PatternLayout"). - addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable")); - appenderBuilder.add(builder.newFilter("MarkerFilter", Filter.Result.DENY, - Filter.Result.NEUTRAL).addAttribute("marker", "FLOW")); - builder.add(appenderBuilder); - builder.add(builder.newLogger("org.apache.logging.log4j", Level.DEBUG). - add(builder.newAppenderRef("Stdout")). - addAttribute("additivity", false)); - builder.add(builder.newRootLogger(Level.ERROR).add(builder.newAppenderRef("Stdout"))); - return builder.build(); - } - - @Override - public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) { - return getConfiguration(loggerContext, source.toString(), null); - } - - @Override - public Configuration getConfiguration(final LoggerContext loggerContext, final String name, final URI configLocation) { - ConfigurationBuilder builder = newConfigurationBuilder(); - return createConfiguration(name, builder); - } - - @Override - protected String[] getSupportedTypes() { - return new String[] {"*"}; - } -} +include::example$manual/customconfig/Usage.java[tag=createConfiguration,indent=0] ---- +<1> The default `ConfigurationBuilder` instance is obtained using link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilderFactory.html#newConfigurationBuilder()[`ConfigurationBuilderFactory.newConfigurationBuilder()`] static method +<2> Add the appender along with the layout +<3> Add the root logger along with a level and appender reference +<4> Create the configuration, but *don't initialize* it ++ +[TIP] +==== +It is a good practice to not initialize ``Configuration``s when they are constructed. +This task should ideally be delegated to <>. +==== -As of version 2.7, the `ConfigurationFactory.getConfiguration()` methods -take an additional `LoggerContext` parameter. +`ConfigurationBuilder` has convenience methods for the base components that can be configured such as loggers, appenders, filters, properties, etc. +Though there are cases where the provided convenience methods fall short of: + +* Custom xref:manual/plugins.adoc#core[plugins that are declared to be represented in a configuration] +* Custom subcomponents (e.g., a xref:manual/appenders.adoc#TriggeringPolicies[triggering policy] for xref:manual/appenders.adoc#RollingFileAppender[`RollingFileAppender`]) + +For those, you can use the generic link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.html#newComponent()[`ConfigurationBuilder#newComponent()`] method. + +See {project-github-url}/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/Configurator1Test.java[`Configurator1Test.java`] for examples on `ConfigurationBuilder`, `newComponent()`, etc. usage. [#Configurator] -== Reconfigure Log4j Using ConfigurationBuilder with the Configurator +=== `Configurator` -An alternative to a custom `ConfigurationFactory` is to configure with the -`Configurator`. Once a `Configuration` object has been constructed, it can -be passed to one of the `Configurator.initialize` methods to set up the -Log4j configuration. +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configurator.html[`Configurator`] is a programmatic interface to associate a ``Configuration`` with either new, or an existing `LoggerContext`. -Using the `Configurator` in this manner allows the application control -over when Log4j is initialized. However, should any logging be attempted -before `Configurator.initialize()` is called then the default -configuration will be used for those log events. +[#Configurator-initialize] +==== Creating a new `LoggerContext` +You can use `Configurator` to create a new `LoggerContext` as follows: + +.Snippet from an example {antora-examples-url}/manual/customconfig/Usage.java[`Usage.java`] [source,java] ---- -ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); -builder.setStatusLevel(Level.ERROR); -builder.setConfigurationName("BuilderTest"); -builder.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.NEUTRAL) - .addAttribute("level", Level.DEBUG)); -AppenderComponentBuilder appenderBuilder = builder.newAppender("Stdout", "CONSOLE").addAttribute("target", - ConsoleAppender.Target.SYSTEM_OUT); -appenderBuilder.add(builder.newLayout("PatternLayout") - .addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable")); -appenderBuilder.add(builder.newFilter("MarkerFilter", Filter.Result.DENY, Filter.Result.NEUTRAL) - .addAttribute("marker", "FLOW")); -builder.add(appenderBuilder); -builder.add(builder.newLogger("org.apache.logging.log4j", Level.DEBUG) - .add(builder.newAppenderRef("Stdout")).addAttribute("additivity", false)); -builder.add(builder.newRootLogger(Level.ERROR).add(builder.newAppenderRef("Stdout"))); -ctx = Configurator.initialize(builder.build()); +include::example$manual/customconfig/Usage.java[tag=useConfiguration,indent=0] ---- -This example shows how to create a configuration that includes a -RollingFileAppender. +This is a convenient way to create single-use, isolated ``LoggerContext``s for tests, etc. + +[#Configurator-reconfigure] +==== Reconfiguring the active `LoggerContext` +You can use `Configurator` to reconfigure the active `LoggerContext` as follows: + +.Snippet from an example {antora-examples-url}/manual/customconfig/Usage.java[`Usage.java`] [source,java] ---- -ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); - -builder.setStatusLevel(Level.ERROR); -builder.setConfigurationName("RollingBuilder"); -// create a console appender -AppenderComponentBuilder appenderBuilder = builder.newAppender("Stdout", "CONSOLE").addAttribute("target", - ConsoleAppender.Target.SYSTEM_OUT); -appenderBuilder.add(builder.newLayout("PatternLayout") - .addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable")); -builder.add(appenderBuilder); -// create a rolling file appender -LayoutComponentBuilder layoutBuilder = builder.newLayout("PatternLayout") - .addAttribute("pattern", "%d [%t] %-5level: %msg%n"); -ComponentBuilder triggeringPolicy = builder.newComponent("Policies") - .addComponent(builder.newComponent("CronTriggeringPolicy").addAttribute("schedule", "0 0 0 * * ?")) - .addComponent(builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", "100M")); -appenderBuilder = builder.newAppender("rolling", "RollingFile") - .addAttribute("fileName", "target/rolling.log") - .addAttribute("filePattern", "target/archive/rolling-%d{MM-dd-yy}.log.gz") - .add(layoutBuilder) - .addComponent(triggeringPolicy); -builder.add(appenderBuilder); - -// create the new logger -builder.add(builder.newLogger("TestLogger", Level.DEBUG) - .add(builder.newAppenderRef("rolling")) - .addAttribute("additivity", false)); - -builder.add(builder.newRootLogger(Level.DEBUG) - .add(builder.newAppenderRef("rolling"))); -LoggerContext ctx = Configurator.initialize(builder.build()); +include::example$manual/customconfig/Usage.java[tag=reconfigureActiveLoggerContext,indent=0] ---- -[#Hybrid] -== Initialize Log4j by Combining Configuration File with Programmatic Configuration +Using the `Configurator` in this manner allows the application control over when Log4j is initialized. +However, should any logging be attempted before `Configurator.initialize()` is called then the default configuration will be used for those log events. + +[#ConfigurationFactory] +=== [[Example]] `ConfigurationFactory` -Sometimes you want to configure with a configuration file but do some -additional programmatic configuration. A possible use case might be that -you want to allow for a flexible configuration using XML but at the same -time make sure there are a few configuration elements that are always -present that can't be removed. +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFactory.html[`ConfigurationFactory`] interface, which is mainly used by xref:manual/configuration.adoc#automatic-configuration[the configuration file mechanism] to load a link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`], can be leveraged to inject a custom `Configuration`. +You need to -The easiest way to achieve this is to extend one of the standard -`Configuration` classes (`XMLConfiguration`, `JSONConfiguration`) and then -create a new `ConfigurationFactory` for the extended class. After the -standard configuration completes the custom configuration can be added -to it. +* xref:manual/configuration.adoc#ConfigurationFactory[Create a custom `ConfigurationFactory` plugin] +* Assign it a higher priority (i.e., lower `@Order` value) +* Support all configuration file types (i.e. return `*` from `getSupportedTypes()`) -The example below shows how to extend `XMLConfiguration` to manually add -an `Appender` and a `LoggerConfig` to the configuration. +Consider the example below: +.Snippet from an example {antora-examples-url}/manual/customconfig/ExampleConfigurationFactory.java[`ExampleConfigurationFactory.java`] [source,java] ---- -@Namespace("ConfigurationFactory") -@Plugin("MyXMLConfigurationFactory") -@Order(10) -public class MyXMLConfigurationFactory extends ConfigurationFactory { - - /** - * Valid file extensions for XML files. - */ - public static final String[] SUFFIXES = new String[] {".xml", "*"}; - - /** - * Return the Configuration. - * @param source The InputSource. - * @return The Configuration. - */ - public Configuration getConfiguration(InputSource source) { - return new MyXMLConfiguration(source, configFile); - } - - /** - * Returns the file suffixes for XML files. - * @return An array of File extensions. - */ - public String[] getSupportedTypes() { - return SUFFIXES; - } -} - -public class MyXMLConfiguration extends XMLConfiguration { - public MyXMLConfiguration(final ConfigurationFactory.ConfigurationSource configSource) { - super(configSource); - } - - @Override - protected void doConfigure() { - super.doConfigure(); - final LoggerContext ctx = (LoggerContext) LogManager.getContext(false); - final Layout layout = PatternLayout.createDefaultLayout(config); - final Appender appender = FileAppender.createAppender("target/test.log", "false", "false", "File", "true", - "false", "false", "4000", layout, null, "false", null, config); - appender.start(); - addAppender(appender); - LoggerConfig loggerConfig = LoggerConfig.createLogger("false", "info", "org.apache.logging.log4j", - "true", refs, null, config, null ); - loggerConfig.addAppender(appender, null, null); - addLogger("org.apache.logging.log4j", loggerConfig); - } -} +include::example$manual/customconfig/ExampleConfigurationFactory.java[tag=class] ---- -[#AddingToCurrent] -== Programmatically Modifying the Current Configuration after Initialization +[#guides] +== How-to guides -Applications sometimes have the need to customize logging separate from -the actual configuration. Log4j allows this although it suffers from a -few limitations: +In this section we will share guides on programmatically configuring Log4j Core for certain use cases. -1. If the configuration file is changed the configuration will be -reloaded and the manual changes will be lost. -2. Modification to the running configuration requires that all the -methods being called (addAppender and addLogger) be synchronized. +[#load-config-file] +=== Loading a configuration file -As such, the recommended approach for customizing a configuration is to -extend one of the standard Configuration classes, override the setup -method to first do super.setup() and then add the custom Appenders, -Filters and LoggerConfigs to the configuration before it is registered -for use. +<> provides the link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFactory.html#getInstance()[`getInstance()`] method returning a meta-`ConfigurationFactory` that combines the behaviour of all available ``ConfigurationFactory`` implementations, including xref:manual/configuration.adoc#configuration-factories[the predefined ones]; `XmlConfigurationFactory`, `JsonConfigurationFactory`, etc. +You can use this `getInstance()` method to load a configuration file programmatically, granted that the input file format is supported by at least one of the available `ConfigurationFactory` plugins: -The following example adds an Appender and a new LoggerConfig using that -Appender to the current configuration. - -//TODO: update code example below with new plugin API +.Snippet from an example {antora-examples-url}/manual/customconfig/Usage.java[`Usage.java`] [source,java] ---- - final LoggerContext ctx = (LoggerContext) LogManager.getContext(false); - final Configuration config = ctx.getConfiguration(); - Layout layout = PatternLayout.createDefaultLayout(config); - Appender appender = FileAppender.createAppender("target/test.log", "false", "false", "File", "true", - "false", "false", "4000", layout, null, "false", null, config); - appender.start(); - config.addAppender(appender); - AppenderRef ref = AppenderRef.createAppenderRef("File", null, null); - AppenderRef[] refs = new AppenderRef[] {ref}; - LoggerConfig loggerConfig = LoggerConfig.createLogger("false", "info", "org.apache.logging.log4j", - "true", refs, null, config, null ); - loggerConfig.addAppender(appender, null, null); - config.addLogger("org.apache.logging.log4j", loggerConfig); - ctx.updateLoggers(); -} +include::example$manual/customconfig/Usage.java[tag=loadConfigurationFile,indent=0] ---- +<1> Passing the optional `LoggerContext` argument as null, since this `Configuration` is not associated with a `LoggerContext` yet +<2> Passing the optional configuration name argument as null, it will default to the configuration source location +<3> URI pointing to the configuration file; `file://path/to/log4j2.xml`, `classpath:log4j2.xml`, etc. -[#AppendingToWritersAndOutputStreams] -== Appending Log Events to Writers and OutputStreams Programmatically +[#CompositeConfiguration] +=== Combining multiple configurations -Log4j 2.5 provides facilities to append log events to Writers and -OutputStreams. For example, this provides simple integration for JDBC -Driver implementors that use Log4j internally and still want to support -the JDBC APIs `CommonDataSource.setLogWriter(PrintWriter)`, -`java.sql.DriverManager.setLogWriter(PrintWriter)`, and -`java.sql.DriverManager.setLogStream(PrintStream)`. +include::partial$manual/composite-configuration.adoc[tag=intro] -Given any `Writer`, like a `PrintWriter`, you tell Log4j to append -events to that writer by creating a `WriterAppender` and updating the -Log4j configuration: +You can programmatically combine multiple configurations into a single one using link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/composite/CompositeConfiguration.html[`CompositeConfiguration`]: +.Snippet from an example {antora-examples-url}/manual/customconfig/Usage.java[`Usage.java`] [source,java] ---- -void addAppender(final Writer writer, final String writerName) { - final LoggerContext context = LoggerContext.getContext(false); - final Configuration config = context.getConfiguration(); - final PatternLayout layout = PatternLayout.createDefaultLayout(config); - final Appender appender = WriterAppender.createAppender(layout, null, writer, writerName, false, true); - appender.start(); - config.addAppender(appender); - updateLoggers(appender, config); -} - -private void updateLoggers(final Appender appender, final Configuration config) { - final Level level = null; - final Filter filter = null; - for (final LoggerConfig loggerConfig : config.getLoggers().values()) { - loggerConfig.addAppender(appender, level, filter); - } - config.getRootLogger().addAppender(appender, level, filter); -} +include::example$manual/customconfig/Usage.java[tag=combineConfigurations,indent=0] ---- +<1> Loading a common, and an application-specific configuration from file +<2> Casting them to `AbstractConfiguration`, the type required by `CompositeConfiguration` +<3> Programmatically creating an uninitialized configuration. +Note that no casting is needed. +<4> Creating a `CompositeConfiguration` using all three configurations created. +Note that passed configuration order matters! + +.How does `CompositeConfiguration` work? +[%collapsible] +==== +include::partial$manual/composite-configuration.adoc[tag=how] +==== + +[#Hybrid] +=== [[AddingToCurrent]] [[AppendingToWritersAndOutputStreams]] Modifying configuration components -You can achieve the same effect with an `OutputStream`, like a -`PrintStream`: +It is unfortunately common that users modify components (appenders, filters, etc.) of a configuration programmatically as follows: [source,java] ---- -void addAppender(final OutputStream outputStream, final String outputStreamName) { - final LoggerContext context = LoggerContext.getContext(false); - final Configuration config = context.getConfiguration(); - final PatternLayout layout = PatternLayout.createDefaultLayout(config); - final Appender appender = OutputStreamAppender.createAppender(layout, null, outputStream, outputStreamName, false, true); - appender.start(); - config.addAppender(appender); - updateLoggers(appender, config); -} +LoggerContext context = LoggerContext.getContext(false); +Configuration config = context.getConfiguration(); +PatternLayout layout = PatternLayout.createDefaultLayout(config); +Appender appender = createCustomAppender(); +appender.start(); +config.addAppender(appender); +updateLoggers(appender, config); ---- -The difference is the use of `OutputStreamAppender` instead of -`WriterAppender`. +*We strongly advise you to avoid programmatically modifying components of a configuration!* +This approach is prone several problems: + +* Your code relies on Log4j Core internals which don't have any backward compatibility guarantees. +You not only risk breaking your build at a minor Log4j Core version upgrade, but also make the life of Log4j maintainers trying to evolve the project extremely difficult. +* You move out from the safety zone, where Log4j Core takes care of components' life cycle (initialization, reconfiguration, etc.), and step into a minefield seriously undermining the reliability of your logging setup. + +If you happen to have code programmatically modifying components of a configuration, we advise you to migrate to other declarative approaches shared in this page. +In case of need, feel free to ask for help in {logging-services-url}/support.html#discussions-user[user support channels]. diff --git a/src/site/antora/modules/ROOT/partials/manual/composite-configuration.adoc b/src/site/antora/modules/ROOT/partials/manual/composite-configuration.adoc new file mode 100644 index 00000000000..89c0ba83d22 --- /dev/null +++ b/src/site/antora/modules/ROOT/partials/manual/composite-configuration.adoc @@ -0,0 +1,50 @@ +//// + 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. +//// + +// tag::intro[] +There are occasions where multiple configurations might need to be combined. +For instance, + +* You have a common Log4j Core configuration that should always be present, and an environment-specific one that extends the common one depending on the environment (test, production, etc.) the application is running on. +* You develop a framework, and it contains a predefined Log4j Core configuration. +Yet you want to allow users to extend it whenever necessary. +* You collect Log4j Core configurations from multiple sources. +// end::intro[] + +// tag::how[] +`CompositeConfiguration` merges multiple configurations into a single one using a link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/composite/MergeStrategy.html[`MergeStrategy`], which can be customized using xref:manual/systemproperties.adoc#log4j2.mergeStrategy[the `log4j2.mergeStrategy` configuration property]. +The default merge strategy works as follows: + +* xref:manual/configuration.adoc#global-configuration-attributes[Global configuration attributes] in later configurations replace those in previous configurations. +The only exception is the `monitorInterval` attribute: the lowest positive value from all the configuration files will be used. + +* xref:manual/configuration.adoc#property-substitution[Properties] are aggregated. +Duplicate properties override those in previous configurations. + +* xref:manual/filters.adoc[Filters] are aggregated under xref:manual/filters.adoc#CompositeFilter[`CompositeFilter`], if more than one filter is defined. + +* xref:manual/scripts.adoc[] are aggregated. +Duplicate definitions override those in previous configurations. + +* xref:manual/appenders.adoc[Appenders] are aggregated. +Appenders with the same name are **overridden** by those in later configurations, including all their elements. + +* xref:manual/configuration.adoc#configuring-loggers[Loggers] are aggregated. +Logger attributes are individually merged, and those in later configurations replace duplicates. +Appender references on a logger are aggregated, and those in later configurations replace duplicates. +The strategy merges filters on loggers using the rule above. +// end::how[] From f8a5534496bef6de7c01e30514fe589825b31605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 1 Jul 2024 09:06:30 +0200 Subject: [PATCH 29/53] Place `Reconfiguration reliability` under `Configuration` --- .../ROOT/pages/manual/architecture.adoc | 113 ++++++++---------- 1 file changed, 52 insertions(+), 61 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/architecture.adoc b/src/site/antora/modules/ROOT/pages/manual/architecture.adoc index 3c9b032d251..b97b63502bc 100644 --- a/src/site/antora/modules/ROOT/pages/manual/architecture.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/architecture.adoc @@ -167,70 +167,13 @@ At a really high level, * A <>, the composition anchor, gets created in combination with a <>. Both can be created either directly (i.e., programmatically) or indirectly at first interaction with Log4j. * `LoggerContext` creates <>s that users interact with for logging purposes. -* <> delivers a -link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html[`LogEvent`] -to a target (file, socket, database, etc.) and typically uses a <> to encode log events and an -link:../javadoc/log4j-core/org/apache/logging/log4j/core/appender/AbstractManager.html[`AbstractManager`] -to handle the lifecycle of the target resource. +* <> delivers a link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html[`LogEvent`] to a target (file, socket, database, etc.) and typically uses a <> to encode log events and an <> to handle the lifecycle of the target resource. * <> encapsulates configuration for a `Logger`, as `AppenderControl` and `AppenderRef` for ``Appender``s. * <> is equipped with <> to allow property substitution in `String`-typed values. * A typical `log()` call triggers a chain of invocations through classes `Logger`, `LoggerConfig`, `AppenderControl`, `Appender`, and `AbstractManager` in order – this is depicted using green arrows in xref:architecture-diagram[xrefstyle=short]. Following sections examine this interplay in detail. -[#reconfiguration] -== Reconfiguration reliability - -The main motivation for such an architecture is reliability to configuration changes. -When a reconfiguration event occurs, two `Configuration` instances are active at the same time. -Threads that already started processing a log event will either: - -* continue logging to the old configuration, if execution already reached the `LoggerConfig` class, -* or switch to the new configuration. - -The service that manages the reconfiguration process is called -link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ReliabilityStrategy.html[`ReliabilityStrategy`] -and it decides: - -* when should ``Logger``s switch to the new configuration, -* when should the old configuration be stopped. - -.Overview of the reconfiguration process -[plantuml] -.... -@startuml -left to right direction - -package LoggerContext { - object Logger - - package "New Configuration" as c2 { - object "LoggerConfig" as lc2 - object "AppenderControl" as ac2 - object "Appender" as app2 - } - - package "Old Configuration" as c1 { - object "LoggerConfig" as lc1 - object "AppenderControl" as ac1 - object "Appender" as app1 - } -} - -object AbstractManager - -Logger ..> lc1 -lc1 --> ac1 -ac1 --> app1 -app1 --> AbstractManager - -Logger --> lc2 -lc2 --> ac2 -ac2 --> app2 -app2 --> AbstractManager -@enduml -.... - [#LoggerContext] == `LoggerContext` @@ -332,13 +275,61 @@ class StrSubstitutor { @enduml .... -During reconfiguration, two `Configuration` instances will be present. -Once all ``Logger``s have been redirected to the new `Configuration`, the old one will be stopped and discarded. - Configuration of Log4j Core is typically done at application initialization. The preferred way is by reading a xref:manual/configuration.adoc[configuration file], but it can also be done xref:manual/customconfig.adoc[programmatically]. This is further discussed in xref:manual/config-intro.adoc[]. +[#reconfiguration] +=== Reconfiguration reliability + +The main motivation for the existing architecture is the reliability to configuration changes. +When a reconfiguration event occurs, two `Configuration` instances are active at the same time. +Threads that already started processing a log event will either: + +* continue logging to the old configuration, if execution already reached the `LoggerConfig` class, +* or switch to the new configuration. + +The service that manages the reconfiguration process is called link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ReliabilityStrategy.html[`ReliabilityStrategy`] and it decides: + +* when should ``Logger``s switch to the new configuration, +* when should the old configuration be stopped. + +.Overview of the reconfiguration process +[plantuml] +.... +@startuml +left to right direction + +package LoggerContext { + object Logger + + package "New Configuration" as c2 { + object "LoggerConfig" as lc2 + object "AppenderControl" as ac2 + object "Appender" as app2 + } + + package "Old Configuration" as c1 { + object "LoggerConfig" as lc1 + object "AppenderControl" as ac1 + object "Appender" as app1 + } +} + +object AbstractManager + +Logger ..> lc1 +lc1 --> ac1 +ac1 --> app1 +app1 --> AbstractManager + +Logger --> lc2 +lc2 --> ac2 +ac2 --> app2 +app2 --> AbstractManager +@enduml +.... + [#Logger] == `Logger` From 0a09e700ed95b9839e4f1112bafcb94c69fc643f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 1 Jul 2024 09:10:21 +0200 Subject: [PATCH 30/53] Advise against programmatically modifying components --- .../antora/modules/ROOT/pages/manual/customconfig.adoc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc b/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc index fae7af0698d..25a89d93f9f 100644 --- a/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc @@ -216,6 +216,12 @@ include::partial$manual/composite-configuration.adoc[tag=how] [#Hybrid] === [[AddingToCurrent]] [[AppendingToWritersAndOutputStreams]] Modifying configuration components +[WARNING] +==== +*We strongly advise against programmatically modifying components of a configuration!* +This section will explain what it is, and why you should avoid it. +==== + It is unfortunately common that users modify components (appenders, filters, etc.) of a configuration programmatically as follows: [source,java] @@ -229,7 +235,6 @@ config.addAppender(appender); updateLoggers(appender, config); ---- -*We strongly advise you to avoid programmatically modifying components of a configuration!* This approach is prone several problems: * Your code relies on Log4j Core internals which don't have any backward compatibility guarantees. From b69044827fbd1a643b803e7fe7f8a0e5d886f923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 1 Jul 2024 09:21:28 +0200 Subject: [PATCH 31/53] Fix Spotless failures --- .../customconfig/ExampleConfiguration.java | 6 +-- .../ExampleConfigurationFactory.java | 1 - .../examples/manual/customconfig/Usage.java | 51 +++++++++---------- 3 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfiguration.java b/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfiguration.java index 9bd41ac8adf..5d8a00b4241 100644 --- a/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfiguration.java +++ b/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfiguration.java @@ -34,16 +34,14 @@ protected void doConfigure() { Appender appender = createAppender("ExampleAppender"); appender.start(); addAppender(appender); - LoggerConfig loggerConfig = LoggerConfig - .newBuilder() + LoggerConfig loggerConfig = LoggerConfig.newBuilder() .withConfig(this) .withAdditivity(false) .withLevel(Level.INFO) .withLoggerName("com.example") - .withRefs(new AppenderRef[]{AppenderRef.createAppenderRef("ExampleAppender", null, null)}) + .withRefs(new AppenderRef[] {AppenderRef.createAppenderRef("ExampleAppender", null, null)}) .build(); loggerConfig.addAppender(appender, null, null); addLogger("com.example", loggerConfig); } - } diff --git a/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfigurationFactory.java b/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfigurationFactory.java index ce9e1b46794..3a33a0cf1d4 100644 --- a/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfigurationFactory.java +++ b/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfigurationFactory.java @@ -37,6 +37,5 @@ public Configuration getConfiguration(LoggerContext loggerContext, Configuration public String[] getSupportedTypes() { return new String[] {"*"}; } - } // end::class[] diff --git a/src/site/antora/modules/ROOT/examples/manual/customconfig/Usage.java b/src/site/antora/modules/ROOT/examples/manual/customconfig/Usage.java index 4b88e562366..bdd5dd6772c 100644 --- a/src/site/antora/modules/ROOT/examples/manual/customconfig/Usage.java +++ b/src/site/antora/modules/ROOT/examples/manual/customconfig/Usage.java @@ -16,35 +16,35 @@ */ package com.example; +import java.net.URI; +import java.util.Arrays; import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.AbstractConfiguration; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.ConfigurationFactory; -import org.apache.logging.log4j.core.config.ConfigurationSource; import org.apache.logging.log4j.core.config.Configurator; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; import org.apache.logging.log4j.core.config.composite.CompositeConfiguration; -import org.apache.logging.log4j.core.LoggerContext; - -import java.net.URI; -import java.util.Arrays; final class Usage { private static Configuration createConfiguration() { // tag::createConfiguration[] ConfigurationBuilder configBuilder = - ConfigurationBuilderFactory.newConfigurationBuilder(); //<1> + ConfigurationBuilderFactory.newConfigurationBuilder(); // <1> Configuration configuration = configBuilder - .add(configBuilder //<2> - .newAppender("CONSOLE", "List") - .add(configBuilder.newLayout("JsonTemplateLayout"))) - .add(configBuilder //<3> - .newRootLogger(Level.WARN) - .add(configBuilder.newAppenderRef("CONSOLE"))) - .build(false); //<4> + .add( + configBuilder // <2> + .newAppender("CONSOLE", "List") + .add(configBuilder.newLayout("JsonTemplateLayout"))) + .add( + configBuilder // <3> + .newRootLogger(Level.WARN) + .add(configBuilder.newAppenderRef("CONSOLE"))) + .build(false); // <4> // end::createConfiguration[] } @@ -66,26 +66,25 @@ private static void reconfigureActiveLoggerContext() { private static Configuration loadConfigurationFile() { // tag::loadConfigurationFile[] - ConfigurationFactory.getInstance().getConfiguration( - null, //<1> - null, //<2> - URI.create("uri://to/my/log4j2.xml")); //<3> + ConfigurationFactory.getInstance() + .getConfiguration( + null, // <1> + null, // <2> + URI.create("uri://to/my/log4j2.xml")); // <3> // end::loadConfigurationFile[] } private static Configuration combineConfigurations() { // tag::combineConfigurations[] ConfigurationFactory configFactory = ConfigurationFactory.getInstance(); - AbstractConfiguration commonConfig = (AbstractConfiguration) //<2> - configFactory.getConfiguration(null, null, URI.create("classpath:log4j2-common.xml")); //<1> - AbstractConfiguration appConfig = (AbstractConfiguration) //<2> - configFactory.getConfiguration(null, null, URI.create("classpath:log4j2-app.xml")); //<1> - AbstractConfiguration runtimeConfig = ConfigurationBuilderFactory - .newConfigurationBuilder() + AbstractConfiguration commonConfig = (AbstractConfiguration) // <2> + configFactory.getConfiguration(null, null, URI.create("classpath:log4j2-common.xml")); // <1> + AbstractConfiguration appConfig = (AbstractConfiguration) // <2> + configFactory.getConfiguration(null, null, URI.create("classpath:log4j2-app.xml")); // <1> + AbstractConfiguration runtimeConfig = ConfigurationBuilderFactory.newConfigurationBuilder() // ... - .build(false); //<3> - return new CompositeConfiguration(Arrays.asList(commonConfig, appConfig, runtimeConfig)); //<4> + .build(false); // <3> + return new CompositeConfiguration(Arrays.asList(commonConfig, appConfig, runtimeConfig)); // <4> // end::combineConfigurations[] } - } From df9ee62a10cd20e18a313e23aeebaa8ae4ca8a40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 1 Jul 2024 10:47:46 +0200 Subject: [PATCH 32/53] Remove incorrectly placed `Filters` section in `configuration.adoc` --- src/site/antora/modules/ROOT/pages/manual/configuration.adoc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc index 0433b9a3954..1c1d0f6ab82 100644 --- a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc @@ -400,11 +400,6 @@ Overrides the logging level of xref:manual/status-logger.adoc[]. Since version `2.24.0`, this attribute is deprecated and should be replaced with the xref:manual/status-logger.adoc#log4j2.statusLoggerLevel[log4j2.statusLoggerLevel] configuration property instead. ==== -[id=configuration-elements-filters] -=== Filters - -See xref:manual/filters.adoc#filters[Filters] for additional filtering capabilities that can be applied to the global configuration object. - [id=configuring-loggers] == [[Loggers]] Loggers From cbbe196852eac631298ca4152730fffbc53c5cc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 1 Jul 2024 21:00:11 +0200 Subject: [PATCH 33/53] Improve wording --- .../modules/ROOT/partials/manual/plugin-preliminaries.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/site/antora/modules/ROOT/partials/manual/plugin-preliminaries.adoc b/src/site/antora/modules/ROOT/partials/manual/plugin-preliminaries.adoc index ce00948b590..f15448a511a 100644 --- a/src/site/antora/modules/ROOT/partials/manual/plugin-preliminaries.adoc +++ b/src/site/antora/modules/ROOT/partials/manual/plugin-preliminaries.adoc @@ -16,7 +16,7 @@ //// Log4j plugin system is the de facto extension mechanism embraced by various Log4j components. -Plugins make it possible for extensible components to _receive_ feature implementations without any explicit links in between. +Plugins provide extension points to components, that can be used to implement new features, without modifying the original component. It is analogous to a https://en.wikipedia.org/wiki/Dependency_injection[dependency injection] framework, but curated for Log4j-specific needs. In a nutshell, you annotate your classes with link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/Plugin.html[`@Plugin`] and their (`static`) factory methods with link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginFactory.html[`@PluginFactory`]. From e2a216836114774adb0d5d5b3143240cf47c3d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 1 Jul 2024 21:00:11 +0200 Subject: [PATCH 34/53] Improve wording --- .../modules/ROOT/partials/manual/plugin-preliminaries.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/site/antora/modules/ROOT/partials/manual/plugin-preliminaries.adoc b/src/site/antora/modules/ROOT/partials/manual/plugin-preliminaries.adoc index ce00948b590..f15448a511a 100644 --- a/src/site/antora/modules/ROOT/partials/manual/plugin-preliminaries.adoc +++ b/src/site/antora/modules/ROOT/partials/manual/plugin-preliminaries.adoc @@ -16,7 +16,7 @@ //// Log4j plugin system is the de facto extension mechanism embraced by various Log4j components. -Plugins make it possible for extensible components to _receive_ feature implementations without any explicit links in between. +Plugins provide extension points to components, that can be used to implement new features, without modifying the original component. It is analogous to a https://en.wikipedia.org/wiki/Dependency_injection[dependency injection] framework, but curated for Log4j-specific needs. In a nutshell, you annotate your classes with link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/Plugin.html[`@Plugin`] and their (`static`) factory methods with link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginFactory.html[`@PluginFactory`]. From def1d8f009cc514c81f9643d1038eef87bdb9fea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 1 Jul 2024 21:11:31 +0200 Subject: [PATCH 35/53] Fix `ExampleConfigurationFactory` method overload --- .../manual/customconfig/ExampleConfigurationFactory.java | 7 ++++++- .../antora/modules/ROOT/pages/manual/customconfig.adoc | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfigurationFactory.java b/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfigurationFactory.java index 3a33a0cf1d4..8c08a1d413f 100644 --- a/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfigurationFactory.java +++ b/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfigurationFactory.java @@ -29,7 +29,12 @@ public class ExampleConfigurationFactory extends ConfigurationFactory { @Override - public Configuration getConfiguration(LoggerContext loggerContext, ConfigurationSource source) { + public Configuration getConfiguration(LoggerContext loggerContext, ConfigurationSource source) { // <1> + // Return a `Configuration`... + } + + @Override + public Configuration getConfiguration(LoggerContext loggerContext, String name, URI configLocation) { // Return a `Configuration`... } diff --git a/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc b/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc index 25a89d93f9f..277c43facbc 100644 --- a/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc @@ -167,6 +167,9 @@ Consider the example below: ---- include::example$manual/customconfig/ExampleConfigurationFactory.java[tag=class] ---- +<1> `getConfiguration(LoggerContext, ConfigurationSource)` is only called if `ConfigurationSource` is not null. +This is possible if the `Configuration` is provided programmatically. +Hence, you are encouraged to implement `getConfiguration(LoggerContext, String, URI)` overload too. [#guides] == How-to guides From b950f8892e84efd901731c8130453e3efc10d735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 1 Jul 2024 21:17:51 +0200 Subject: [PATCH 36/53] Remove the mention of `type-safe` --- src/site/antora/modules/ROOT/pages/manual/customconfig.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc b/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc index 277c43facbc..ed40f4552ab 100644 --- a/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc @@ -50,7 +50,7 @@ In short, we will create ``Configuration``s using `ConfigurationBuilder`, and ac === `ConfigurationBuilder` link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.html[`ConfigurationBuilder`] interface models a fluent API to programmatically create link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`]s. -If you have ever created a xref:manual/configuration.adoc[Log4j Core configuration file], consider `ConfigurationBuilder` as a convenience utility to model the very same declarative configuration structure programmatically, in a type-safe way. +If you have ever created a xref:manual/configuration.adoc[Log4j Core configuration file], consider `ConfigurationBuilder` as a convenience utility to model the very same declarative configuration structure programmatically. Let's show `ConfigurationBuilder` usage with an example. Consider the following Log4j Core configuration file: From cd56f42a13f226bd6568980a6c47a90b04ff9615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 1 Jul 2024 21:32:24 +0200 Subject: [PATCH 37/53] Fix explanation on `Configurator.initialize()` --- .../antora/modules/ROOT/pages/manual/customconfig.adoc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc b/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc index ed40f4552ab..9093a40110d 100644 --- a/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc @@ -124,9 +124,9 @@ See {project-github-url}/log4j-core-test/src/test/java/org/apache/logging/log4j/ link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configurator.html[`Configurator`] is a programmatic interface to associate a ``Configuration`` with either new, or an existing `LoggerContext`. [#Configurator-initialize] -==== Creating a new `LoggerContext` +==== Obtaining a `LoggerContext` -You can use `Configurator` to create a new `LoggerContext` as follows: +You can use `Configurator` to obtain a `LoggerContext`: .Snippet from an example {antora-examples-url}/manual/customconfig/Usage.java[`Usage.java`] [source,java] @@ -134,7 +134,8 @@ You can use `Configurator` to create a new `LoggerContext` as follows: include::example$manual/customconfig/Usage.java[tag=useConfiguration,indent=0] ---- -This is a convenient way to create single-use, isolated ``LoggerContext``s for tests, etc. +`initialize()` will either return the `LoggerContext` currently associated with the caller, or create a new one. +This is a convenient way to create isolated ``LoggerContext``s for tests, etc. [#Configurator-reconfigure] ==== Reconfiguring the active `LoggerContext` From 6399dbe131b7084a90aab0e1f6cb17140e692792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 1 Jul 2024 21:40:07 +0200 Subject: [PATCH 38/53] Fix explanation on `ConfigurationFactory` ordering --- .../antora/modules/ROOT/pages/manual/configuration.adoc | 7 +++++-- .../antora/modules/ROOT/pages/manual/customconfig.adoc | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc index 1c1d0f6ab82..388deed0dc0 100644 --- a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc @@ -71,13 +71,16 @@ How this works under the hood and how you can introduce your custom implementati .Supported configuration file formats by predefined `ConfigurationFactory` plugins [%header,cols="1,1m,1"] |=== -| File format | Extension | Plugin order +| File format | Extension | Order | XML | xml | 5 | JSON | json, jsn | 6 | YAML | yaml, yml | 7 | Properties | properties | 8 |=== +Note that `ConfigurationFactory` plugins will be employed in descending order. +That is, for instance, XML file format will be checked last, as a fallback. + Some `ConfigurationFactory` plugins require additional dependencies on the classpath: include::partial$configuration-file-format-deps.adoc[] @@ -1215,7 +1218,7 @@ Under the hood, Log4j Core uses plugins extending from link:../javadoc/log4j-cor This procedure can be summarized as follows: . Load plugins which extend from link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFactory.html[`ConfigurationFactory`] and whose plugin `category` is set to link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFactory.html#CATEGORY[`ConfigurationFactory.CATEGORY`] -. Sort them by link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Order.html[`@Order`] annotation, if present +. Sort them by link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Order.html[`@Order`] annotation (in decreasing value order), if present . Feed <> to `ConfigurationFactory` instances in order, if the file extension is contained by `String[]` returned from link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFactory.html#getSupportedTypes()[`ConfigurationFactory#getSupportedTypes()`] If xref:manual/systemproperties.adoc#log4j2.configurationFactory[the `log4j2.configurationFactory` system property] is defined, it will be used before any other factory implementations. diff --git a/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc b/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc index 9093a40110d..afd017b1fba 100644 --- a/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc @@ -158,7 +158,7 @@ link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFac You need to * xref:manual/configuration.adoc#ConfigurationFactory[Create a custom `ConfigurationFactory` plugin] -* Assign it a higher priority (i.e., lower `@Order` value) +* Assign it a higher priority (i.e., higher `@Order` value) * Support all configuration file types (i.e. return `*` from `getSupportedTypes()`) Consider the example below: From d1149f00f98ed2a4ece7418028e2ac420ccd7025 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 1 Jul 2024 22:02:47 +0200 Subject: [PATCH 39/53] Fixing HTTP redirects based on fragments The browser does not send the fragment part to the server, when requesting a page. So all redirects from `a.html#fragment` to `b.html` will not work. On the other hand you can redirect `a.html` to `b.html#fragment`, but you need to add the NE (disables escaping) flag. --- src/site/resources/.htaccess | 48 ------------------------------------ 1 file changed, 48 deletions(-) diff --git a/src/site/resources/.htaccess b/src/site/resources/.htaccess index 9114cab475d..ad47945d850 100644 --- a/src/site/resources/.htaccess +++ b/src/site/resources/.htaccess @@ -44,54 +44,6 @@ RewriteRule "^log4j-mongodb3(/index)?\.html$" "manual/appenders.html#NoSQLAppend RewriteRule "^log4j-mongodb4(/index)?\.html$" "manual/appenders.html#log4j-mongodb4" [R=permanent,NE] RewriteRule "^log4j-slf4j2?-impl(/index)?\.html$" "manual/installation.html#impl-core-bridge-slf4j" [R=permanent,NE] RewriteRule "^manual/api-separation\.html$" "manual/api.html" [R=permanent] -RewriteRule "^manual/customconfig\.html#ConfigurationFactory$" "manual/configuration.html#ConfigurationFactory" [R=permanent] -RewriteRule "^manual/extending\.html#Appenders$" "manual/appenders.html#extending" [R=permanent] -RewriteRule "^manual/extending\.html#Filters$" "manual/filters.html#extending" [R=permanent] -RewriteRule "^manual/extending\.html#Layouts$" "manual/layouts.html#extending" [R=permanent] -RewriteRule "^manual/extending\.html#Lookups$" "manual/lookups.html#extending" [R=permanent] -RewriteRule "^manual/extending\.html#PatternConverters$" "manual/pattern-layout.html#extending-converters" [R=permanent] -RewriteRule "^manual/extending\.html#Plugin_Builders$" "manual/plugins.html#declare-plugin-factory" [R=permanent] -RewriteRule "^manual/layouts\.html#enable-jansi$" "manual/pattern-layout.html#jansi" [R=permanent] -RewriteRule "^manual/layouts\.html#EndOfBatch$" "manual/pattern-layout.html#converter-end-of-batch" [R=permanent] -RewriteRule "^manual/layouts\.html#LevelPatternSelector$" "manual/pattern-layout.html#plugin-element-LevelPatternSelector" [R=permanent] -RewriteRule "^manual/layouts\.html#MarkerPatternSelector$" "manual/pattern-layout.html#plugin-element-MarkerPatternSelector" [R=permanent] -RewriteRule "^manual/layouts\.html#(PatternLayout|pattern-layout)$" "manual/pattern-layout.html" [R=permanent] -RewriteRule "^manual/layouts\.html#Patterns$" "manual/pattern-layout.html#converters" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternClass$" "manual/pattern-layout.html#converter-class" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternDate$" "manual/pattern-layout.html#converter-date" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternException$" "manual/pattern-layout.html#converter-exception" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternExtendedException$" "manual/pattern-layout.html#converter-exception-extended" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternFile$" "manual/pattern-layout.html#converter-file" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternLevel$" "manual/pattern-layout.html#converter-level" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternLine$" "manual/pattern-layout.html#converter-line" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternLocation$" "manual/pattern-layout.html#converter-location" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternLoggerFqcn$" "manual/pattern-layout.html#converter-fqcn" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternMap$" "manual/pattern-layout.html#converter-map" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternMarker(SimpleName)?$" "manual/pattern-layout.html#converter-marker" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternMaxLength$" "manual/pattern-layout.html#converter-max-len" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternMDC$" "manual/pattern-layout.html#converter-thread-context-map" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternMessage$" "manual/pattern-layout.html#converter-message" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternMethod$" "manual/pattern-layout.html#converter-method" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternNDC$" "manual/pattern-layout.html#converter-thread-context-stack" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternPercentLiteral$" "manual/pattern-layout.html#usage" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternRelative$" "manual/pattern-layout.html#converter-relative" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternRepeat$" "manual/pattern-layout.html#converter-repeat" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternReplace$" "manual/pattern-layout.html#converter-replace" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternSelectors$" "manual/pattern-layout.html#plugin-element-PatternSelector" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternSequenceNumber$" "manual/pattern-layout.html#converter-seq" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternStyle$" "manual/pattern-layout.html#converter-style" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternThreadId$" "manual/pattern-layout.html#converter-thread-id" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternThreadName$" "manual/pattern-layout.html#converter-thread-name" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternThreadPriority$" "manual/pattern-layout.html#converter-thread-priority" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternUUID$" "manual/pattern-layout.html#converter-uuid" [R=permanent] -RewriteRule "^manual/layouts\.html#NanoTime$" "manual/pattern-layout.html#converter-nano" [R=permanent] -RewriteRule "^manual/layouts\.html#PatternNewLine$" "manual/pattern-layout.html#usage" [R=permanent] -RewriteRule "^manual/layouts\.html#Process_ID$" "manual/pattern-layout.html#converter-pid" [R=permanent] -RewriteRule "^manual/layouts\.html#ScriptPatternSelector$" "manual/pattern-layout.html#plugin-element-ScriptPatternSelector" [R=permanent] -RewriteRule "^manual/layouts\.html#VariablesNotEmpty$" "manual/pattern-layout.html#converter-not-empty" [R=permanent] -RewriteRule "^manual/plugins\.html#converters$" "manual/pattern-layout.html#extending-converters" [R=permanent] -RewriteRule "^manual/plugins\.html#developer-notes$" "manual/plugins.html" [R=permanent] -RewriteRule "^manual/plugins\.html#lookups$" "manual/lookups.html#extending" [R=permanent] RewriteRule "^manual/scala-api\.html$" "/log4j/scala/latest/index.html" [R=permanent] RewriteRule "^manual/usage\.html$" "manual/api.html" [R=permanent] RewriteRule "^runtime-dependencies\.html$" "manual/installation.html" [R=permanent] From c3e71b4b2e18b1698dd03f4eacb16f48fdfa58f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 1 Jul 2024 22:04:11 +0200 Subject: [PATCH 40/53] Fix `@Order` in `ExampleConfigurationFactory` Co-authored-by: Piotr P. Karwasz --- .../manual/customconfig/ExampleConfigurationFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfigurationFactory.java b/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfigurationFactory.java index 8c08a1d413f..25a625c1b15 100644 --- a/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfigurationFactory.java +++ b/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfigurationFactory.java @@ -24,7 +24,7 @@ import org.apache.logging.log4j.core.config.plugins.Plugin; // tag::class[] -@Order(5) +@Order(100) @Plugin(name = "ExampleConfigurationFactory", category = ConfigurationFactory.CATEGORY) public class ExampleConfigurationFactory extends ConfigurationFactory { From 725352e3956ce409b0c36d157b88c619891d7ce9 Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Tue, 2 Jul 2024 13:42:38 +0200 Subject: [PATCH 41/53] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Volkan Yazıcı --- src/site/antora/modules/ROOT/pages/javadoc.adoc | 4 ++-- .../modules/ROOT/pages/manual/appenders.adoc | 7 +++---- .../antora/modules/ROOT/pages/manual/lookups.adoc | 2 +- .../antora/modules/ROOT/pages/manual/webapp.adoc | 15 +++++++-------- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/javadoc.adoc b/src/site/antora/modules/ROOT/pages/javadoc.adoc index bb2b3c01988..f8d5a862cd0 100644 --- a/src/site/antora/modules/ROOT/pages/javadoc.adoc +++ b/src/site/antora/modules/ROOT/pages/javadoc.adoc @@ -25,8 +25,8 @@ The table below contains links to the Javadoc API Documentation for the componen | The interface that applications should use and code against. | link:javadoc/log4j-core/index.html[Implementation] -| The standard implementation, also called the Log4j 2 Core, that contains Appenders, Filters, and more. +| The logging implementation (i.e., Log4j Core) that contains appenders, layouts, filters, and more. | link:javadoc/log4j-jakarta-web/index.html[Log4j Web] -| Tools to use Log4j in Jakarta EE applications. +| Tools to use Log4j Core in Jakarta EE applications. |=== diff --git a/src/site/antora/modules/ROOT/pages/manual/appenders.adoc b/src/site/antora/modules/ROOT/pages/manual/appenders.adoc index 71bd42c2c45..317132351ad 100644 --- a/src/site/antora/modules/ROOT/pages/manual/appenders.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/appenders.adoc @@ -3814,7 +3814,7 @@ Note that the AuditAppender was predefined while the RollingFileAppenders are cr == Servlet appender The servlet appender allows users to forward all logging calls to the -https://jakarta.ee/specifications/servlet/5.0/apidocs/jakarta/servlet/servletcontext#log(java.lang.String,java.lang.Throwable)[`ServletContext.log(String, Throwable)`] +https://jakarta.ee/specifications/servlet/5.0/apidocs/jakarta/servlet/servletcontext#log(java.lang.String,java.lang.Throwable)[`ServletContext.log()`] methods. You can use it by declaring an appender of type `Servlet` in your configuration file: @@ -3849,8 +3849,7 @@ include::example$manual/appenders/servlet-appender.properties[tag=servlet] ---- ==== -<1> The appender passes the exception using the `Throwable` parameter of the -https://jakarta.ee/specifications/servlet/5.0/apidocs/jakarta/servlet/servletcontext#log(java.lang.String,java.lang.Throwable)[`ServletContext.log(String, Throwable)`] method. +<1> Encodes events using xref:manual/pattern-layout.adoc[] and forwards the call to `ServletContext.log()`. Setting `alwaysWriteExceptions` to `false` prevents exceptions from appearing in the log file twice. Additional runtime dependencies are required for using the servlet appender: @@ -3862,7 +3861,7 @@ See xref:manual/webapp.adoc[] for more information. [CAUTION] ==== `ServletContext.log(String, Throwable)` is a very primitive logging interface. -Modern application servers usually forward its calls to a proper logging framework and use a **fixed** logging level for all the events. +Modern application servers usually forward their calls to a proper logging framework and use a **fixed** logging level for all the events. ==== [[SMTPAppender]] diff --git a/src/site/antora/modules/ROOT/pages/manual/lookups.adoc b/src/site/antora/modules/ROOT/pages/manual/lookups.adoc index d50c8e4ee13..be95a7306f9 100644 --- a/src/site/antora/modules/ROOT/pages/manual/lookups.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/lookups.adoc @@ -663,7 +663,7 @@ See xref:manual/webapp.adoc[] for more information. |Description |attr. -|Value of the ` servlet context attribute. +|Value of the `` servlet context attribute. |contextPathName |The **first** fragment of the servlet context path. diff --git a/src/site/antora/modules/ROOT/pages/manual/webapp.adoc b/src/site/antora/modules/ROOT/pages/manual/webapp.adoc index 0e814b1468e..11150cfabe9 100644 --- a/src/site/antora/modules/ROOT/pages/manual/webapp.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/webapp.adoc @@ -40,7 +40,7 @@ The second approach is more complex and requires changes to the configuration of See <> for more details. [#log4j-jakarta-web] -== Integrating with Web Applications +== Integrating with web applications [NOTE] ==== @@ -63,7 +63,7 @@ To properly synchronize the lifecycles of Log4j Core and Jakarta EE applications [#log4j-jakarta-web-installation] === Installation -In order to install Log4j Web in your Web application, you need to add it as runtime dependency: +In order to install Log4j Web in your web application, you need to add it as runtime dependency: include::partial$manual/dependencies-log4j-jakarta-web.adoc[] @@ -80,10 +80,10 @@ See also <>. [WARNING] ==== While the Servlet Specification allows web fragments to automatically add context listeners, it does not give any guarantees regarding the order in which those listeners are executed -(cf. https://jakarta.ee/specifications/servlet/5.0/jakarta-servlet-spec-5.0#Assembling_the_descriptor[section 8.2.3]). +(see https://jakarta.ee/specifications/servlet/5.0/jakarta-servlet-spec-5.0#Assembling_the_descriptor[Section 8.2.3]). If other context listeners in your application use logging, you need to make sure that `Log4jServletContextListener` is the last listener to be executed at shutdown. -In order to do it you must create a `web.xml` descriptor and add the `Log4jServletContextListener` explicitly as **first** context listener: +In order to do it you must create a `web.xml` descriptor and add the `Log4jServletContextListener` explicitly as the **first** context listener: [source,xml,indent=0] ---- @@ -473,10 +473,9 @@ See xref:manual/lookups.adoc#JndiLookup[JNDI lookup] for more information. When using a single logger context you choose between: * logging all events to a single appender. -You'll need a tool like Elastic Search to separate log events. -We recommend the usage of a structured layout like -xref:manual/json-template-layout.adoc[] -in this case: +We strongly recommend using a structured layout (e.g., xref:manual/json-template-layout.adoc[]) with an additional field capturing the Servlet context name. +This would allow separation of application logs by filtering on the context name. +Following example demonstrates this scheme using a Socket Appender writing to Elasticsearch: + [tabs] ==== From 457bbd8d18b19696bb3b41194314295067b1f074 Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Tue, 2 Jul 2024 14:26:48 +0200 Subject: [PATCH 42/53] Apply review suggestions (2) --- src/site/antora/modules/ROOT/pages/javadoc.adoc | 2 +- .../antora/modules/ROOT/pages/manual/appenders.adoc | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/javadoc.adoc b/src/site/antora/modules/ROOT/pages/javadoc.adoc index f8d5a862cd0..cc2bee3d5b0 100644 --- a/src/site/antora/modules/ROOT/pages/javadoc.adoc +++ b/src/site/antora/modules/ROOT/pages/javadoc.adoc @@ -22,7 +22,7 @@ The table below contains links to the Javadoc API Documentation for the componen | Component | Description | link:javadoc/log4j-api/index.html[API] -| The interface that applications should use and code against. +| The logging interface (i.e., Log4j API) that applications should use and code against. | link:javadoc/log4j-core/index.html[Implementation] | The logging implementation (i.e., Log4j Core) that contains appenders, layouts, filters, and more. diff --git a/src/site/antora/modules/ROOT/pages/manual/appenders.adoc b/src/site/antora/modules/ROOT/pages/manual/appenders.adoc index 317132351ad..530f90098db 100644 --- a/src/site/antora/modules/ROOT/pages/manual/appenders.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/appenders.adoc @@ -3811,7 +3811,7 @@ Note that the AuditAppender was predefined while the RollingFileAppenders are cr ---- [#servlet-appender] -== Servlet appender +=== Servlet appender The servlet appender allows users to forward all logging calls to the https://jakarta.ee/specifications/servlet/5.0/apidocs/jakarta/servlet/servletcontext#log(java.lang.String,java.lang.Throwable)[`ServletContext.log()`] @@ -3849,8 +3849,10 @@ include::example$manual/appenders/servlet-appender.properties[tag=servlet] ---- ==== -<1> Encodes events using xref:manual/pattern-layout.adoc[] and forwards the call to `ServletContext.log()`. -Setting `alwaysWriteExceptions` to `false` prevents exceptions from appearing in the log file twice. +<1> Encodes events using xref:manual/pattern-layout.adoc[] and forwards the call to +`ServletContext.log()`. +Setting `alwaysWriteExceptions` to `false` prevents the stacktrace from appearing as both part of the `message` argument and as `throwable` argument: +this usually results in the stacktrace being printed to the log file twice. Additional runtime dependencies are required for using the servlet appender: @@ -3860,8 +3862,8 @@ See xref:manual/webapp.adoc[] for more information. [CAUTION] ==== -`ServletContext.log(String, Throwable)` is a very primitive logging interface. -Modern application servers usually forward their calls to a proper logging framework and use a **fixed** logging level for all the events. +The `ServletContext.log(String, Throwable)` method predates modern logging APIs. +By using Servlet appender you typically will not be able to differentiate log events by log level or logger name. ==== [[SMTPAppender]] From 6ad209e057421baa57a79de2fe594a1f92d07b51 Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Tue, 2 Jul 2024 14:48:47 +0200 Subject: [PATCH 43/53] Apply IDEA suggestions --- .../modules/ROOT/pages/manual/webapp.adoc | 120 ++++++++++-------- 1 file changed, 66 insertions(+), 54 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/webapp.adoc b/src/site/antora/modules/ROOT/pages/manual/webapp.adoc index 11150cfabe9..d2ea4252677 100644 --- a/src/site/antora/modules/ROOT/pages/manual/webapp.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/webapp.adoc @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. //// - +[#jakarta-ee] = Using Log4j in a Jakarta EE environment :jetty-latest-url: https://jetty.org/docs/jetty/12 :tomcat-latest-url: https://tomcat.apache.org/tomcat-11.0-doc @@ -29,9 +29,9 @@ While the first approach is the easiest to implement, it has some limitations: * Log events emitted by each application and the libraries bundled with it will be handled by Log4j Core, but events related to the application emitted by a **shared** library (e.g. JPA implementation) will be handled by the application server. -To diagnose a problem with the application you might need to look into multiple log files. +To diagnose a problem with the application, you might need to look into multiple log files. * Each application must use a **different** log file to prevent problems with concurrent access to the same file by multiple applications. -Problems may arise especially if xref:manual/appenders.adoc#rollingfileappender[rolling file appender] is used. +Problems may arise, especially if xref:manual/appenders.adoc#rollingfileappender[rolling file appender] is used. * Web applications have a different lifecycle from the application server. Additional care is required to stop Log4j Core when the application is stopped. See <> for more details. @@ -44,12 +44,14 @@ See <> for more details. [NOTE] ==== -To avoid problems, some Log4j API and Log4j Core features are automatically disabled, when running in a Jakarta EE environment. Most notably: +To avoid problems, +some Log4j API and Log4j Core features are automatically disabled when running in a Jakarta EE environment. +Most notably: -- the usage of `ThreadLocal` for object pooling is disabled, +- the usage of `ThreadLocal` for object pooling is disabled. - a web-safe implementation of -link:../javadoc/log4j-api/org/apache/logging/log4j/spi/ThreadContextMap.html[`ThreadContextMap`] is used, -- JMX notifications are sent synchronously, +link:../javadoc/log4j-api/org/apache/logging/log4j/spi/ThreadContextMap.html[`ThreadContextMap`] is used. +- JMX notifications are sent synchronously. - the JVM shutdown hook is disabled. See xref:manual/systemproperties.adoc#log4j2.isWebapp[`log4j2.isWebapp`] for more details. @@ -58,12 +60,13 @@ See xref:manual/systemproperties.adoc#log4j2.isWebapp[`log4j2.isWebapp`] for mor Using a logging implementation like **Log4j Core** in a Jakarta EE application requires particular care. Since the lifecycle of a container or web application is independent of the lifecycle of the JVM, it's important for logging resources to be properly cleaned up (database connections closed, files closed, etc.) when the container or web application shuts down. -To properly synchronize the lifecycles of Log4j Core and Jakarta EE applications an additional **Log4j Web** artifact is provided. +To properly synchronize the lifecycles of Log4j Core and Jakarta EE applications, +an additional **Log4j Web** artifact is provided. [#log4j-jakarta-web-installation] === Installation -In order to install Log4j Web in your web application, you need to add it as runtime dependency: +To install Log4j Web in your web application, you need to add it as a runtime dependency: include::partial$manual/dependencies-log4j-jakarta-web.adoc[] @@ -71,8 +74,8 @@ If you are writing a Servlet 3.0 or later application, Apache Log4j Web will reg https://jakarta.ee/specifications/servlet/5.0/apidocs/jakarta/servlet/servletcontainerinitializer[`ServletContainerInitializer`] that takes care of configuring the Log4j lifecycle for you. Under the hood this will: -* initialize Log4j Core with the correct configuration file, -* register a `Log4jServletContextListener` to automatically shut down Log4j Core, when the application shuts down, +* initialize Log4j Core with the correct configuration file. +* register a `Log4jServletContextListener` to automatically shut down Log4j Core, when the application shuts down. * register a `Log4jServletFilter` to enable the xref:manual/lookups.adoc#WebLookup[web lookup]. See also <>. @@ -83,7 +86,7 @@ While the Servlet Specification allows web fragments to automatically add contex (see https://jakarta.ee/specifications/servlet/5.0/jakarta-servlet-spec-5.0#Assembling_the_descriptor[Section 8.2.3]). If other context listeners in your application use logging, you need to make sure that `Log4jServletContextListener` is the last listener to be executed at shutdown. -In order to do it you must create a `web.xml` descriptor and add the `Log4jServletContextListener` explicitly as the **first** context listener: +To do it, you must create a `web.xml` descriptor and add the `Log4jServletContextListener` explicitly as the **first** context listener: [source,xml,indent=0] ---- @@ -91,9 +94,11 @@ include::example$manual/webapp/web.xml[tag=context-listener] ---- ==== +[#log4j-jakarta-manual-instalation] ==== Manual installation -If you are maintaining an older Servlet 2.5 (or earlier) application or if you disabled +If you are maintaining an older Servlet 2.5 (or earlier) application, +or if you disabled the <>. [source,xml,indent=0] ---- @@ -141,7 +146,7 @@ If set to `true`, the `Log4jServletContextListener` will not register a `Log4jSe | https://docs.oracle.com/javase/{java-target-version}/docs/api/java/util/concurrent/TimeUnit.html#SECONDS[`SECONDS`] |=== -The `TimeUnit` to use for the shut-down delay. +Specifies the `TimeUnit` used for the shut-down delay. [id=log4j.stop.timeout] ==== `log4j.stop.timeout` @@ -167,7 +172,7 @@ The `TimeUnit` to use for the shut-down delay. | _automatically computed_ |=== -Specifies the name of the logger context to use. +Used to specify the name of the logger context. If <> is used, this parameter **must** be explicitly provided. Otherwise, the default value is: @@ -203,17 +208,17 @@ Must be set to `true` to use the <>. The location of a Log4j Core configuration file. If the provided value is not an **absolute** URI, Log4j interprets it as: -. path to a -https://jakarta.ee/specifications/servlet/5.0/apidocs/jakarta/servlet/servletcontext#getResource(java.lang.String)[servlet context resource], if it exists, -. path to a file, if it exists, -. path to a -https://docs.oracle.com/javase/8/docs/api/java/lang/ClassLoader.html#getResource-java.lang.String-[classpath resource], otherwise. +. the path to an existing +https://jakarta.ee/specifications/servlet/5.0/apidocs/jakarta/servlet/servletcontext#getResource(java.lang.String)[servlet context resource], +. the path to an existing file, +. the path to a +https://docs.oracle.com/javase/8/docs/api/java/lang/ClassLoader.html#getResource-java.lang.String-[classpath resource]. If no value is provided: . Log4j Web looks for a servlet context resource named `/WEB-INF/log4j2-.`, where `` is the name of the logger context, . if no such file exists it looks for a servlet context resource named `/WEB-INF/log4j2.`, -. otherwise it searches for a configuration file on the classpath using the usual https://logging.apache.org/log4j/2.x/manual/configuration.html#automatic-configuration[automatic configuration procedure]. +. otherwise, it searches for a configuration file on the classpath using the usual https://logging.apache.org/log4j/2.x/manual/configuration.html#automatic-configuration[automatic configuration procedure]. [#async] === Asynchronous requests and threads @@ -221,10 +226,12 @@ If no value is provided: In order for the xref:manual/lookups.adoc#WebLookup[web lookup] to work correctly, Log4j must be able to always identify the `ServletContext` used by the current thread. -When standard requests, forwards, includes, and error resources are processed, the `Log4jServletFilter` binds the `LoggerContext` to the thread handling the request, and you don't have to do anything. +When standard requests, forwards, inclusions, and error resources are processed, +the `Log4jServletFilter` binds the `LoggerContext` to the thread handling the request, +and you don't have to do anything. The handling of asynchronous requests is however more tricky, since it allows you to execute code on threads that were not prepared by `Log4jServletFilter`. -Such a situation occurs for example, if your code was started using the +Such a situation occurs, for example, if your code was started using the https://jakarta.ee/specifications/servlet/5.0/apidocs/jakarta/servlet/asynccontext#start(java.lang.Runnable)[`AsyncContext.start(Runnable)`] method. @@ -251,7 +258,7 @@ include::example$manual/webapp/AsyncServlet.java[tag=manual] To help users add logging statements to JavaServer Pages, Log4j provides a JSP tag library modeled after the https://web.archive.org/web/20140215182415/http://jakarta.apache.org/taglibs/log/[Jakarta Commons Log Tag library]. -In order to use it, you need to add the following runtime dependency to your web application project: +To use it, you need to add the following runtime dependency to your web application project: include::partial$manual/dependencies-log4j-taglib.adoc[] @@ -266,7 +273,8 @@ include::example$manual/webapp/taglib.jsp[tag=declaration] ==== The Log4j Taglib component is deprecated and is scheduled for removal in Log4j 3. -Currently, it only works with JavaServer Pages 2.3 or older and no version compatible with Jakarta Server Pages 3.0 is available. +Currently, it only works with JavaServer Pages 2.3 and previous releases, +and no version compatible with Jakarta Server Pages 3.0 is available. ==== The Log4j Taglib library defines a tag for most @@ -327,7 +335,7 @@ WildFly:: WildFly implicitly adds a shared copy of `log4j-api` to each web application deployment. This copy of `log4j-api` is configured to forward all events to WildFly's centralized logging system and does **not** use the copy of Log4j Core bundled with the web application. + -In order to use Log4j Core, you need to set the `add-logging-api-dependencies` attribute of the logging subsystem to `false`. +To use Log4j Core, you need to set the `add-logging-api-dependencies` attribute of the logging subsystem to `false`. See {wildfly-latest-url}/Admin_Guide.html#logging-attributes[WildFly documentation] for more details. [#sharing] @@ -339,7 +347,7 @@ without losing the ability to configure logging for each application separately. Sharing Log4j Core has two main advantages: * You can send log statements from multiple applications to the same log file. -Under the hood Log4j Core will use a single +Under the hood, Log4j Core will use a single xref:manual/architecture.adoc#AbstractManager[manager] per file, which will serialize concurrent access from multiple applications. * You can capture log statements issued by other shared libraries, so you don't have to look for them in the global application server log. @@ -347,7 +355,7 @@ per file, which will serialize concurrent access from multiple applications. [#sharing-setup] === Setup -In order to share Log4j Core between applications, you need to share at least these two JAR files: +To share Log4j Core between applications, you need to share at least these two JAR files: * https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-api/{log4j-api-version}[`log4j-api`] * https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-core/{log4j-core-version}[`log4j-core`] @@ -359,7 +367,7 @@ if you use a Java EE application server) Since sharing libraries between applications is not part of the Jakarta EE standard, the instructions are specific to each application server: GlassFish:: -In GlassFish you can add those libraries to the _common classloader_. +In GlassFish, you can add those libraries to the _common classloader_. See https://glassfish.org/docs/latest/application-development-guide.html#circumventing-class-loader-isolation[GlassFish documentation] for more details. Jetty:: @@ -390,7 +398,7 @@ https://jakarta.ee/specifications/servlet/6.0/jakarta-servlet-spec-6.0#web-appli ) use a _"parent last"_ delegation strategy, but prevent application from overriding implementation classes provided by the container. If you share Log4j between applications and the applications themselves contain Log4j Core, the logging behavior depends on the application server. -Some application servers will use the shared instance (e.g. WildFly), while others will use the application instance (e.g. Tomcat). +Some application servers will use the shared instance (e.g., WildFly), while others will use the application instance (e.g., Tomcat). There are two solutions to this problem: * you can remove Log4j from the WAR or EAR archive: @@ -402,22 +410,23 @@ of all Log4j libraries as `provided`. Gradle:: If you use Gradle, you can add `log4j-api` to the `providedCompile` configuration, while `log4j-core` to the `providedRuntime` configuration. -See https://docs.gradle.org/current/userguide/war_plugin.html#sec:war_dependency_management[Gradle WAR plugin] for more details. +See the https://docs.gradle.org/current/userguide/war_plugin.html#sec:war_dependency_management[Gradle WAR plugin] for more details. -* you can use an application server specific configuration option to delegate the loading of Log4j API to the parent classloader. +* you can use an application-server-specific configuration option to delegate the loading of Log4j API to the parent classloader. ==== [#log-separation] === Log separation -When using a shared instance of Log4j Core, you might be interested to identify the application associated to a given log event. +When using a shared instance of Log4j Core, +you might be interested in identifying the application associated with a given log event. Log4j Core provides a mechanism to split all link:../javadoc/log4j-api/org/apache/logging/log4j/Logger.html[`Logger`] instances into logging domains called xref:manual/architecture.adoc#LoggerContext[`LoggerContext`]s. You have therefore two ways to separate log events: -. You can create a separate logger context for each web application and one for the common libraries. +. You can create a separate logger context for each web application and one context for the common libraries. See <> for more details. . You can also use a **single** logger context for all log events, but use xref:manual/lookups.adoc[lookups] to add context data to your log events. @@ -425,7 +434,8 @@ See <> for more details. [IMPORTANT] ==== -These two approaches deliver similar results for log events generates by the web applications themselves or the libraries bundled in the WAR or EAR archive. +These two approaches deliver similar results for log events generated by the web applications themselves or the libraries +bundled in the WAR or EAR archive. Differences between these approaches appear in the handling of **shared** libraries. There are two kinds of shared libraries: @@ -436,18 +446,19 @@ These libraries will always use the same logger context, which will not be one o This kind includes all the shared libraries, which were not written with Jakarta EE in mind. . Shared libraries that use **instance** `Logger` fields. -These libraries will use the logger context associated to the web application that uses them. +These libraries will use the logger context associated with the web application that uses them. + -Application server implementation usually use instance `Logger` fields. +Application server implementations usually use instance `Logger` fields. -Since the first kind of libraries is more common, counterintuitively the <> approach will usually give better results than the <> approach. +Since the first kind of libraries is more common, +counterintuitively the <> approach will usually give better results than the <> approach. ==== [#single-context] ==== Single logger context By default, Log4j Core creates a separate logger context per classloader. -In order to use a single logger context you need to set the +To use a single logger context, you need to set the xref:manual/systemproperties.adoc#log4j2.contextSelector[`log4j2.contextSelector`] system property to: @@ -458,7 +469,7 @@ to use synchronous loggers, link:../javadoc/log4j-core/org/apache/logging/log4j/core/async/BasicAsyncLoggerContextSelector.html[`org.apache.logging.log4j.core.async.BasicAsyncLoggerContextSelector`] to use asynchronous loggers. -In this approach you must use xref:manual/lookups.adoc[lookups] to register the application that generated a log event. +In this approach, you must use xref:manual/lookups.adoc[lookups] to register the application that generated a log event. The most useful lookups in this case are: Web lookup:: @@ -470,12 +481,12 @@ JNDI lookup:: It covers a larger part of the handling of a request, but it requires additional setup to export the name of the application via JNDI. See xref:manual/lookups.adoc#JndiLookup[JNDI lookup] for more information. -When using a single logger context you choose between: +When using a single logger context, you choose between: -* logging all events to a single appender. +* Logging all events to a single appender. We strongly recommend using a structured layout (e.g., xref:manual/json-template-layout.adoc[]) with an additional field capturing the Servlet context name. This would allow separation of application logs by filtering on the context name. -Following example demonstrates this scheme using a Socket Appender writing to Elasticsearch: +The following example demonstrates this scheme using a Socket Appender writing to Elasticsearch: + [tabs] ==== @@ -508,8 +519,8 @@ include::example$manual/webapp/log4j2-single.properties[tag=global] ---- ==== -* logging events to a separate appender for each application. -In this case you can use +* Logging events to a separate appender for each application. +In this case, you can use xref:manual/appenders.adoc#RoutingAppender[routing appender] to separate the events: + @@ -550,7 +561,7 @@ include::example$manual/webapp/log4j2-single.properties[tag=routing] Since Log4j Core uses link:../javadoc/log4j-core/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.html[`ClassLoaderContextSelector`] by default, no configuration is needed to achieve multiple logger contexts in your application server: -the classes of each classloader will use the logger context associated to the classloader. +the classes of each classloader will use the logger context associated with the classloader. [TIP] ==== @@ -559,15 +570,15 @@ To provide a different configuration file for each logger context, you can add f See <> and <> for more details. ==== -Associating logger contexts to classloaders has however some limitations: **shared** libraries will not be able to use the per-application logger contexts. -To overcome this limitation Log4j Core provides an alternative algorithm to determine the right logger context to choose: JNDI lookups. +Associating logger contexts to classloaders has, however, some limitations: **shared** libraries will not be able to use the per-application logger contexts. +To overcome this limitation, Log4j Core provides an alternative algorithm to determine the right logger context to choose: JNDI lookups. [#jndi-configuration] ==== JNDI context selector -Application servers set up the correct JNDI context as soon as they determine, which application will handle a request. +Application servers set up the correct JNDI context as soon as they determine which application will handle a request. Log4j Core allows the usage of JNDI to coordinate the usage of logger contexts in a Jakarta EE application server. -In order to use this feature, you need to: +To use this feature, you need to: . Set the xref:manual/systemproperties.adoc#log4j2.contextSelector[`log4j2.contextSelector`] @@ -583,7 +594,7 @@ include::example$manual/webapp/jndi.xml[tag=jndi] ---- [#replace] -== Replacing application server logging subsystem +== Replacing the application server logging subsystem Some application servers allow administrators to replace the default logging subsystem of the application server with Log4j Core. Known instructions are listed in the section. @@ -594,10 +605,11 @@ If your application server is not listed here, check the documentation of the ap Tomcat uses a modified version of Apache Commons Logging called {tomcat-latest-url}/logging.html[Tomcat JULI] -as internal logging system. -Tomcat JULI uses `java.util.logging` as default logging implementation, but since Tomcat 8.5 you can easily replace it with a different backend. +as the internal logging system. +Tomcat JULI uses `java.util.logging` as default logging implementation, +but since Tomcat 8.5 you can replace it with a different backend. -In order to use Log4j Core as logging backend, you need to modify the +To use Log4j Core as logging backend, you need to modify the {tomcat-latest-url}/class-loader-howto.html#Class_Loader_Definitions[system classloader] of the server. Assuming `$CATALINA_BASE` is the main directory of your Tomcat instance you need to: From 91041776a6ce91f0706b784ccd8da9b6d980babf Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Tue, 2 Jul 2024 14:53:28 +0200 Subject: [PATCH 44/53] Reword global approach to logging --- .../antora/modules/ROOT/pages/manual/webapp.adoc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/webapp.adoc b/src/site/antora/modules/ROOT/pages/manual/webapp.adoc index d2ea4252677..9cee5efb88e 100644 --- a/src/site/antora/modules/ROOT/pages/manual/webapp.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/webapp.adoc @@ -27,16 +27,20 @@ In a Jakarta EE environment, there are two possible approaches to logging: While the first approach is the easiest to implement, it has some limitations: -* Log events emitted by each application and the libraries bundled with it will be handled by Log4j Core, +Shared libraries:: +Log events emitted by each application and the libraries bundled with it will be handled by Log4j Core, but events related to the application emitted by a **shared** library (e.g. JPA implementation) will be handled by the application server. To diagnose a problem with the application, you might need to look into multiple log files. -* Each application must use a **different** log file to prevent problems with concurrent access to the same file by multiple applications. +Separate log files:: +Each application must use a **different** log file to prevent problems with concurrent access to the same file by multiple applications. Problems may arise, especially if xref:manual/appenders.adoc#rollingfileappender[rolling file appender] is used. -* Web applications have a different lifecycle from the application server. +Lifecycle:: +Web applications have a different lifecycle from the application server. Additional care is required to stop Log4j Core when the application is stopped. See <> for more details. -The second approach is more complex and requires changes to the configuration of the application server. +The second approach requires changes to the configuration of the application server, +but produces better results in terms of separating log events of different applications. See <> for more details. [#log4j-jakarta-web] From 2809eaae3db7089f9c56b9b1e5a218e0606a4f38 Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Tue, 2 Jul 2024 15:11:55 +0200 Subject: [PATCH 45/53] Apply review suggestions (3) --- .../modules/ROOT/pages/manual/webapp.adoc | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/webapp.adoc b/src/site/antora/modules/ROOT/pages/manual/webapp.adoc index 9cee5efb88e..bdf902a2429 100644 --- a/src/site/antora/modules/ROOT/pages/manual/webapp.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/webapp.adoc @@ -92,6 +92,7 @@ While the Servlet Specification allows web fragments to automatically add contex If other context listeners in your application use logging, you need to make sure that `Log4jServletContextListener` is the last listener to be executed at shutdown. To do it, you must create a `web.xml` descriptor and add the `Log4jServletContextListener` explicitly as the **first** context listener: +.Snippet from an example {antora-examples-url}/manual/webapp/web.xml[`web.xml`] [source,xml,indent=0] ---- include::example$manual/webapp/web.xml[tag=context-listener] @@ -104,6 +105,7 @@ include::example$manual/webapp/web.xml[tag=context-listener] If you are maintaining an older Servlet 2.5 (or earlier) application, or if you disabled the <>. +.Snippet from an example {antora-examples-url}/manual/webapp/web.xml[`web.xml`] [source,xml,indent=0] ---- include::example$manual/webapp/web.xml[tags=context-listener;filter] @@ -164,6 +166,8 @@ Specifies the `TimeUnit` used for the shut-down delay. | `30` |=== +It specifies the duration of the shut-down delay. + [id=log4jContextName] ==== `log4jContextName` @@ -244,6 +248,7 @@ link:../javadoc/log4j-jakarta-web/org/apache/logging/log4j/web/WebLoggerContextU helper class is made available. Using this class you can either decorate a `Runnable` with method calls that bind the appropriate logger context to the thread: +.Snippet from an example {antora-examples-url}/manual/webapp/AsyncServlet.java[`AsyncServlet.java`] [source,java,indent=0] ---- include::example$manual/webapp/AsyncServlet.java[tag=automatic] @@ -252,6 +257,7 @@ include::example$manual/webapp/AsyncServlet.java[tag=automatic] or, if more flexibility is required, you can apply the same logic by using link:../javadoc/log4j-jakarta-web/org/apache/logging/log4j/web/Log4jWebSupport.html[`Log4jWebSupport`]: +.Snippet from an example {antora-examples-url}/manual/webapp/AsyncServlet.java[`AsyncServlet.java`] [source,java,indent=0] ---- include::example$manual/webapp/AsyncServlet.java[tag=manual] @@ -287,6 +293,7 @@ methods, including: * simple and parameterized log statements: + +.Snippet from an example {antora-examples-url}/manual/webapp/taglib.jsp[`taglib.jsp`] [source,xml,indent=0] ---- include::example$manual/webapp/taglib.jsp[tag=simple] @@ -294,6 +301,7 @@ include::example$manual/webapp/taglib.jsp[tag=simple] * flow tracing statements: + +.Snippet from an example {antora-examples-url}/manual/webapp/taglib.jsp[`taglib.jsp`] [source,xml,indent=0] ---- include::example$manual/webapp/taglib.jsp[tag=entry-exit] @@ -301,6 +309,7 @@ include::example$manual/webapp/taglib.jsp[tag=entry-exit] * catching and throwing statements: + +.Snippet from an example {antora-examples-url}/manual/webapp/taglib.jsp[`taglib.jsp`] [source,xml,indent=0] ---- include::example$manual/webapp/taglib.jsp[tag=catching] @@ -315,6 +324,7 @@ include::example$manual/webapp/taglib.jsp[tag=if-enabled] * tags to set the name of the logger used: + +.Snippet from an example {antora-examples-url}/manual/webapp/taglib.jsp[`taglib.jsp`] [source,xml,indent=0] ---- include::example$manual/webapp/taglib.jsp[tag=set-logger] @@ -322,14 +332,12 @@ include::example$manual/webapp/taglib.jsp[tag=set-logger] * a `dump` tag that prints the contents of a JSP scope: + +.Snippet from an example {antora-examples-url}/manual/webapp/taglib.jsp[`taglib.jsp`] [source,xml,indent=0] ---- include::example$manual/webapp/taglib.jsp[tag=dump] ---- -A complete JSP page example is available -{antora-examples-url}/manual/webapp/taglib.jsp[on GitHub]. - [#web-application-specific] === Application server specific notes @@ -370,6 +378,8 @@ if you use a Java EE application server) Since sharing libraries between applications is not part of the Jakarta EE standard, the instructions are specific to each application server: +[tabs] +==== GlassFish:: In GlassFish, you can add those libraries to the _common classloader_. See https://glassfish.org/docs/latest/application-development-guide.html#circumventing-class-loader-isolation[GlassFish documentation] for more details. @@ -394,9 +404,10 @@ You can install Log4j as a _global module_ or in a _global directory_. See {wildfly-latest-url}/Admin_Guide.html#EE_Application_Deployment_Configuration[WildFly EE Application Deployment documentation] for more details. + Check also the <>. +==== [WARNING] -==== +===== Web application classloaders (see https://jakarta.ee/specifications/servlet/6.0/jakarta-servlet-spec-6.0#web-application-class-loader[Servlet Specification 10.7.2] ) use a _"parent last"_ delegation strategy, but prevent application from overriding implementation classes provided by the container. @@ -406,18 +417,21 @@ Some application servers will use the shared instance (e.g., WildFly), while oth There are two solutions to this problem: * you can remove Log4j from the WAR or EAR archive: - ++ +[tabs] +==== Maven:: -If you use Maven, you can declare the +You can declare the https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope[scope] of all Log4j libraries as `provided`. Gradle:: -If you use Gradle, you can add `log4j-api` to the `providedCompile` configuration, while `log4j-core` to the `providedRuntime` configuration. +You can add `log4j-api` to the `providedCompile` configuration, while `log4j-core` to the `providedRuntime` configuration. See the https://docs.gradle.org/current/userguide/war_plugin.html#sec:war_dependency_management[Gradle WAR plugin] for more details. +==== * you can use an application-server-specific configuration option to delegate the loading of Log4j API to the parent classloader. -==== +===== [#log-separation] === Log separation From 0338eada282faaa12a16cb27b9e0e6c2198e90e2 Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Tue, 2 Jul 2024 15:17:43 +0200 Subject: [PATCH 46/53] Apply review suggestions (4) --- src/site/antora/modules/ROOT/pages/manual/webapp.adoc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/webapp.adoc b/src/site/antora/modules/ROOT/pages/manual/webapp.adoc index bdf902a2429..8de25f076bb 100644 --- a/src/site/antora/modules/ROOT/pages/manual/webapp.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/webapp.adoc @@ -540,7 +540,9 @@ include::example$manual/webapp/log4j2-single.properties[tag=global] * Logging events to a separate appender for each application. In this case, you can use xref:manual/appenders.adoc#RoutingAppender[routing appender] -to separate the events: +to separate the events. +This kind of configuration might be used on the development server together with the +xref:manual/pattern-layout.adoc[human-friendly Pattern Layout]: + [tabs] ==== @@ -606,6 +608,7 @@ xref:manual/systemproperties.adoc#log4j2.enableJndiContextSelector[`log4j2.enabl Log4j configuration property to `true`, . Each web application needs to configure the servlet context parameter `isLog4jContextSelectorNamed` to `true` and provide a value for the `log4jContextName` servlet context parameter and `java:comp/env/log4j/context-name` JNDI environment entry: + +.Snippet from an example {antora-examples-url}/manual/webapp/jndi.xml[`web.xml`] [source,xml,indent=0] ---- include::example$manual/webapp/jndi.xml[tag=jndi] From a44fe6cca642eb16fe263c049a55c3b6c0a72eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Tue, 2 Jul 2024 21:52:09 +0200 Subject: [PATCH 47/53] Improve wording on `Usage#loadConfigurationFile()` example --- src/site/antora/modules/ROOT/pages/manual/customconfig.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc b/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc index afd017b1fba..fc7f00881dd 100644 --- a/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc @@ -188,8 +188,8 @@ You can use this `getInstance()` method to load a configuration file programmati ---- include::example$manual/customconfig/Usage.java[tag=loadConfigurationFile,indent=0] ---- -<1> Passing the optional `LoggerContext` argument as null, since this `Configuration` is not associated with a `LoggerContext` yet -<2> Passing the optional configuration name argument as null, it will default to the configuration source location +<1> Passing the `LoggerContext` argument as null, since this is the first time we are instantiating this `Configuration`, and it is not associated with a `LoggerContext` yet +<2> Passing the configuration name argument as null, since it is not used when the configuration source location is provided <3> URI pointing to the configuration file; `file://path/to/log4j2.xml`, `classpath:log4j2.xml`, etc. [#CompositeConfiguration] From 43f84a784ca11ae1f8b4a064fa397327aa4f7e66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Tue, 2 Jul 2024 22:44:21 +0200 Subject: [PATCH 48/53] Improve `LoggerContextFactory` explanations --- src/site/antora/modules/ROOT/pages/manual/extending.adoc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/extending.adoc b/src/site/antora/modules/ROOT/pages/manual/extending.adoc index 463269e16ff..5cdd89c4dce 100644 --- a/src/site/antora/modules/ROOT/pages/manual/extending.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/extending.adoc @@ -92,9 +92,8 @@ Yet when this happens, you can use xref:manual/systemproperties.adoc#log4j2.prov === `LoggerContextFactory` link:../javadoc/log4j-api/org/apache/logging/log4j/spi/LoggerContextFactory.html[`LoggerContextFactory`] is the factory class used by Log4j API implementations to create xref:manual/architecture.adoc#LoggerContext[`LoggerContext`]s. -If you are using Log4j Core, you can provide a custom implementation using xref:manual/systemproperties.adoc#log4j2.loggerContextFactory[the `log4j2.loggerContextFactory` property]. -Another way to provide a custom `LoggerContextFactory` is to <>. -But this is generally discouraged, since it requires a more complicated setup and is intended mostly to be used by Log4j API implementations. +If you are using Log4j Core, you can use <>s to influence the way its `LoggerContextFactory` implementation works. +If you are creating a new Log4j API implementation, you should <> to introduce your custom `LoggerContextFactory` implementation. [#ContextSelector] === `ContextSelector` From 8fd4b7e680a8f12dacd4333d578aefbc8ad755ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Tue, 2 Jul 2024 22:57:33 +0200 Subject: [PATCH 49/53] Improve message factory documentation on placeholders --- .../antora/modules/ROOT/pages/manual/extending.adoc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/site/antora/modules/ROOT/pages/manual/extending.adoc b/src/site/antora/modules/ROOT/pages/manual/extending.adoc index 5cdd89c4dce..e7230a4ebdc 100644 --- a/src/site/antora/modules/ROOT/pages/manual/extending.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/extending.adoc @@ -142,3 +142,13 @@ You can replace the default `MessageFactory2` implementation with a custom one o In the case of xref:manual/flowtracing.adoc[], Log4j Core uses link:../javadoc/log4j-api/org/apache/logging/log4j/message/FlowMessageFactory.html[`FlowMessageFactory`]. You can replace the default `FlowMessageFactory` implementation with a custom one of yours by using xref:manual/systemproperties.adoc#log4j2.flowMessageFactory[the `log4j2.flowMessageFactory` property]. + +[IMPORTANT] +==== +Message factory implementations are expected to interpret formatting patterns containing placeholders denoted with `{}`. +For instance, the default message factory chooses between a xref:manual/messages.adoc#SimpleMessage[`SimpleMessage`] and a xref:manual/messages.adoc#ParameterizedMessage[`ParameterizedMessage`] depending on the presence of placeholders in the formatting pattern. + +If you want to change the placeholder style (e.g., switching from `{}` to `%s`), you should *not* replace the default message factory. +Because this will break the existing Log4j API calls using the standard placeholder style. +Instead, you can use link:../javadoc/log4j-api/org/apache/logging/log4j/LogManager.html[`LogManager`] methods accepting a message factory to create ``Logger``s with your custom message factory implementations. +==== From e505c169906cdfc91b90473963e85a64b3d72f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Tue, 2 Jul 2024 22:57:49 +0200 Subject: [PATCH 50/53] Improve wording Co-authored-by: Piotr P. Karwasz --- src/site/antora/modules/ROOT/pages/manual/extending.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/extending.adoc b/src/site/antora/modules/ROOT/pages/manual/extending.adoc index e7230a4ebdc..4b7716fa9b0 100644 --- a/src/site/antora/modules/ROOT/pages/manual/extending.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/extending.adoc @@ -115,7 +115,7 @@ You can use xref:manual/systemproperties.adoc#log4j2.configurationFactory[the `l === `LoggerConfig` link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/LoggerConfig.html[`LoggerConfig`] denotes the `Logger` configurations in a `Configuration`. -A custom `LoggerConfig` needs to suffice following conditions: +A custom `LoggerConfig` needs to satisfy the following conditions: * It needs to extend from `LoggerConfig` class * It needs to be declared as a <> From 242a7ed45a08a52ab79941cc9f83fafc171e7a33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Wed, 3 Jul 2024 09:32:18 +0200 Subject: [PATCH 51/53] Remove the mention of `elementType` --- .../modules/ROOT/pages/manual/appenders.adoc | 1 - .../modules/ROOT/pages/manual/filters.adoc | 1 - .../modules/ROOT/pages/manual/layouts.adoc | 1 - .../modules/ROOT/pages/manual/plugins.adoc | 27 +++++++------------ 4 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/appenders.adoc b/src/site/antora/modules/ROOT/pages/manual/appenders.adoc index cb0d394ea82..23ef540f734 100644 --- a/src/site/antora/modules/ROOT/pages/manual/appenders.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/appenders.adoc @@ -4496,7 +4496,6 @@ While annotating your appender with `@Plugin`, you need to make sure that * It has a unique `name` attribute across all available `Appender` plugins * The `category` attribute is set to link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Node.html#CATEGORY[`Node.CATEGORY`] -* The `elementType` attribute is set to link:../javadoc/log4j-core/org/apache/logging/log4j/core/Appender.html#ELEMENT_TYPE[`Appender.ELEMENT_TYPE`] Most appender implementation use *managers*, which model an abstraction owning the resources, such as an `OutputStream` or a socket. When a reconfiguration occurs a new appender will be created. diff --git a/src/site/antora/modules/ROOT/pages/manual/filters.adoc b/src/site/antora/modules/ROOT/pages/manual/filters.adoc index 3a65b191bec..ad8eaeff125 100644 --- a/src/site/antora/modules/ROOT/pages/manual/filters.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/filters.adoc @@ -1075,7 +1075,6 @@ While annotating your filter with `@Plugin`, you need to make sure that * It has a unique `name` attribute across all available `Filter` plugins * The `category` attribute is set to link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Node.html#CATEGORY[`Node.CATEGORY`] -* The `elementType` attribute is set to link:../javadoc/log4j-core/org/apache/logging/log4j/core/Filter.html#ELEMENT_TYPE[`Filter.ELEMENT_TYPE`] You can check out following files for examples: diff --git a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc index d5b85647753..956b8823a1f 100644 --- a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc @@ -1113,7 +1113,6 @@ While annotating your layout with `@Plugin`, you need to make sure that * It has a unique `name` attribute across all available `Layout` plugins * The `category` attribute is set to link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Node.html#CATEGORY[`Node.CATEGORY`] -* The `elementType` attribute is set to link:../javadoc/log4j-core/org/apache/logging/log4j/core/Layout.html#ELEMENT_TYPE[`Layout.ELEMENT_TYPE`] You can check out following files for examples: diff --git a/src/site/antora/modules/ROOT/pages/manual/plugins.adoc b/src/site/antora/modules/ROOT/pages/manual/plugins.adoc index 67dad5c9f25..fd00d59f907 100644 --- a/src/site/antora/modules/ROOT/pages/manual/plugins.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/plugins.adoc @@ -55,20 +55,11 @@ It is recommended to be distinct among plugins sharing the same `category`. A name used for grouping a set of plugins. `category` matching is case-sensitive. -`elementType` (optional):: -Name of the corresponding category of the xref:manual/configuration.adoc[Log4j configuration] file element this plugin belongs under. -You can omit this attribute if your plugin is not meant to be represented in a configuration file. +`elementType` (deprecated):: +We don't recommend the usage of `elementType` anymore. +Existing usages are kept for backward compatibility reasons with the legacy configuration syntax: `> for details. - -For clarity's sake, see following examples: - -* {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/DateLookup.java[`LowerLookup.java`] (uses only `category`) -* {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/CsvParameterLayout.java[`CsvParameterLayout.java`] (uses both `category` and `elementType`) +See {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/LowerLookup.java[`LowerLookup.java`] (a xref:manual/lookups.adoc[lookup] for lower-casing its input) for a simple example. .Click to read more on *name collision* and *overriding an existing plugin* [%collapsible] @@ -86,14 +77,14 @@ In short, name collisions are even more unpredictable in an OSGi environment. If your plugin needs to be represented by an element in a configuration file (such as an xref:manual/appenders.adoc[appender], xref:manual/layouts.adoc[layout], xref:manual/api.adoc#loggers[logger], or xref:manual/filters.adoc[filter]), following requirements must be met: * The `category` attribute of the `@Plugin` annotation must be set to link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Node.html#CATEGORY[`Node.CATEGORY`] (`Core`) -* The `elementType` attribute of the `@Plugin` annotation must be configured, if it belongs under a certain category of elements (appenders, layouts, etc.) * It must have a xref:declare-plugin-factory[plugin factory] -See {project-github-url}/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java[`JsonTemplateLayout.java`] for following details: +See {project-github-url}/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java[`JsonTemplateLayout.java`] for an example and notice these details: -* Layout's `@Plugin` annotation sets `category` and `elementType` to `Node.CATEGORY` and link:../javadoc/log4j-core/org/apache/logging/log4j/core/Layout.html#ELEMENT_TYPE[`Layout.ELEMENT_TYPE`], respectively -* All plugin declarations are provided with a `@PluginBuilderFactory`-annotated static method -* ``EventTemplateAdditionalField`` class' `@Plugin` annotation doesn't have an `elementType` attribute, even though it maps to a configuration file element +* There are two plugin declarations: `JsonTemplateLayout` and `JsonTemplateLayout.EventTemplateAdditionalField` +* Both plugin declarations +** Set the `category` attribute to `Node.CATEGORY` +** Provide a `@PluginBuilderFactory`-annotated static method [#declare-plugin-factory] === Declaring plugin factories From be4b992cc682006590eeee3550cd4fd86fa0a27d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Wed, 3 Jul 2024 09:47:38 +0200 Subject: [PATCH 52/53] Fix code typo --- src/site/antora/modules/ROOT/pages/manual/plugins.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/plugins.adoc b/src/site/antora/modules/ROOT/pages/manual/plugins.adoc index fd00d59f907..729a9d3cc23 100644 --- a/src/site/antora/modules/ROOT/pages/manual/plugins.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/plugins.adoc @@ -57,7 +57,7 @@ A name used for grouping a set of plugins. `elementType` (deprecated):: We don't recommend the usage of `elementType` anymore. -Existing usages are kept for backward compatibility reasons with the legacy configuration syntax: ` Date: Wed, 3 Jul 2024 10:02:59 +0200 Subject: [PATCH 53/53] Explain which class loader is used --- src/site/antora/modules/ROOT/pages/manual/plugins.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/site/antora/modules/ROOT/pages/manual/plugins.adoc b/src/site/antora/modules/ROOT/pages/manual/plugins.adoc index 729a9d3cc23..17ce5d8e72c 100644 --- a/src/site/antora/modules/ROOT/pages/manual/plugins.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/plugins.adoc @@ -252,7 +252,7 @@ dependencies { link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/util/PluginManager.html[`PluginManager`] is responsible for discovering plugins and loading their descriptions. It locates plugins by looking in following places in given order: -. Plugin descriptor files on the classpath. +. Plugin descriptor files on the classpath (using the class loader that loaded the `log4j-core` artifact). These files are generated automatically at compile-time by the Log4j plugin annotation processor. See <> for details.