////
    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.
////
= Lookups

Log4j Core provides a flexible and extensible property substitution system.

[#StrSubstitutor-diagram]
.Property substitution system
[plantuml]
....
@startuml
class StrSubstitutor #line.bold {
  Interpolator interpolator
  String replace(String input)
  String replace(LogEvent event, String input)
}

StrSubstitutor --> Interpolator

class Interpolator {
  StrLookup[] lookups
  String lookup(String key)
  String lookup(LogEvent event, String key)
}

Interpolator --> "0..*" StrLookup

class StrLookup {
  String lookup(String input)
  String lookup(LogEvent event, String key)
}

@enduml
....

The property substitution system is composed of these elements:

* A string interpolation engine (xref:manual/architecture.adoc#StrSubstitutor[`StrSubstitutor`]) that evaluates `$+{...}+` expressions.
These expressions can contain recursive expressions and default values.
+
See xref:manual/configuration.adoc#property-substitution[property substitution] for more details.

* The
link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/Interpolator.html[`Interpolator`]
that evaluates simple `$\{name}` expressions.
+
The Interpolator has two functions:

** If `name` does not contain a colon `:` character, the Interpolator uses the
xref:manual/configuration.adoc#global-properties[`Properties` configuration element] to resolve its value.

** If `name` is of the form `prefix:key`, the Interpolator delegates the lookup to a `StrLookup` associated with `prefix` and falls back to evaluating `$+{key}+` if the lookup was not successful.

* A set of
xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-core_org-apache-logging-log4j-core-lookup-StrLookup[`StrLookup`]
plugins, each one associated with a prefix, which retrieve data from external sources.

`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 dynamically in a global context or in the context of log event.

[#common-concerns]
== Common concerns

[#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.

Some configuration attributes (e.g., xref:manual/pattern-layout.adoc#plugin-attr-pattern[the `pattern` attribute of Pattern Layout]) supports both evaluation contexts:

* During the configuration process the `$+{...}+` expressions are evaluated using a global context.
The same process converts escaped `$$+{...}+` expressions to `$+{...}+` expressions.

* For each log event, the remaining expressions are evaluated, using the log event as context.

Lookups can choose to react differently depending on the execution context.
<<DateLookup>> is such an example:

* When used in a global context, it formats the **current** timestamp obtained through
https://docs.oracle.com/en/java/javase/{java-target-version}/docs/api/java.base/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()`].

[#lookups-patterns]
=== Lazy lookups and pattern converters

For historical reasons, the
xref:manual/pattern-layout.adoc#plugin-attr-pattern[`pattern` attribute of PatternLayout]
supports two similar string replacement mechanisms:

* `+${...}+` property expressions.
* xref:manual/pattern-layout.adoc#converters[`%<name>` pattern converters].

Both lazy `+$${...}+` property expressions and pattern converters have access to the value of the current `LogEvent` and can provide similar results.
There is, however, an important difference between them:

* Pattern converters can be garbage-free.
See xref:manual/pattern-layout.adoc#garbage-free[Garbage-free pattern converters] for more details.
* Lazy lookups are **not** garbage-free and always create temporary `String` objects.

[#collection]
== Collection

Log4j Core provides many lookups out-of-the-box:

Lookups operating on the global context:: A large group of lookups supports evaluation in a global context.
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-list]
.Lookups operating on the global context
[cols="1,2m,5"]
|===
| Prefix | Dependency | Data source

| <<ResourceBundleLookup,`bundle`>>
|
| A Java
https://docs.oracle.com/en/java/javase/{java-target-version}/docs/api/java.base/java/util/ResourceBundle.html[resource bundle]

| <<ContextMapLookup,`ctx`>>
|
| {log4j2-url}/manual/thread-context.html[]

| <<DateLookup,`date`>>
|
| Current timestamp

| <<DockerLookup,`docker`>>
| log4j-docker
| Docker container

| <<EnvironmentLookup,`env`>>
|
| Environment variables

| <<JavaLookup,`java`>>
|
| JVM characteristics

| <<JndiLookup,`jndi`>>
| log4j-jndi
| JNDI

| <<Log4jLookup,`log4j`>>
|
| Location of Log4j configuration file

| <<LowerLookup,`lower`>>
|
| It converts the supplied key to lowercase


| <<MainMapLookup,`main`>>
|
| JVM application arguments

| <<MarkerLookup,`marker`>>
|
| Returns `key` if a marker named `key` exists

| <<SystemPropertiesLookup,`sys`>>
|
| Java system properties

| <<UpperLookup,`upper`>>
|
| It converts the supplied key to uppercase

| <<WebLookup,`web`>>
| log4j-jakarta-web
| Jakarta
https://jakarta.ee/specifications/servlet/6.0/apidocs/jakarta.servlet/jakarta/servlet/servletcontext[`ServletContext`].

|===

Lookups operating on the log event context::
The following lookups only support evaluation in the context of a log event or behave differently, when evaluated in such a context:
+
[#event-context-list]
.Lookups operating on the log event context
[cols="1,2m,5"]
|===
| Prefix | Dependency | Data source

| <<ContextMapLookup,`ctx`>>
|
| Log event
link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html#getContextData()[context data]

| <<DateLookup,`date`>>
|
| Log event
link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html#getTimeMillis()[timestamp]

| <<EventLookup,`event`>>
|
| link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html[Log event]

| <<MapLookup,`map`>>
|
| {log4j2-url}/manual/messages.html#MapMessage[`MapMessage`]

| <<MarkerLookup,`marker`>>
|
| Log event
link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html#getMarker()[marker]

| <<StructuredDataLookup,`sd`>>
|
| {log4j2-url}/manual/messages.html#StructuredDataMessage[`StructuredDataMessage`]

|===

[#ResourceBundleLookup]
=== Resource Bundle Lookup

[cols="1h,4"]
|===
| Context | _global_

| Syntax
a| `bundle:<baseName>:<key>`

where:

`baseName`::
the base name of a resource bundle (see
https://docs.oracle.com/en/java/javase/{java-target-version}/docs/api/java.base/java/util/ResourceBundle.html[`ResourceBundle`]).

`key`::
the key for the resource string.
|===

The Resource Bundle Lookup retrieves strings from Java Resource bundles, e.g.:

----
${bundle:org.example.Main:errorMessage}
----

[TIP]
====
Do you want to use the values in Spring Boot's `application.properties` file?
Use <<SpringBootLookup3>> instead.
====

[#ContextMapLookup]
=== Context Map Lookup

[cols="1h,4"]
|===
| Context | _global_ and _log event_

| Syntax
| `ctx:<key>`

where `<key>` is any `String`.
|===

The Context Map Lookup can be used in two different contexts:

Global context::
If used in the global context, it uses the
{log4j2-url}/manual/thread-context.html[ThreadContext]
to retrieve data.
+
[WARNING]
====
When used in this context
{log4j2-url}/manual/thread-context.html#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.
{log4j2-url}/manual/thread-context.html#custom-ContextDataProvider[Custom context data providers] are therefore supported.
+
[TIP]
====
Don't use `$$+{ctx:key}+` in the xref:manual/pattern-layout.adoc[] conversion patterns!
Use xref:manual/pattern-layout.adoc#converter-thread-context-map[the `%X\{key}` pattern converter] instead.

See <<lookups-patterns>> for more information.
====

[#DateLookup]
=== Date Lookup

[cols="1h,4"]
|===
| Context | _global_ and _log event_

| Syntax
| `date:<format>`

where `<format>` is a
https://docs.oracle.com/en/java/javase/{java-target-version}/docs/api/java.base/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/en/java/javase/{java-target-version}/docs/api/java.base/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]
====
Don't use `$$+{date:format}+` in the xref:manual/pattern-layout.adoc[] conversion patterns!
Use xref:manual/pattern-layout.adoc#converter-date[the `%d\{key}` pattern converter] instead.

See <<lookups-patterns>> for more information.
====

[#DockerLookup]
=== Docker Lookup

[cols="1h,4"]
|===
| Context | _global_

| Syntax
| `docker:<key>`

where `<key>` is one of the <<DockerLookup-keys>>.
| Dependency
| xref:components.adoc#log4j-docker[`log4j-docker`]
|===

Docker Lookup queries https://docs.docker.com/engine/api/[the API of the Docker Engine] running your container.
It supports the retrieval of following container attributes:

.Docker Lookup supported keys
[%header,cols="1m,4",id=DockerLookup-keys]
|===
|Key |Description
|containerId |Container ID
|containerName |Container name
|imageId |Container image ID
|imageName |Container image name
|shortContainerId |The first 12 characters of the container ID
|shortImageId |The first 12 characters of the container image ID
|===

.Additional runtime dependencies are required for using Docker Lookup:
[%collapsible]
=====
include::partial$components/log4j-docker.adoc[]
=====

[#EnvironmentLookup]
=== Environment Lookup

[cols="1h,4"]
|===
| Context | _global_

| Syntax
| `env:<key>`

where `<key>` is any `String`
|===

The Environment Lookup retrieves the value of the
https://docs.oracle.com/en/java/javase/{java-target-version}/docs/api/java.base/java/lang/System.html#getenv(java.lang.String)[OS environment variable]
associated with the key.

[#EventLookup]
=== Event Lookup

[cols="1h,4"]
|===
| Context | _log event_

| Syntax
| `event:<key>`

where `<key>` is one of the <<EventLookup-keys>>.
|===

The Event Lookup provides access to fields of the current log event.
It supports the retrieval of the following event attributes:

.Event Lookup supported keys
[cols="1m,4a",id=EventLookup-keys]
|===
|Key |Description

|Exception
|Simple class name of the exception, if one is present.

|Level
|{log4j2-url}/manual/customloglevels.html[Logging level] of the event

|Logger
|Name of the logger

|Marker
|{log4j2-url}/manual/markers.html[Marker] associated with the log event, if one is present.

|Message
|Formatted {log4j2-url}/manual/messages.html[`Message`]

|ThreadId
|Thread id associated with the log event

|ThreadName
|Name of the thread associated with the log event

|Timestamp
|UNIX timestamp in milliseconds of the log event
|===

[TIP]
====
Don't use `$$+{event:key}+` in the xref:manual/pattern-layout.adoc[] conversion patterns!
There is a xref:manual/pattern-layout.adoc#converters[specialized pattern converter replacement] for each of these lookups.

See <<lookups-patterns>> for more information.
====

[#JavaLookup]
=== Java Lookup

[cols="1h,4"]
|===
| Context    | _global_

| Syntax
| `java:<key>`

where `<key>` is one of the <<JavaLookup-keys>>.
|===

The Java Lookup allows retrieving information about the Java environment the application is using.
The following keys are supported

.Java Lookup supported keys
[cols="1m,2,6m",id=JavaLookup-keys]
|===
|Key |Description |Example

|version
|Short Java version
|Java version 21.0.3

|runtime
|Java runtime version
|OpenJDK Runtime Environment (build 21.0.3+9-LTS) from Eclipse Adoptium

|vm
|Java VM version
|OpenJDK 64-Bit Server VM (build 21.0.3+9-LTS, mixed mode, sharing)

|os
|OS version
|Linux 6.1.0-18-amd64, architecture: amd64-64

|locale
|System locale and file encoding
|default locale: en_US, platform encoding: UTF-8

|hw
|Hardware information
|processors: 32, architecture: amd64-64, instruction sets: amd64`

|===

[#JndiLookup]
=== JNDI Lookup

[cols="1h,4"]
|===
| Context    | _global_

| Syntax
| `jndi:<name>`

where `<name>` is a JNDI
https://docs.oracle.com/en/java/javase/{java-target-version}/docs/api/java.naming/javax/naming/Name.html[`Name`].

| Dependency | `log4j-jndi`
|===

[IMPORTANT]
====
You need to enable the JNDI lookup **explicitly** by setting the
xref:manual/systemproperties.adoc#log4j.jndi.enableLookup[`log4j.jndi.enableLookup`]
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]
----
$${jndi:app_name}
----

[NOTE]
====
Android does not support JNDI.
====

Additional runtime dependencies are required for using JNDI Lookup:

include::partial$components/log4j-jndi.adoc[]

[#Log4jLookup]
=== Configuration Location Lookup

[cols="1h,4"]
|===
| Context    | _global_

| Syntax
| `log4j:<key>`

where `<key>` is one of the <<Log4jLookup-keys>>.
|===

The Configuration Location Lookup supports two keys:

.Configuration Location Lookup supported keys
[cols="1m,4",id=Log4jLookup-keys]
|===
|Key |Description

|configLocation
|Returns the location of the configuration file as an absolute file path or URI.

|configParentLocation
|Returns the location of the folder containing the configuration file as an absolute file path or URI.
|===

[#LowerLookup]
=== Lower Lookup

[cols="1h,4"]
|===
| Context    | _global_

| Syntax
| `lower:<key>`

where `<key>` is any `String`.
|===

The Lower Lookup converts the passed in argument to lowercase.

Presumably, the value will be the result of a nested lookup as in the example:

[source]
----
${lower:${sys:appname}}
----

[#MainMapLookup]
=== Main Arguments Lookup

[cols="1h,4"]
|===
| Context    | _global_

| Syntax
| `main:<key>`

wherre `<key>` either a non-negative `int` or a `String`.
|===

[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.
====

The Main Arguments Lookup provides a way to query the arguments received by your application.
It supports two kinds of keys:

* 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
|===

You can use this lookup to provide a primitive argument parsing mechanism to your application:

* First, you need to pass your application's arguments to the `MainMapLookup#setMainArguments` method:
+
[source,java,indent=0]
----
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.

* Now you can use `$+{main:...}+` lookups in your configuration file to support the usage of a `--logfile <file>` CLI argument to specify the log file and `--loglevel <level>` CLI argument to specify the log level.
+
[tabs]
====
XML::
+
[source,xml]
----
include::example$manual/lookups/mainArgs.xml[lines=1;18..-1]
----

JSON::
+
[source,json]
----
include::example$manual/lookups/mainArgs.json[]
----

YAML::
+
[source,yaml]
----
include::example$manual/lookups/mainArgs.yaml[lines=17..-1]
----

Properties::
+
[source,properties]
----
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 `:\-`.

[#MapLookup]
=== Map Lookup

[cols="1h,4"]
|===
| Context    | _log event_

| Syntax
| `map:<key>`

where `<key>` is any `String`.
|===

The Map Lookup retrieves the value assigned to the given key in a
{log4j2-url}/manual/messages.html#MapMessage[`MapMessage`].

[TIP]
====
Don't use `$$+{map:key}+` in the xref:manual/pattern-layout.adoc[] conversion patterns!
Use xref:manual/pattern-layout.adoc#converter-map[the `%K\{key}` pattern converter] instead.

See <<lookups-patterns>> for more information.
====

[#MarkerLookup]
=== Marker Lookup

[cols="1h,4"]
|===
| Context    | _global_ or _log event_

| Syntax
| `marker:<key>`

where `<key>` is any `String`
|===

The Marker Lookup can be used in two different ways:

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.

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]
====
Don't use `$$+{marker:}+` in the xref:manual/pattern-layout.adoc[] conversion patterns!
Use xref:manual/pattern-layout.adoc#converter-marker[the `%markerSimpleName` pattern converter] instead.

See <<lookups-patterns>> for more information.
====

[#StructuredDataLookup]
=== Structured Data Lookup

[cols="1h,4"]
|===
| Context    | _log event_

| Syntax
| `sd:<key>`

where `<key>` is either one of the <<StructuredDataLookup-keys>> or any `String`
|===

The Structured Data Lookup is very similar to <<MapLookup>> and retrieves the value assigned to the given key in a
{log4j2-url}/manual/messages.html#StructuredDataMessage[`StructuredDataMessage`].
Additionally, the following virtual keys are supported:

.Structured Data Lookup virtual keys
[cols="1m,1,4",id=StructuredDataLookup-keys]
|===
| Key | RFC5424 field | Description

| 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`.

| 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`.

|===

[TIP]
====
Except `++$${sd:id}++` and `++$${sd:type}++`, don't use other `++$${sd:key}++` expressions in the xref:manual/pattern-layout.adoc[] conversion patterns!
Use xref:manual/pattern-layout.adoc#converter-map[the `%K\{key}` pattern converter] instead.

See <<lookups-patterns>> for more information.
====

[#SystemPropertiesLookup]
=== System Properties Lookup

[cols="1h,4"]
|===
| Context    | _global_

| Syntax
| `sys:<prop>`

where `<prop>` is any `String`
|===

The System Properties Lookup retrieves the value of the
https://docs.oracle.com/en/java/javase/{java-target-version}/docs/api/java.base/java/lang/System.html#getProperties()[Java system property]
associated with the key.

[#UpperLookup]
=== Upper Lookup

[cols="1h,4"]
|===
| Context    | _global_

| Syntax
| `upper:<key>`

where `<key>` wi any `String`
|===

The Upper Lookup converts the passed in argument to uppercase.

Presumably, the value will be the result of a nested lookup as in the example:

[source]
----
${upper:${sys:appname}}
----

[#WebLookup]
=== Web Lookup

[cols="1h,4"]
|===
| Context    | _global_

| Syntax
| `web:<key>`

where `<key>` is one of the <<WebLookup-keys>>.

| Dependency | `log4j-jakarta-web`
|===

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 following table lists various keys that can be retrieved:

.Web Lookup supported keys
[cols="1m,4",id=WebLookup-keys]
|===
|Key |Description

|attr.<name>
|Returns the `ServletContext` attribute with the specified `<name>`.

|contextPath
|The context path of the web application

|contextPathName
|The first token in the context path of the web application splitting on "/" characters.

|cookie.<name>
|Value of the `<name>` HTTP cookie.

|effectiveMajorVersion
|Gets the major version of the Servlet specification that the application
represented by this ServletContext is based on.

|effectiveMinorVersion
|Gets the minor version of the Servlet specification that the application
represented by this ServletContext is based on.

|header.<name>
|Value of the `<name>` request HTTP header.

|initParam.<name>
|Returns the ServletContext initialization parameter with the specified `<name>`.

|majorVersion
|Returns the major version of the Servlet API that this servlet container supports.

|minorVersion
|Returns the minor version of the Servlet API that this servlet container supports.

|request.attr.<name>
|Value of the `<name>` servlet request attribute.

|request.method
|HTTP request method.

|request.parameter.<name>
|Value of the `<name>` servlet request parameter.

|request.principal
|Servlet request principal.

|request.remoteAddress
|Servlet request remote address.

|request.remoteHost
|Servlet request remote host.

|request.remotePort
|Servlet request remote port.

|request.uri
|Servlet request URI.

|request.url
|Servlet request URL.

|rootDir
|Returns the result of calling getRealPath with a value of "/".

|serverInfo
|Returns the name and version of the servlet container on which the servlet is running.

|servletContextName
|Returns the name of the web application as defined in the display-name element of the deployment descriptor

|session.attr.<name>
|Value of the `<name>` servlet session attribute.

|session.id
|Servlet session id.

|<name>
|Return the first of `attr.<name>` and `initParam.<name>` that is defined.
|===

Using the Web Lookup, you can, for example, place the log file in the application's root directory:

[source,xml]
----
<Appenders>
  <File name="ApplicationLog" fileName="${web:rootDir}/app.log"/>
</Appenders>
----

Additional runtime dependencies are required for using web lookup:

include::partial$features/servlet-support.adoc[]

[#third-party]
== Third-party lookups

The following additional lookups are available from third-party vendors:

[#KubernetesLookup]
=== Kubernetes Lookup

[cols="1h,4"]
|===
| Syntax | `k8s:<key>`
| 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"]
|===
| Syntax  | `spring:<key>`
| 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.

[#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 <<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 the following files for examples:

* {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/DateLookup.java[`LowerLookup.java`] – <<LowerLookup>> lower-cases its input
* {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/EventLookup.java[`EventLookup.java`] – <<EventLookup>> extracts specified fields from the effective `LogEvent` in the context
