diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/lookup/MainInputArgumentsJmxLookupTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/lookup/MainInputArgumentsJmxLookupTest.java index e140b6d25c3..2789d4d184f 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/lookup/MainInputArgumentsJmxLookupTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/lookup/MainInputArgumentsJmxLookupTest.java @@ -18,6 +18,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; +import java.util.Map; import org.junit.jupiter.api.Test; /** @@ -36,15 +37,40 @@ public static void main(final String[] args) { @Test public void testMap() { final JmxRuntimeInputArgumentsLookup lookup = JmxRuntimeInputArgumentsLookup.JMX_SINGLETON; - assertNull(lookup.lookup(null)); - assertNull(lookup.lookup("X")); - assertNull(lookup.lookup("foo.txt")); + String result2 = null; + if (null != null) { + final Map map2 = lookup.getMap(); + result2 = map2 == null ? null : map2.get(null); + } + assertNull(result2); + String result1 = null; + if ("X" != null) { + final Map map1 = lookup.getMap(); + result1 = map1 == null ? null : map1.get("X"); + } + assertNull(result1); + String result = null; + if ("foo.txt" != null) { + final Map map = lookup.getMap(); + result = map == null ? null : map.get("foo.txt"); + } + assertNull(result); } public void callFromMain() { final JmxRuntimeInputArgumentsLookup lookup = JmxRuntimeInputArgumentsLookup.JMX_SINGLETON; - assertNull(lookup.lookup(null)); - assertNull(lookup.lookup("X")); + String result1 = null; + if (null != null) { + final Map map1 = lookup.getMap(); + result1 = map1 == null ? null : map1.get(null); + } + assertNull(result1); + String result = null; + if ("X" != null) { + final Map map = lookup.getMap(); + result = map == null ? null : map.get("X"); + } + assertNull(result); // Eclipse adds -Dfile.encoding=Cp1252 // assertEquals("--file", lookup.lookup("0")); // assertEquals("foo.txt", lookup.lookup("1")); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/AbstractLookup.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/AbstractLookup.java index 1950fa96884..2e039d1430e 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/AbstractLookup.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/AbstractLookup.java @@ -16,6 +16,8 @@ */ package org.apache.logging.log4j.core.lookup; +import org.apache.logging.log4j.core.LogEvent; + /** * A default lookup for others to extend. * diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/EnvironmentLookup.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/EnvironmentLookup.java index b31055bde28..7836c946705 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/EnvironmentLookup.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/EnvironmentLookup.java @@ -27,12 +27,12 @@ public class EnvironmentLookup extends AbstractLookup { /** * Looks up the value of the environment variable. - * @param event The current LogEvent (is ignored by this StrLookup). + * * @param key the key to be looked up, may be null * @return The value of the environment variable. */ @Override - public String lookup(final LogEvent event, final String key) { + public String lookup(final LogEvent ignored, final String key) { return System.getenv(key); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JavaLookup.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JavaLookup.java index 11e74852a45..54c7fed56d0 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JavaLookup.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JavaLookup.java @@ -90,14 +90,12 @@ public String getVirtualMachine() { /** * Looks up the value of the environment variable. * - * @param event - * The current LogEvent (is ignored by this StrLookup). * @param key * the key to be looked up, may be null * @return The value of the environment variable. */ @Override - public String lookup(final LogEvent event, final String key) { + public String lookup(final LogEvent ignored, final String key) { switch (key) { case "version": return "Java version " + getSystemProperty("java.version"); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JmxRuntimeInputArgumentsLookup.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JmxRuntimeInputArgumentsLookup.java index ae9acbc3db5..45f216f1b40 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JmxRuntimeInputArgumentsLookup.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JmxRuntimeInputArgumentsLookup.java @@ -48,12 +48,7 @@ public JmxRuntimeInputArgumentsLookup(final Map map) { } @Override - public String lookup(final LogEvent event, final String key) { - return lookup(key); - } - - @Override - public String lookup(final String key) { + public String lookup(final LogEvent ignored, final String key) { if (key == null) { return null; } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JndiLookup.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JndiLookup.java index 33df36d2046..34acd58a003 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JndiLookup.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JndiLookup.java @@ -50,12 +50,11 @@ public JndiLookup() { /** * Looks up the value of the JNDI resource. * - * @param event The current LogEvent (is ignored by this StrLookup). * @param key the JNDI resource name to be looked up, may be null * @return The String value of the JNDI resource. */ @Override - public String lookup(final LogEvent event, final String key) { + public String lookup(final LogEvent ignored, final String key) { if (key == null) { return null; } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Log4jLookup.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Log4jLookup.java index 8b19de63402..b16d47b7681 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Log4jLookup.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Log4jLookup.java @@ -53,7 +53,7 @@ private static URI getParent(final URI uri) throws URISyntaxException { } @Override - public String lookup(final LogEvent event, final String key) { + public String lookup(final LogEvent ignored, final String key) { if (configuration != null) { final ConfigurationSource configSrc = configuration.getConfigurationSource(); final File file = configSrc.getFile(); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/LowerLookup.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/LowerLookup.java index 79fe30c74ec..474a4e28972 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/LowerLookup.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/LowerLookup.java @@ -25,26 +25,16 @@ * Converts values to lower case. The passed in "key" should be the value of another lookup. */ @Plugin(name = "lower", category = StrLookup.CATEGORY) -public class LowerLookup implements StrLookup { +public class LowerLookup extends AbstractLookup { /** * Converts the "key" to lower case. + * * @param key the key to be looked up, may be null * @return The value associated with the key. */ @Override - public String lookup(final String key) { + public String lookup(final LogEvent ignored, final String key) { return key != null ? toRootLowerCase(key) : null; } - - /** - * Converts the "key" to lower case. - * @param event The current LogEvent. - * @param key the key to be looked up, may be null - * @return The value associated with the key. - */ - @Override - public String lookup(final LogEvent event, final String key) { - return lookup(key); - } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/MainMapLookup.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/MainMapLookup.java index 144515c8131..0b7a9a16f90 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/MainMapLookup.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/MainMapLookup.java @@ -82,7 +82,7 @@ public static void setMainArguments(final String... args) { } @Override - public String lookup(final LogEvent event, final String key) { + public String lookup(final LogEvent ignored, final String key) { return MAIN_SINGLETON.getMap().get(key); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/ResourceBundleLookup.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/ResourceBundleLookup.java index dd99467a84a..9b95f14f87b 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/ResourceBundleLookup.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/ResourceBundleLookup.java @@ -39,14 +39,12 @@ public class ResourceBundleLookup extends AbstractLookup { * * For example: "com.domain.messages:MyKey". * - * @param event - * The current LogEvent. * @param key * the key to be looked up, may be null * @return The value associated with the key. */ @Override - public String lookup(final LogEvent event, final String key) { + public String lookup(final LogEvent ignored, final String key) { if (key == null) { return null; } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/StructuredDataLookup.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/StructuredDataLookup.java index 3b4324b04bb..05ef9e42079 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/StructuredDataLookup.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/StructuredDataLookup.java @@ -24,7 +24,7 @@ * Looks up keys from {@link org.apache.logging.log4j.message.StructuredDataMessage} log messages. */ @Plugin(name = "sd", category = StrLookup.CATEGORY) -public class StructuredDataLookup implements StrLookup { +public class StructuredDataLookup extends AbstractLookup { /** * Key to obtain the id of a structured message. @@ -36,16 +36,6 @@ public class StructuredDataLookup implements StrLookup { */ public static final String TYPE_KEY = "type"; - /** - * Returns {@code null}. This Lookup plugin does not make sense outside the context of a LogEvent. - * @param key The key to be looked up, may be null. - * @return {@code null} - */ - @Override - public String lookup(final String key) { - return null; - } - /** * Looks up the value for the key using the data in the LogEvent. * @param event The current LogEvent. diff --git a/log4j-docker/src/main/java/org/apache/logging/log4j/docker/DockerLookup.java b/log4j-docker/src/main/java/org/apache/logging/log4j/docker/DockerLookup.java index 6d0d2a97ad8..c52a5100b6c 100644 --- a/log4j-docker/src/main/java/org/apache/logging/log4j/docker/DockerLookup.java +++ b/log4j-docker/src/main/java/org/apache/logging/log4j/docker/DockerLookup.java @@ -95,7 +95,7 @@ public DockerLookup() { } @Override - public String lookup(final LogEvent event, final String key) { + public String lookup(final LogEvent ignored, final String key) { if (container == null) { return null; } diff --git a/log4j-jakarta-web/src/main/java/org/apache/logging/log4j/web/WebLookup.java b/log4j-jakarta-web/src/main/java/org/apache/logging/log4j/web/WebLookup.java index 18cbe26f8a8..35e47b821f8 100644 --- a/log4j-jakarta-web/src/main/java/org/apache/logging/log4j/web/WebLookup.java +++ b/log4j-jakarta-web/src/main/java/org/apache/logging/log4j/web/WebLookup.java @@ -30,7 +30,7 @@ public class WebLookup extends AbstractLookup { private static final String INIT_PARAM_PREFIX = "initParam."; @Override - public String lookup(final LogEvent event, final String key) { + public String lookup(final LogEvent ignored, final String key) { final ServletContext ctx = WebLoggerContextUtils.getServletContext(); if (ctx == null) { return null; diff --git a/src/site/antora/modules/ROOT/examples/manual/lookups/MainArgsExample.java b/src/site/antora/modules/ROOT/examples/manual/lookups/MainArgsExample.java new file mode 100644 index 00000000000..3cb4b4af890 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/lookups/MainArgsExample.java @@ -0,0 +1,43 @@ +/* + * 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 org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public final class MainArgsExample implements Runnable { + + // tag::usage[] + private final Logger logger = LogManager.getLogger(); // <1> + + public static void main(final String[] args) { + try { // <2> + Class.forName("org.apache.logging.log4j.core.lookup.MainMapLookup") + .getDeclaredMethod("setMainArguments", String[].class) + .invoke(null, (Object) args); + } catch (final ReflectiveOperationException e) { + // Log4j Core is not used. + } + new MainArgsExample().run(); + } + // end::usage[] + + @Override + public void run() { + logger.info("Hello `main` lookup!"); + } +} diff --git a/src/site/antora/modules/ROOT/examples/manual/lookups/mainArgs.json b/src/site/antora/modules/ROOT/examples/manual/lookups/mainArgs.json new file mode 100644 index 00000000000..1b731b0f494 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/lookups/mainArgs.json @@ -0,0 +1,31 @@ +{ + "Configuration": { + "Properties": { + "Property": [ // <1> + { + "name": "--logfile", + "value": "logs/app.log" + }, + { + "name": "--loglevel", + "value": "INFO" + } + ] + }, + "Appenders": { + "File": { + "fileName": "${main:\\--logfile}", // <2> + "name": "FILE", + "JsonTemplateLayout": {} + } + }, + "Loggers": { + "Root": { + "level": "${main:\\--loglevel", // <2> + "AppenderRef": { + "ref": "FILE" + } + } + } + } +} \ No newline at end of file diff --git a/src/site/antora/modules/ROOT/examples/manual/lookups/mainArgs.properties b/src/site/antora/modules/ROOT/examples/manual/lookups/mainArgs.properties new file mode 100644 index 00000000000..af488be152d --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/lookups/mainArgs.properties @@ -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. +# +## +# <1> +property.--logfile = logs/app.log +property.--loglevel = INFO + +appender.0.type = File +# <2> +appender.0.fileName = ${main:\\--logfile} +appender.0.name = FILE + +# <2> +rootLogger.level = ${main:\\--loglevel} +rootLogger.appenderRef.0.ref = FILE diff --git a/src/site/antora/modules/ROOT/examples/manual/lookups/mainArgs.xml b/src/site/antora/modules/ROOT/examples/manual/lookups/mainArgs.xml new file mode 100644 index 00000000000..7982925d596 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/lookups/mainArgs.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + diff --git a/src/site/antora/modules/ROOT/examples/manual/lookups/mainArgs.yaml b/src/site/antora/modules/ROOT/examples/manual/lookups/mainArgs.yaml new file mode 100644 index 00000000000..dcbc3b1ab53 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/lookups/mainArgs.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: + Properties: # <1> + Property: + - name: "--logfile" + value: "logs/app.log" + - name: "--loglevel" + value: "INFO" + Appenders: + File: + fileName: "${main:\\--logfile}" # <2> + name: "FILE" + JsonTemplateLayout: {} + Loggers: + Root: + level: "${main:\\--loglevel}" # <2> + AppenderRef: + ref: "FILE" diff --git a/src/site/antora/modules/ROOT/pages/manual/appenders.adoc b/src/site/antora/modules/ROOT/pages/manual/appenders.adoc index d37ce1139f4..0700ff1c458 100644 --- a/src/site/antora/modules/ROOT/pages/manual/appenders.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/appenders.adoc @@ -39,13 +39,16 @@ Appenders always have a name so that they can be referenced from Loggers. In the tables below, the "Type" column corresponds to the Java type expected. For non-JDK classes, these should usually be in link:../javadoc/log4j-core/index.html[Log4j Core] unless otherwise noted. +[#commons-concerns] +== Common concerns + [#common-properties] -== Common properties +=== Common properties These configuration attributes are shared by multiple appenders: [#immediateFlush] -=== `immediateFlush` +==== `immediateFlush` [cols="1h,5"] |=== @@ -69,6 +72,55 @@ 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. ==== +[#runtime-evaluation] +=== Runtime evaluation of attributes + +The following configuration attributes are also evaluated at runtime, +so can contain escaped `$$+{...}+` expressions. + +.List of attributes evaluated at runtime +[cols="1,1,1,1"] +|=== +| Component | Parameter | Event type | Evaluation context + +| <> +| <> +| Log event +| xref:manual/lookups.adoc#global-context[_global_] + +| <> +| <> +| Log event +| xref:manual/lookups.adoc#event-context[_log event_] + +| <> +| <> +| Log event +| xref:manual/lookups.adoc#event-context[_log event_] + +| <> +| <> +| Log event +| xref:manual/lookups.adoc#global-context[_global_] + +| <> +| <> +| Rollover +| xref:manual/lookups.adoc#global-context[_global_] + +| <> +| `basePath` +| Rollover +| xref:manual/lookups.adoc#global-context[_global_] + +|=== + +The <> component of the <> is special: its children are evaluated at runtime, +but they are **not** evaluated at configuration time. +Inside the `Route` component you **should not** use escaped `$$+{...}+` expressions, but only unescaped `$+{...}+` expressions. + +See xref:manual/configuration.adoc#lazy-property-substitution[runtime property substitution] for more details. + [#collection] == Collection @@ -1541,7 +1593,7 @@ public class JpaLogEntity extends AbstractLogEventWrapperEntity { return this.id; } - public void setId(long id) { + public void setId(final long id) { this.id = id; } @@ -1611,8 +1663,14 @@ Optional, default is 0 (infinite timeout). |readTimeoutMillis |integer |The socket read timeout in milliseconds. Optional, default is 0 (infinite timeout). -|headers |Property[] |Additional HTTP headers to use. The values support -xref:manual/lookups.adoc[lookups]. +| [[HttpAppender-element-headers]]headers +| Property[] +| Additional HTTP headers to use. + +The values support +xref:manual/configuration.adoc#lazy-property-substitution[runtime property substitution] +and are evaluated in a +xref:manual/lookups.adoc#global-context[_global context_]. |ignoreExceptions |boolean |The default is `true`, causing exceptions encountered while appending events to be internally logged and then @@ -1648,7 +1706,7 @@ Here is a sample HttpAppender configuration snippet: The KafkaAppender logs events to an https://kafka.apache.org/[Apache Kafka] topic. Each log event is sent as a Kafka record. -[width="100%",options="header"] +[cols="1m,1m,5"] |=== |Parameter Name |Type |Description @@ -1656,9 +1714,14 @@ Each log event is sent as a Kafka record. |String |The Kafka topic to use. Required. -|key +|[[KafkaAppender-attr-key]]key |String -|The key that will be sent to Kafka with every message. Optional value defaulting to `null`. Any of the xref:manual/lookups.adoc[Lookups] can be included. +|The key that will be sent to Kafka with every message. + +Supports +xref:manual/configuration.adoc#lazy-property-substitution[runtime property substitution] +and is evaluated in the +xref:manual/lookups.adoc#event-context[_context of the current event_]. |filter |Filter @@ -1852,6 +1915,22 @@ NoSQLObject>> |_Required._ The NoSQL provider that provides connections to the chosen NoSQL database. |======================================================================= +.`NoSqlAppender` -- nested elements +[cols="1m,1,4",id=NoSqlAppender-element-keyValuePairs] +|=== +| Type | Multiplicity | Description + +| xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-core_org-apache-logging-log4j-core-util-KeyValuePair[`KeyValuePair`] +| Zero or more +a| Adds a simple key/value field to the NoSQL object. + +The `value` attribute of the pair supports +xref:manual/configuration.adoc#lazy-property-substitution[runtime property substitution] +using the +xref:manual/lookups.adoc#event-context[current event as context]. + +|=== + You specify which NoSQL provider to use by specifying the appropriate configuration element within the `` element. The only type currently supported is ``. To create your custom provider, read the JavaDoc for the `NoSQLProvider`, `NoSQLConnection`, and `NoSQLObject` classes and the documentation about creating Log4j plugins. @@ -2391,6 +2470,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. @@ -2398,11 +2478,18 @@ The properties will not be added to the actual ThreadContext Map. The property values may contain variables that will be evaluated when the configuration is processed as well as when the event is logged. [cols="20%,20%,60%",options="header",] -|======================================================================= +|=== |Parameter Name |Type |Description -|properties |Property[] |One of more Property elements to define the -keys and values to be added to the ThreadContext Map. -|======================================================================= + +|[[PropertiesRewritePolicy-element-properties]]properties +|Property[] +|One of more Property elements to define the keys and values to be added to the ThreadContext Map. + +The `value` attribute of each `Property` element supports +xref:manual/configuration.adoc#lazy-property-substitution[runtime property substitution] +in the +xref:manual/lookups.adoc#global-context[_global context_]. +|=== The following configuration shows a RewriteAppender configured to add a product key and its value to the MapMessage: @@ -2524,15 +2611,32 @@ CompositeFilter. |fileName |String |The name of the file to write to. If the file, or any of its parent directories, do not exist, they will be created. -|filePattern |String |The pattern of the file name of the archived log -file. The format of the pattern is dependent on the `RolloverPolicy` that -is used. The DefaultRolloverPolicy will accept both a date/time pattern +| [[RollingFileAppender-attr-filePattern]]filePattern +| String +a| The pattern of the archived log filename. + +The format of the pattern is dependent on the `RolloverStrategy` that is used. +The standard rollover strategies accept: + +* xref:manual/configuration.adoc#lazy-property-substitution[runtime lookups], +which are evaluated in +xref:manual/lookups.adoc#global-context[a global context]. +* a `%d++{...}++` pattern, functionally identical to the +xref:manual/pattern-layout.adoc#converter-date[homonymous `PatternLayout` pattern] +except it uses the actual or inferred timestamp of the **previous** rollover. +* a `%i` pattern that expands to the computed index for the archived file. + +All patterns accept also format specifiers, e.g. `%03i` prints the index as zero-padded number with 3 digits. + +will accept both a date/time pattern compatible with https://docs.oracle.com/javase/{java-target-version}/docs/api/java/text/SimpleDateFormat.html[`SimpleDateFormat`] -and/or a %i which represents an integer counter. The integer counter +and/or a %i which represents an integer counter. +The integer counter allows specifying a padding, like %3i for space-padding the counter to 3 digits or (usually more useful) %03i for zero-padding the counter to -3 digits. The pattern also +3 digits. +The pattern also supports interpolation at runtime so any of the Lookups (such as the xref:manual/lookups.adoc#DateLookup[DateLookup]) can be included in the pattern. @@ -3703,6 +3807,15 @@ If the `Route` contains a ref attribute then the `Route` will reference an `Appe If the `Route` contains an `Appender` definition then an `Appender` will be created within the context of the `RoutingAppender` and will be reused each time a matching `Appender` name is referenced through a `Route`. +[WARNING] +==== +Lookups in the **children** of the `Route` component are **not** evaluated at configuration time. +The substitution is delayed until the `Route` element is evaluated. +This means that `$+{...}+` expression **should not** be escaped as `$$+{...}+`. + +See xref:manual/configuration.adoc#lazy-property-substitution[lazy property substitution] for more details. +==== + This script is passed the following variables: .RoutingAppender Routes Script Parameters diff --git a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc index fbb82f3a5db..92161ff9f17 100644 --- a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc @@ -286,6 +286,7 @@ Properties:: include::example$manual/configuration/main-elements.properties[lines=17..-1] ---- ==== + <1> Configures a console appender named `CONSOLE` with a pattern layout. <2> Configures a file appender named `MAIN` with a JSON template layout. <3> Configures a file appender named `DEBUG_LOG` with a pattern layout. @@ -618,8 +619,8 @@ Loggers can emit additional context data that will be integrated with other cont ==== The `value` of each property is subject to <> twice: -* when the configuration is loaded, -* each time a log event is generated. +* when the configuration is loaded, it is evaluated in the xref:manual/lookups.adoc#global-context[global context]. +* each time a log event is generated, it is evaluated in the xref:manual/lookups.adoc#event-context[context of the event]. Therefore, if you wish to insert a value that changes in time, you must double the `$` sign, as shown in the example below. ==== @@ -771,8 +772,8 @@ If `name` starts with a hyphen `-` (e.g. `-variable`), it must be escaped with a + The most common lookup prefixes are: + -* `sys` for Java system properties (see xref:manual/lookups.adoc#system-properties-lookup[System Properties lookup]), -* `env` for environment variables (see xref:manual/lookups.adoc#environment-lookup[Environment lookup]). +* `sys` for Java system properties (see xref:manual/lookups.adoc#SystemPropertiesLookup[System Properties lookup]), +* `env` for environment variables (see xref:manual/lookups.adoc#EnvironmentLookup[Environment lookup]). The above expansions have a version with an additional `default` value that is **expanded** if the lookup fails: @@ -862,9 +863,8 @@ and the OS environment variable `FOO` has a value of `environment`, Log4j will e [WARNING] ===== For security reasons, if the **expansion** of a `${...}` expression contains other expressions, these will **not** be expanded. -The only exception to this rule is the expansion of properties in the `Properties` container. -Properties defined in the `Properties` container can depend on each other. +Properties defined in the `Properties` container, however, can depend on each other. If your configuration contains, for example: [tabs] @@ -908,26 +908,44 @@ Therefore, the value of the `logging.file` property will be: ===== [id=lazy-property-substitution] -=== Lazy property substitution +=== Runtime property substitution -For most attributes, property substitution is performed only once at **configuration time**, but there are two categories of exceptions to this rule: +For most attributes, property substitution is performed only once at **configuration time**, +but there are exceptions to this rule: some attributes are **also** evaluated when a component-specific event occurs. -* Some attributes are **also** evaluated when a component-specific event occurs. -For example -<> -and the `pattern` attribute of the example below are evaluated at each log event, while the `filePattern` attribute of a -xref:manual/appenders.adoc#rollingfileappender[rolling file appender] -is evaluated at each rollover. -+ In this case: -** If you want property substitution to happen only once, use one dollar sign, e.g., `${date:HH:mm:ss}`. -** If you want property substitution to happen at each cyclic event, you use two dollar signs, e.g., `$${date:HH:mm:ss}` +** If you want property substitution to happen at configuration time, use one dollar sign, e.g., `${date:HH:mm:ss}`. +** If you want property substitution to happen at runtime, you use two dollar signs, e.g., `$${date:HH:mm:ss}` -* Other components defer the evaluation of their child components. -In this case, you only need one dollar `$` sign. -+ -This case happens for the children of the `Route` element below: +The list of attributes that support runtime property substitution is: + +* The `value` attribute of <> of a logger configuration. +* The +xref:manual/pattern-layout.adoc#plugin-attr-pattern[`pattern`] attribute of the xref:manual/pattern-layout.adoc[]. +* Some values of a JSON template used by xref:manual/json-template-layout.adoc[]. +See xref:manual/json-template-layout.adoc#property-substitution-in-template[property substitution in JSON Template Layout] +for more details. +* The appender attributes listed in xref:manual/appenders.adoc#runtime-evaluation[runtime property substitution in appenders]. + +[WARNING] +==== +Certain lookups might behave differently when they are expanded at runtime. +See xref:manual/lookups.adoc#evaluation-contexts[lookup evaluation contexts] for details. +==== + +[NOTE] +==== +The +xref:manual/appenders.adoc#Routes[`Route`] component of the +xref:manual/appenders.adoc#RoutingAppender[`RoutingAppender`] +is a different case altogether. +The attributes of its children are expanded at runtime, but are not expanded at configuration time. + +Inside the `Route` component you **should not** use escaped `$$+{...}+` expressions, but only unescaped `$+{...}+` expressions. +==== + +The complete spectrum of behaviors concerning runtime property substitution is given by the routing appender example below: [tabs] ==== @@ -964,9 +982,9 @@ include::example$manual/configuration/routing.properties[tag=appender] ---- ==== -<1> The `pattern` attribute is evaluated at configuration time and also each time a log event is routed. +<1> The `pattern` attribute is evaluated at configuration time, and also each time a log event is routed. Therefore, the dollar `$` sign needs to be escaped. -<2> All the attributes inside the `File` element have a **deferred** evaluation, therefore they need only one `$` sign. +<2> All the attributes of children of the `Route` element have a **deferred** evaluation. Therefore, they need only one `$` sign. [id=arbiters] == [[Arbiters]] Arbiters @@ -1066,6 +1084,7 @@ Properties:: include::example$manual/configuration/arbiters-select.properties[tag=select] ---- ==== + <1> If the Java system property `env` has a value of `dev`, a Pattern Layout will be used. <2> Otherwise, a JSON Template Layout will be used. diff --git a/src/site/antora/modules/ROOT/pages/manual/lookups.adoc b/src/site/antora/modules/ROOT/pages/manual/lookups.adoc index 76aaecb77de..191fa2e1445 100644 --- a/src/site/antora/modules/ROOT/pages/manual/lookups.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/lookups.adoc @@ -16,63 +16,298 @@ //// = Lookups -Lookups provide a way to add values to the Log4j configuration at -arbitrary places. They are a particular type of Plugin that implements -the -link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/StrLookup.html[`StrLookup`] -interface. Information on how to use Lookups in configuration files can -be found in the xref:manual/configuration.adoc#PropertySubstitution[Property -Substitution] section of the xref:manual/configuration.adoc[Configuration] -page. +Log4j Core provides a flexible and extensible property substitution system loosely based on the +https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/StringSubstitutor.html[Commons Text `StringSubstitutor`]. + +The property substitution system is composed of two kinds of elements: + +* A string interpolation engine (xref:manual/architecture.adoc#StrSubstitutor[`StrSubstitutor`]) that evaluates `$+{...}+` expressions. +See xref:manual/configuration.adoc#property-substitution[property substitution] for more details. +* A set of +xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-core_org-apache-logging-log4j-core-lookup-StrLookup[`StrLookup`] +plugins that provide values for simple `$+{prefix:key}+` expressions. + +`StrLookup` is a simple map-like interface. +The main difference between a map and `StrLookup` is +that the latter can compute the value of a key programmatically in a global context or in the context of log event. + +[#evaluation-contexts] +== Evaluation contexts + +Each lookup has an associated prefix, +and Log4j can evaluate it in one of the following ways: + +[#global-context] +Global context:: +In a global context Log4j evaluates `$+{prefix:key}+` expressions by calling +link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/StrLookup.html#lookup(java.lang.String)[`lookup("key")`] +on the lookup associated to `prefix`. +The result of this call only takes into account the global state of the system. ++ +The global context is used to expand the attributes of a +xref:manual/configuration.adoc[configuration file]. + +[#event-context] +Log event context:: +In the context of a log event `event`, Log4j evaluates `$+{prefix:key}+` expressions by calling +link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/StrLookup.html#lookup(org.apache.logging.log4j.core.LogEvent,java.lang.String)[`lookup(event, "key")`] on the lookup associated to `prefix`. +The result of this call might take into account the contents of the log event, besides the global state of the system. + +The xref:manual/pattern-layout.adoc#plugin-attr-pattern[`pattern`] attribute of `PatternLayout` is an example of attribute that supports both evaluation contexts: + +* During the configuration process the `$+{...}+` expressions are evaluated using a global context. +* For each log event the `$$+{...}+` expressions are evaluated, using the log event as context. + +An example of lookup that can be used in both a global and event context is the `$+{date:...}+` lookup: + +* When used in a global context, it formats the **current** timestamp obtained through +https://docs.oracle.com/javase/{java-target-version}/docs/api/java/lang/System.html#currentTimeMillis--[`System.currentTimeMillis()`]. +* When used in the context of an event, it formats the **event** timestamp obtained through +link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html#getTimeMillis()[`LogEvent.getTimeMillis()`]. + +[#collection] +== Collection + +Log4j Core provides many lookups out-of-the-box, which can be categorized into three non-disjoint groups: + +* A large group of lookups is available in a global context and provides results, which do not vary in time. +These lookups can be safely used in eagerly evaluated properties of a +xref:manual/configuration.adoc[configuration file] +using the `${prefix:key}` syntax: ++ +[#global-context-immutable-list] +.Immutable lookups available in the global context +[cols="1,2m,5"] +|=== +| Prefix | Dependency | Data source -[#ContextMapLookup] -== Context Map Lookup +| <> +| +| A Java +https://docs.oracle.com/javase/{java-target-version}/docs/api/java/util/ResourceBundle.html[resource bundle] -The ContextMapLookup allows applications to store data in the Log4j -ThreadContext Map and then retrieve the values in the Log4j -configuration. In the example below, the application would store the -current user's login id in the ThreadContext Map with the key "loginId". -During initial configuration processing the first '$' will be removed. -The PatternLayout supports interpolation with Lookups and will then -resolve the variable for each event. Note that the pattern -"%X\{loginId}" would achieve the same result. +| <> +| log4j-docker +| Docker container + +| <> +| +| Environment variables + +| <> +| +| JVM characteristics + +| <> +| +| Location of Log4j configuration file + +| <> +| +| JVM application arguments + +| <> +| +| Returns `key` if a marker named `key` exists + +| <> +| log4j-spring-boot +| Spring Boot 2.x environment. + +| <> +| +| Java system properties + +| <> +| log4j-jakarta-web +| Jakarta +https://jakarta.ee/specifications/servlet/6.0/apidocs/jakarta.servlet/jakarta/servlet/servletcontext[`ServletContext`]. + +|=== + +* A second group of lookups either gives results that vary in time or needs to be evaluated in the context of a log event. +These lookups should be evaluated lazily using the `$$+{prefix:key}+` syntax. +See +xref:manual/configuration.adoc#lazy-property-substitution[lazy property substitution] +for details. ++ +[#global-context-mutable-list] +.Mutable lookups available in the global context +[cols="1,2m,5"] +|=== +| Prefix | Dependency | Data source + +| <> +| +| xref:manual/thread-context.adoc[] + +| <> +| +| Current timestamp + +| <> +| +| JNDI + +|=== ++ +[#event-context-list] +.Lookups available in the context of a log event +[cols="1,2m,5"] +|=== +| Prefix | Dependency | Data source + +| <> +| +| Log event +link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html#getContextData()[context data] + +| <> +| +| Log event +link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html#getTimeMillis()[timestamp] + +| <> +| +| link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html[Log event] + +| <> +| +| xref:manual/messages.adoc#MapMessage[`MapMessage`] + +| <> +| +| Log event +link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html#getMarker()[marker] + +| <> +| +| xref:manual/messages.adoc#StructuredDataMessage[`StructuredDataMessage`] + +|=== + +* Log4j Core also provides two lookups that can be used to modify the result of another lookup: ++ +[#helper-lookups-list] +.Helper lookups +[cols="1,2m,5"] +|=== +| Prefix | Dependency | Description + +| <> +| +| It converts the supplied key to lowercase + +| <> +| +| It converts the supplied key to uppercase + +|=== + +[#ResourceBundleLookup] +=== Resource Bundle Lookup + +[cols="1h,4"] +|=== +| Lookup ID | `bundle` + +| Context | _global_ + +| Key format +a| ` ":" ` + +where: + +`baseName`:: +the base name of a resource bundle (see +https://docs.oracle.com/javase/8/docs/api/java/util/ResourceBundle.html[`ResourceBundle`]) + +`key`:: +the key for the resource string +|=== + +The Resource Bundle lookup retrieves strings from Java Resource bundles, e.g.: -[source,xml] ---- - - - %d %p %c{1.} [%t] $${ctx:loginId} %m%n - - +${bundle:org.example.Main:errorMessage} ---- +[#ContextMapLookup] +=== Context Map Lookup + +[cols="1h,4"] +|=== +| Lookup ID | `ctx` +| Context | _global_ and _log event_ +| Key format | _any_ `String` +|=== + +The Context Map lookup can be used in two different contexts: + +Global context:: +If used in the global context, it uses the +xref:manual/thread-context.adoc[] +to retrieve data. ++ +[WARNING] +==== +When used in this context +xref:manual/thread-context.adoc#custom-ContextDataProvider[custom context data providers] +are not supported. +==== + +Log event context:: +In the context of an event, the Context Map lookup uses the +Log event +link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html#getContextData()[context map data] +of a log event to resolve the key. +xref:manual/thread-context.adoc#custom-ContextDataProvider[Custom context data providers] are therefore supported. ++ +[TIP] +==== +Whenever conversion patterns are allowed, a `$$+{ctx:key}+` lookup is equivalent to an +xref:manual/pattern-layout.adoc#converter-thread-context-map[`%X\{key}` 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 -date format string that is valid for -https://docs.oracle.com/javase/{java-target-version}/docs/api/java/text/SimpleDateFormat.html[`SimpleDateFormat`]. -The current date, or the date associated with the current log event will -be formatted as specified. +[cols="1h,4"] +|=== +| Lookup ID | `date` -[source,xml] ----- - - - %d %p %c{1.} [%t] %m%n - - - ----- +| Context | _global_ and _log event_ + +| Key format +| https://docs.oracle.com/javase/{java-target-version}/docs/api/java/text/SimpleDateFormat.html[`SimpleDateFormat`] pattern + +|=== + +The Date Lookup formats a timestamp, using the supplied key as format. +The timestamp used depends on the context: + +Global context:: +When used in a global context, the timestamp used is the current system timestamp as returned by +https://docs.oracle.com/javase/8/docs/api/java/lang/System.html#currentTimeMillis--[`System.currentTimeMillis()`]. + +Log event context:: +When used in the context of a log event, the timestamp of the log event is used. ++ +[TIP] +==== +Whenever conversion patterns are allowed, a `$$+{date:format}+` lookup is equivalent to a +xref:manual/pattern-layout.adoc#converter-date[`%d\{format}` pattern]. +==== [#DockerLookup] -== Docker Lookup +=== Docker Lookup [cols="1h,4"] |=== -|Lookup ID |`docker` -|Dependency |xref:log4j-docker.adoc[] +| Lookup ID | `docker` +| Context | _global_ +| Key format | _enumeration_ +| Dependency | xref:log4j-docker.adoc[] |=== Docker Lookup queries https://docs.docker.com/engine/api/[the API of the Docker Engine] running your container. @@ -89,561 +324,475 @@ It supports the retrieval of following container attributes: |shortImageId |The first 12 characters of the container image ID |=== -[id=environment-lookup] -== [[EnvironmentLookup]] Environment Lookup +Additional runtime dependencies are required for using Docker Lookup: -The EnvironmentLookup allows systems to configure environment variables, -either in global files such as /etc/profile or in the startup scripts -for applications, and then retrieve those variables from within the -logging configuration. The example below includes the name of the -currently logged in user in the application log. +include::partial$manual/dependencies-log4j-docker.adoc[] -[source,xml] ----- - - - %d %p %c{1.} [%t] $${env:USER} %m%n - - ----- +[#EnvironmentLookup] +=== Environment Lookup -This lookup also supports default value syntax. In the sample below, -when the `USER` environment variable is undefined, the default value -`jdoe` is used: +[cols="1h,4"] +|=== +| Lookup ID | `env` +| Context | _global_ +| Key format | _any_ `String` +|=== + +The Environment Lookup retrieves the value of the +https://docs.oracle.com/javase/{java-target-version}/docs/api/java/lang/System.html#getenv-java.lang.String-[OS environment variable] +associated with the key. -[source,xml] ----- - - - %d %p %c{1.} [%t] $${env:USER:-jdoe} %m%n - - ----- [#EventLookup] -== Event Lookup +=== Event Lookup + +[cols="1h,4"] +|=== +| Lookup ID | `event` +| Context | _log event_ +| Key format | _enumeration_ +|=== -The EventLookup provides access to fields within the log event from the configuration. +The Event Lookup provides access to fields of the current log event. +It supports the retrieval of the following event attributes: [cols="1m,4a"] |=== |Key |Description |Exception -|Returns the simple class name of the Exception, if one is included in the event. +|Simple class name of the exception, if one is present. |Level -|Returns the logging Level of the event. +|xref:manual/customloglevels.adoc[Logging level] of the event |Logger -|Returns the name of the Logger. +|Name of the logger |Marker -|Returns the name of the Marker associated with the log event, if one is present. +|xref:manual/markers.adoc[Marker] associated with the log event, if one is present. |Message -|Returns the formatted Message string. +|Formatted xref:manual/messages.adoc[`Message`] |ThreadId -|Returns the thread id associated with the log event. +|Thread id associated with the log event |ThreadName -|Returns the name of the thread associate with the log event. +|Name of the thread associated with the log event |Timestamp -|Returns the time in milliseconds when the event occurred. - +|UNIX timestamp in milliseconds of the log event |=== -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] ----- - - - - - - - - - - - - - - - %d %p %c{1.} [%t] %m%n - - - - - - - - - - - - - - - - ----- - [#JavaLookup] -== Java Lookup +=== Java Lookup + +[cols="1h,4"] +|=== +| Lookup ID | `java` +| Context | _global_ +| Key format | _enumeration_ +|=== -The JavaLookup allows Java environment information to be retrieved in -convenient preformatted strings using the `java:` prefix. +The Java Lookup allows retrieving information about the Java environment the application is using. +The following keys are supported -[cols="1m,4a"] +[cols="1m,2,6m"] |=== -|Key |Description +|Key |Description |Example |version -|The short Java version, like: - -`Java version 1.7.0_67` +|Short Java version +|Java version 21.0.3 |runtime -|The Java runtime version, like: - -`Java(TM) SE Runtime Environment (build 1.7.0_67-b01) from Oracle Corporation` +|Java runtime version +|OpenJDK Runtime Environment (build 21.0.3+9-LTS) from Eclipse Adoptium |vm -|The Java VM version, like: - -`Java HotSpot(TM) 64-Bit Server VM (build 24.65-b04, mixed mode)` +|Java VM version +|OpenJDK 64-Bit Server VM (build 21.0.3+9-LTS, mixed mode, sharing) |os -|The OS version, like: - -`Windows 7 6.1 Service Pack 1, architecture: amd64-64` +|OS version +|Linux 6.1.0-18-amd64, architecture: amd64-64 |locale -|Hardware information, like: - -`default locale: en_US, platform encoding: Cp1252` +|System locale and file encoding +|default locale: en_US, platform encoding: UTF-8 |hw -|System locale and file encoding information, like: +|Hardware information +|processors: 32, architecture: amd64-64, instruction sets: amd64` + +|=== -`processors: 4, architecture: amd64-64, instruction sets: amd64` +[#JndiLookup] +=== JNDI Lookup +[cols="1h,4"] |=== +| Lookup ID | `jndi` +| Context | _global_ -For example: +| Key format +| JNDI https://docs.oracle.com/javase/{java-target-version}/docs/api/javax/naming/Name.html[`Name`] +|=== -[source,xml] +[IMPORTANT] +==== +As of Log4j `2.17.0` you need to enable the JNDI lookup **explicitly** by setting the +xref:manual/systemproperties.adoc#log4j2.enableJndiLookup[`log4j2.enableJndiLookup`] +configuration property to `true`. +==== + +The JNDI Lookup retrieves the value of an environment entry from JNDI. +Only the `java:` protocol is supported. +If the key does not have a protocol, `java:comp/env` is prepended. + +As an example, to retrieve the value of `java:comp/env/app_name` you can use: + +[source] ---- - - - %d %m%n - - +$${jndi:app_name} ---- -[#JndiLookup] -== JNDI Lookup +[NOTE] +==== +Android does not support JNDI. +==== -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 -xref:manual/systemproperties.adoc#log4j2.enableJndiLookup[`log4j2.enableJndiLookup`] system property. +[#Log4jLookup] +=== Configuration Location Lookup + +[cols="1h,4"] +|=== +| Lookup ID | `log4j` +| Context | _global_ +| Key format | _enumeration_ +|=== -The JndiLookup allows variables to be retrieved via JNDI. By default the -key will be prefixed with java:comp/env/, however if the key contains a -":" no prefix will be added. +The Configuration Location Lookup supports two keys: -By default the JNDI Lookup only supports the java, ldap, and ldaps protocols or no protocol. Additional -protocols may be supported by specifying them on the ``log4j2.allowedJndiProtocols`` property. -When using LDAP Java classes that implement the Referenceable interface are not supported for security -reasons. Only the Java primative classes are supported by default as well as any classes specified by the -``log4j2.allowedLdapClasses`` property. When using LDAP only references to the local host name -or ip address are supported along with any hosts or ip addresses listed in the -``log4j2.allowedLdapHosts`` property. +[cols="1m,4"] +|=== +|Key |Description -[source,xml] ----- - - - %d %p %c{1.} [%t] $${jndi:logging/context-name} %m%n - - ----- +|configLocation +|Returns the location of the configuration file as an absolute file path or URI. -*Java's JNDI module is not available on Android.* +|configParentLocation +|Returns the location of the folder containing the configuration file as an absolute file path or URI. +|=== -[#JmxRuntimeInputArgumentsLookup] -== JVM Input Arguments Lookup (JMX) +[#LowerLookup] +=== Lower Lookup -Maps JVM input arguments -- but not _main_ arguments -- using JMX to -acquire the JVM arguments. +[cols="1h,4"] +|=== +| Lookup ID | `lower` +| Context | _global_ +| Key format | _any_ `String` +|=== -Use the prefix `jvmrunargs` to access JVM arguments. +The Lower Lookup converts the passed in argument to lowercase. -See the Javadocs for -https://docs.oracle.com/javase/{java-target-version}/docs/api/java/lang/management/RuntimeMXBean.html#getInputArguments--[`java.lang.management.RuntimeMXBean.getInputArguments()`]. +Presumably, the value will be the result of a nested lookup as in the example: -*Java's JMX module is not available on Android or on Google App Engine.* +[source] +---- +${lower:${sys:appname}} +---- -[#KubernetesLookup] -== Kubernetes Lookup +[#MainMapLookup] +=== Main Arguments Lookup [cols="1h,4"] |=== -|Lookup ID |`k8s` -|Dependency |{log4j-kubernetes-url}[Log4j Kubernetes of Fabric8] +| Lookup ID | `main` +| Context | _global_ +| Key format | either an `int` or a `String` |=== -Kubernetes Lookup queries https://kubernetes.io/docs/concepts/overview/kubernetes-api/[the Kubernetes API] to retrieve certain information about the current container and its environment. -Kubernetes Lookup is distributed as a part of Fabric8's Kubernetes Client, refer to {log4j-kubernetes-url}[its website] for details. +[IMPORTANT] +==== +This lookup requires a setup step: +your application needs to call +link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/MainMapLookup.html#setMainArguments(java.lang.String...)[`MainMapLookup#setMainArguments()`] +and pass as argument the arguments received by the application. +==== -[#Log4jConfigLookup] -== Log4j Configuration Location Lookup +The Main Arguments Lookup provides a way to query the arguments received by your application. +It supports two kinds of keys: -Log4j configuration properties. The expressions -`${log4j:configLocation}` and `${log4j:configParentLocation}` -respectively provide the absolute path to the log4j configuration file -and its parent folder. +* if the key is an integer, e.g. `${main:0}`, it is interpreted as 0-based index in the argument array. +* if the key is a `String`, e.g. `${main:foo}`, the argument that follows `foo` in the argument array is returned. + +.Lookup results for "foo bar baz" arguments +[cols="1m,1m"] +|=== +| Lookup | Expansion +| ${main:0} | foo +| ${main:1} | bar +| ${main:2} | baz +| ${main:foo} | bar +| ${main:bar} | baz +|=== -The example below uses this lookup to place log files in a directory -relative to the log4j configuration file. +You can use this lookup to provide a primitive argument parsing mechanism to your application: -[source,xml] +* First, you need to pass your application's arguments to the `MainMapLookup#setMainArguments` method: ++ +[source,java,indent=0] ---- - - - %d %p %c{1.} [%t] %m%n - - +include::example$manual/lookups/MainArgsExample.java[tag=usage] ---- ++ +<1> Use an **instance** logger field instead of a static one, +to prevent Log4j Core initialization before `main()` is called. +<2> Call `MainMapLookup#setMainArguments` by reflection to allow your application to run with a different Log4j API implementation. -[#LowerLookup] -== Lower Lookup - -The LowerLookup converts the passed in argument to lower case. Presumably the value will be the -result of a nested lookup. - +* Now you can use `$+{main:...}+` lookups in your configuration file to support the usage of a `--logfile ` CLI argument to specify the log file and `--loglevel ` CLI argument to specify the log level. ++ +[tabs] +==== +XML:: ++ [source,xml] ---- - - - %d %p %c{1.} [%t] $${lower:${spring:spring.application.name}} %m%n - - +include::example$manual/lookups/mainArgs.xml[lines=1;18..-1] ---- -[#AppMainArgsLookup] -== Main Arguments Lookup (Application) +JSON:: ++ +[source,json] +---- +include::example$manual/lookups/mainArgs.json[] +---- -This lookup requires that you manually provide the main arguments of the -application to Log4j: +YAML:: ++ +[source,yaml] +---- +include::example$manual/lookups/mainArgs.yaml[lines=17..-1] +---- -[source,java] +Properties:: ++ +[source,properties] ---- -import org.apache.logging.log4j.core.lookup.MainMapLookup; -public class Main { - public static void main(String[] args) { - MainMapLookup.setMainArguments(args); - // ... - } -} +include::example$manual/lookups/mainArgs.properties[lines=18..-1] ---- +==== ++ +<1> Provide default values for the CLI arguments if they are not specified. +<2> Escape the special `:-` sequence using `:\-`. -If the main arguments have been set, this lookup allows applications to -retrieve these main argument values from within the logging -configuration. The key that follows the `main:` prefix can either be a -0-based index into the argument list, or a string, where -`${main:myString}` is substituted with the value that follows `myString` -in the main argument list. +[#MapLookup] +=== Map Lookup -Note: Many applications use leading dashes to identify command arguments. Specifying -`${main:--file}` would result in the lookup failing because it would look for a variable -named "main" with a default value of "-file". To avoid this the ":" separating the Lookup name from the -key must be followed by a backslash as an escape character as in `${main:\--file}`. +[cols="1h,4"] +|=== +| Lookup ID | `map` +| Context | _log event_ +| Key format | _any_ `String` +|=== -For example, suppose the static void main String[] arguments are: +The Map Lookup retrieves the value assigned to the given key in a +xref:manual/messages.adoc#MapMessage[`MapMessage`]. -.... ---file foo.txt --verbose -x bar -.... +[TIP] +==== +Whenever conversion patterns are allowed, a `$$+{map:key}+` lookup is equivalent to a +xref:manual/pattern-layout.adoc#converter-map[`%K\{key}` pattern]. +==== -Then the following substitutions are possible: +[#MarkerLookup] +=== Marker Lookup -[cols="m,m"] +[cols="1h,4"] +|=== +| Lookup ID | `marker` +| Context | _global_ or _log event_ +| Key format | _any_ `String` |=== -|Expression |Result - -|${main:0} -|--file -|${main:1} -|foo.txt +The Marker Lookup can be used in two different ways: -|${main:2} -|--verbose +Global context:: +When used in a global context, it returns `key` if there is a marker named `key` or `null` otherwise. +For example: ++ +---- +${marker:AUDIT:-NO_AUDIT} +---- ++ +will expand to `AUDIT` if a marker with that name exists or `NO_AUDIT` otherwise. -|${main:3} -|-x +Log event context:: +When used in the context of a log event, it returns the +link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html[log event marker] if it exists. ++ +[TIP] +==== +Whenever conversion patterns are allowed, a `$$+{marker:}+` lookup is equivalent to a +xref:manual/pattern-layout.adoc#converter-marker[`%markerSimpleName` pattern]. +==== -|${main:4} -|bar +[#SpringBootLookup] +=== Spring Boot 2 Lookup -|${main:\--file} -|foo.txt +[cols="1h,4"] +|=== +| Lookup ID | `spring` +| Context | _global_ +| Key format | _any_ `String` +|=== -|${main:\-x} -|bar +[IMPORTANT] +==== +If you are using Spring Boot 2, you should use the third party <> instead. +==== -|${main:bar} -|null +The Spring Boot 2 Lookup allows user to query Spring Boot's +https://docs.spring.io/spring-boot/reference/features/external-config.html[externalized configuration files]. +It supports the following keys: -|${main:\--quiet:-true} -|true +[cols="1m,4"] |=== +|Key |Description -Example usage: +|profiles.active +|Comma-separated list of active profiles. -[source,xml] ----- - - - %d %m%n - - ----- +|profiles.active[] +|The active profile with 0-based index ``. -[#MapLookup] -== Map Lookup - -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 -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 -properties that are defined in the configuration file. These variables -are specified without a prefix - e.g. `$\{name}`. The second usage allows -a value from the current -link:../javadoc/log4j-api/org/apache/logging/log4j/message/MapMessage.html[`MapMessage`], -if one is part of the current log event, to be substituted. In the -example below the RoutingAppender will use a different -RollingFileAppender for each unique value of the key named "type" in the -MapMessage. Note that when used this way a value for "type" should be -declared in the properties declaration to provide a default value in -case the message is not a MapMessage or the MapMessage does not contain -the key. See the xref:manual/configuration.adoc#PropertySubstitution[Property -Substitution] section of the xref:manual/configuration.adoc[Configuration] -page for information on how to set the default values. +|profiles.default +|Comma-separated list of default profiles. -[source,xml] ----- - - - - - - %d %p %c{1.} [%t] %m%n - - - - - - ----- +|profiles.default[] +|The default profile with 0-based index ``. -[#marker-lookup] -== Marker Lookup +| +|The value associated with `` in Spring's +https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/env/Environment.html[`Environment`]. +|=== -The marker lookup allows you to use markers in interesting -configurations like a routing appender. Consider the following YAML -configuration and code that logs to different files based on markers: +[WARNING] +==== +Spring Boot 2 initializes Log4j Core at least **twice**: -[source,yaml] ----- -Configuration: - status: debug - - Appenders: - Console: - RandomAccessFile: - - name: SQL_APPENDER - fileName: logs/sql.log - PatternLayout: - Pattern: "%d{ISO8601_BASIC} %-5level %logger{1} %X %msg%n" - - name: PAYLOAD_APPENDER - fileName: logs/payload.log - PatternLayout: - Pattern: "%d{ISO8601_BASIC} %-5level %logger{1} %X %msg%n" - - name: PERFORMANCE_APPENDER - fileName: logs/performance.log - PatternLayout: - Pattern: "%d{ISO8601_BASIC} %-5level %logger{1} %X %msg%n" - - Routing: - name: ROUTING_APPENDER - Routes: - pattern: "$${marker:}" - Route: - - key: PERFORMANCE - ref: PERFORMANCE_APPENDER - - key: PAYLOAD - ref: PAYLOAD_APPENDER - - key: SQL - ref: SQL_APPENDER - - Loggers: - Root: - level: trace - AppenderRef: - - ref: ROUTING_APPENDER ----- +* Log4j Core is initialized the first time using +xref:manual/configuration.adoc#automatic-configuration[its own automatic configuration procedure]. +At this point, the lookup will always return `null`. +Configuration files that use the standard `log4j2.` naming convention, +should provide default values for all Spring lookups. -[source,java] ----- -public static final Marker SQL = MarkerFactory.getMarker("SQL"); -public static final Marker PAYLOAD = MarkerFactory.getMarker("PAYLOAD"); -public static final Marker PERFORMANCE = MarkerFactory.getMarker("PERFORMANCE"); +* As soon as Spring's `Environment` is ready, the lookup becomes **available** and a reconfiguration is triggered. +If you want to provide a configuration file specifically for this phase, call it `log4j2-spring.`. +==== -final Logger logger = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); +Additional runtime dependencies are required for using Spring Boot Lookup: -logger.info(SQL, "Message in Sql.log"); -logger.info(PAYLOAD, "Message in Payload.log"); -logger.info(PERFORMANCE, "Message in Performance.log"); ----- +include::partial$manual/dependencies-log4j-spring-boot.adoc[] -Note the key part of the configuration is `pattern: "$${marker:}"`. This -will produce three log files, each with a log event for a specific -marker. Log4j will route the log event with the `SQL` marker to -`sql.log`, the log event with the `PAYLOAD` marker to `payload.log`, and -so on. +[#StructuredDataLookup] +=== Structured Data Lookup -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`. +[cols="1h,4"] +|=== +| Lookup ID | `sd` +| Context | _log event_ +| Key format | _any_ `String` +|=== -== 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}`. +The Structured Data Lookup is very similar to <> and retrieves the value assigned to the given key in a +xref:manual/messages.adoc#StructuredDataMessage[`StructuredDataMessage`]. +Additionally, the following virtual keys are supported: -[source, xml] ----- - - -%d %p %c{1.} [%t] $${bundle:MyBundle:MyKey} %m%n - - ----- +.Structured Data Lookup—virtual keys +[cols="1m,1,4"] +|=== +| Key | RFC5424 field | Description -== 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]". +| id +| https://datatracker.ietf.org/doc/html/rfc5424#section-6.3.2[`SD-ID`] +| The +link:../javadoc/log4j-api/org/apache/logging/log4j/message/StructuredDataMessage.html#getId()[`id` field] +of the `StructuredDataMessage`. -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. +| type +| https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.7[`MSGID`] +| The +link:../javadoc/log4j-api/org/apache/logging/log4j/message/StructuredDataMessage.html#getType()[`type` field] +of a `StructuredDataMessage`. -[source, xml] ----- - - -%d %p %c{1.} [%t] $${spring:spring.application.name} %m%n - - ----- +|=== -This Lookup requires log4j-spring-cloud-config-client be included in the application. +[TIP] +==== +Whenever conversion patterns are allowed and `key` is not one of the virtual keys, +an `$$+{sd:key}+` lookup is equivalent to a +xref:manual/pattern-layout.adoc#converter-map[`%K\{key}` pattern]. +==== -[#StructuredDataLookup] -== Structured Data Lookup +[#SystemPropertiesLookup] +=== System Properties Lookup -The StructuredDataLookup is very similar to the MapLookup in that it -will retrieve values from StructuredDataMessages. In addition to the Map -values it will also return the name portion of the id (not including the -enterprise number) and the type field. The main difference between the -example below and the example for MapMessage is that the "type" is an -attribute of the -link:../javadoc/log4j-api/org/apache/logging/log4j/message/StructuredDataMessage.html[`StructuredDataMessage`] -while "type" would have to be an item in the Map in a MapMessage. +[cols="1h,4"] +|=== +| Lookup ID | `sys` +| Context | _global_ +| Key format | _any_ `String` +|=== -[source,xml] ----- - - - - - - %d %p %c{1.} [%t] %m%n - - - - - - ----- +The System Properties Lookup retrieves the value of the +https://docs.oracle.com/javase/{java-target-version}/docs/api/java/lang/System.html#getProperties--[Java system property] +associated with the key. -[id=system-properties-lookup] -== [[SystemPropertiesLookup]] System Properties Lookup +[#UpperLookup] +=== Upper Lookup -As it is quite common to define values inside and outside the -application by using System Properties, it is only natural that they -should be accessible via a Lookup. As system properties are often -defined outside the application it would be quite common to see -something like: +[cols="1h,4"] +|=== +| Lookup ID | `upper` +| Context | _global_ +| Key format | _any_ `String` +|=== -[source,xml] ----- - - - ----- +The Upper Lookup converts the passed in argument to uppercase. -This lookup also supports default value syntax. In the sample below, -when the `logPath` system property is undefined, the default value -`/var/logs` is used: +Presumably, the value will be the result of a nested lookup as in the example: -[source,xml] +[source] ---- - - - +${upper:${sys:appname}} ---- -[#UpperLookup] -== Upper Lookup - -The LowerLookup converts the passed in argument to upper case. Presumably the value will be the -result of a nested lookup. +[#WebLookup] +=== Web Lookup -[source,xml] ----- - - - %d %p %c{1.} [%t] $${upper:{${spring:spring.application.name}} %m%n - - ----- +[cols="1h,4"] +|=== +| Lookup ID | `web` +| Context | _global_ +| Key format | _any_ `String` +| Dependency | `log4j-jakarta-web` +|=== -[#WebLookup] -== Web Lookup +The Web Lookup allows applications to retrieve variables that are associated with the Jakarta +https://jakarta.ee/specifications/servlet/5.0/apidocs/jakarta/servlet/servletcontext[`ServletContext`] +of the web application. -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 following table lists various keys that can be retrieved: [cols="1m,4"] |=== |Key |Description -|attr._name_ -|Returns the ServletContext attribute with the specified name +|attr. +|Returns the `ServletContext` attribute with the specified ``. |contextPath |The context path of the web application @@ -659,8 +808,8 @@ represented by this ServletContext is based on. |Gets the minor version of the Servlet specification that the application represented by this ServletContext is based on. -|initParam._name_ -|Returns the ServletContext initialization parameter with the specified name +|initParam. +|Returns the ServletContext initialization parameter with the specified ``. |majorVersion |Returns the major version of the Servlet API that this servlet container supports. @@ -676,12 +825,12 @@ represented by this ServletContext is based on. |servletContextName |Returns the name of the web application as defined in the display-name element of the deployment descriptor + +| +|Return the first of `attr.` and `initParam.` that is defined. |=== -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. +Using the Web Lookup, you can, for example, place the log file in the application's root directory: [source,xml] ---- @@ -694,6 +843,42 @@ Additional runtime dependencies are required for using web lookup: include::partial$manual/dependencies-log4j-jakarta-web.adoc[] +[#third-party] +== Third-party lookups + +The following additional lookups are available from third-party vendors: + +[#KubernetesLookup] +=== Kubernetes Lookup + +[cols="1h,4"] +|=== +|Lookup ID |`k8s` +|Dependency |{log4j-kubernetes-url}[Log4j Kubernetes of Fabric8] +|=== + +Kubernetes Lookup queries https://kubernetes.io/docs/concepts/overview/kubernetes-api/[the Kubernetes API] to retrieve certain information about the current container and its environment. +Kubernetes Lookup is distributed as a part of Fabric8's Kubernetes Client, refer to {log4j-kubernetes-url}[its website] for details. + +[#SpringBootLookup3] +=== Spring Boot 3 Lookup + +[cols="1h,4"] +|=== +|Lookup ID | `spring` +|Dependency | _integrated in Spring Boot 3_ +|=== + +Starting with Spring Boot 3 a `$+{spring:...}+` lookup is available out-of-the-box. +https://docs.spring.io/spring-boot/reference/features/logging.html#features.logging.log4j2-extensions.environment-properties-lookup[Spring Boot documentation] +for more details. + +[WARNING] +==== +The Spring Boot 3 Lookup conflicts with the <<#SpringBootLookup>>. +If you are upgrading to Spring Boot 3, make sure to remove the latter from your classpath. +==== + [#extending] == Extending 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 5a6d655e2a2..d6795079c1a 100644 --- a/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/pattern-layout.adoc @@ -135,6 +135,11 @@ Pattern Layout plugin configuration accepts the following attributes: A composite pattern string of one or more <>. `pattern` and <> are mutually exclusive, that is, only one can be specified. +This attribute supports +xref:manual/configuration.adoc#lazy-property-substitution[runtime property substitution] +using an +xref:manual/lookups.adoc#event-context[event evaluation context]. + [WARNING] ==== If the provided pattern does not contain an exception converter and <> is not disabled, an implicit <> is appended to the pattern. diff --git a/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-docker.adoc b/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-docker.adoc new file mode 100644 index 00000000000..d59733aea24 --- /dev/null +++ b/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-docker.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-docker + runtime + +---- + +Gradle:: ++ +[source,groovy] +---- +// We assume you use `log4j-bom` for dependency management +runtimeOnly 'org.apache.logging.log4j:log4j-docker' +---- +==== diff --git a/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-spring-boot.adoc b/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-spring-boot.adoc new file mode 100644 index 00000000000..f98da777c5f --- /dev/null +++ b/src/site/antora/modules/ROOT/partials/manual/dependencies-log4j-spring-boot.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-spring-boot + runtime + +---- + +Gradle:: ++ +[source,groovy] +---- +// We assume you use `log4j-bom` for dependency management +runtimeOnly 'org.apache.logging.log4j:log4j-spring-boot' +---- +====