////
    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.
////
[#jakarta-ee]
= Integrating with Jakarta EE

:jetty-latest-url: https://jetty.org/docs/jetty/12
:tomcat-latest-url: https://tomcat.apache.org/tomcat-11.0-doc
:wildfly-latest-url: https://docs.wildfly.org/32

In a Jakarta EE environment, there are two possible approaches to logging:

. Each application can use their own copy of Log4j Core and include `log4j-core` in the WAR or EAR archive.
. Applications can also use a single copy of Log4j Core that must be installed globally on the application server.

While the first approach is the easiest to implement, it has some limitations:

Shared libraries::
Log events emitted by each application and the libraries bundled with it will be handled by Log4j Core,
but events related to the application emitted by a **shared** library (e.g. JPA implementation) will be handled by the application server.
To diagnose a problem with the application, you might need to look into multiple log files.
Separate log files::
Each application must use a **different** log file to prevent problems with concurrent access to the same file by multiple applications.
Problems may arise, especially if a xref:manual/appenders/rolling-file.adoc[rolling file appender] is used.
Lifecycle::
Web applications have a different lifecycle from the application server.
Additional care is required to stop Log4j Core when the application is stopped.
See <<log4j-jakarta-web>> for more details.

The second approach requires changes to the configuration of the application server,
but produces better results in terms of separating log events of different applications.
See <<sharing>> for more details.

[#log4j-jakarta-web]
== Integrating with web applications

[NOTE]
====
To avoid problems,
some Log4j API and Log4j Core features are automatically disabled when running in a Jakarta EE environment.
Most notably:

- the usage of `ThreadLocal` for object pooling is disabled.
- a web-safe implementation of
link:javadoc/log4j-api/org/apache/logging/log4j/spi/ThreadContextMap.html[`ThreadContextMap`] is used.
- JMX notifications are sent synchronously.
- the JVM shutdown hook is disabled.

See xref:manual/systemproperties.adoc#log4j2.isWebapp[`log4j2.isWebapp`] for more details.
====

Using a logging implementation like **Log4j Core** in a Jakarta EE application requires particular care.
Since the lifecycle of a container or web application is independent of the lifecycle of the JVM, it's important for logging resources to be properly cleaned up (database connections closed, files closed, etc.) when the container or web application shuts down.

To properly synchronize the lifecycles of Log4j Core and Jakarta EE applications,
an additional **Log4j Web** artifact is provided.

[#log4j-jakarta-web-installation]
=== Installation

To install Log4j Web in your web application, you need to add it as a runtime dependency:

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

If you are writing a Servlet 3.0 or later application, Apache Log4j Web will register a
https://jakarta.ee/specifications/servlet/5.0/apidocs/jakarta/servlet/servletcontainerinitializer[`ServletContainerInitializer`]
that takes care of configuring the Log4j lifecycle for you. Under the hood this will:

* initialize Log4j Core with the correct configuration file.
* register a `Log4jServletContextListener` to automatically shut down Log4j Core, when the application shuts down.
* register a `Log4jServletFilter` to enable the xref:manual/lookups.adoc#WebLookup[web lookup].

See also <<web-application-specific>>.

[WARNING]
====
While the Servlet Specification allows web fragments to automatically add context listeners, it does not give any guarantees regarding the order in which those listeners are executed
(see https://jakarta.ee/specifications/servlet/5.0/jakarta-servlet-spec-5.0#Assembling_the_descriptor[Section 8.2.3]).

If other context listeners in your application use logging, you need to make sure that `Log4jServletContextListener` is the last listener to be executed at shutdown.
To do it, you must create a `web.xml` descriptor and add the `Log4jServletContextListener` explicitly as the **first** context listener:

.Snippet from an example {antora-examples-url}/manual/webapp/web.xml[`web.xml`]
[source,xml,indent=0]
----
include::example$manual/webapp/web.xml[tag=context-listener]
----
====

[#log4j-jakarta-manual-instalation]
==== Manual installation

If you are maintaining an older Servlet 2.5 (or earlier) application,
or if you disabled the <<isLog4jAutoInitializationDisabled,servlet container initializer>>.

.Snippet from an example {antora-examples-url}/manual/webapp/web.xml[`web.xml`]
[source,xml,indent=0]
----
include::example$manual/webapp/web.xml[tags=context-listener;filter]
----

[#configuration]
=== Configuration

Log4j Web provides many configuration options to finely tune its installation.
These configuration options should be specified as
https://jakarta.ee/specifications/servlet/5.0/jakarta-servlet-spec-5.0#initialization-parameters[servlet context initialization parameters].

[id=isLog4jAutoInitializationDisabled]
==== `isLog4jAutoInitializationDisabled`

[cols="1h,5"]
|===
| Type          | `boolean`
| Default value | `false`
|===

If set to `true`, the `Log4jServletContainerInitializer` will be disabled, which prevents the automatic registration of both the `Log4jServletContextListener` and `Log4jServletFilter`.

[id=isLog4jAutoShutdownDisabled]
==== `isLog4jAutoShutdownDisabled`

[cols="1h,5"]
|===
| Type          | `boolean`
| Default value | `false`
|===

If set to `true`, the `Log4jServletContextListener` will not register a `Log4jServletContextListener` to handle the web application shut down.

[id=log4j.stop.timeout.timeunit]
==== `log4j.stop.timeout.timeunit`

[cols="1h,5"]
|===
| Type
| https://docs.oracle.com/javase/{java-target-version}/docs/api/java/util/concurrent/TimeUnit.html[`TimeUnit`]

| Default value
| https://docs.oracle.com/javase/{java-target-version}/docs/api/java/util/concurrent/TimeUnit.html#SECONDS[`SECONDS`]
|===

Specifies the `TimeUnit` used for the shut-down delay.

[id=log4j.stop.timeout]
==== `log4j.stop.timeout`

[cols="1h,5"]
|===
| Type
| `long`

| Default value
| `30`
|===

It specifies the duration of the shut-down delay.

[id=log4jContextName]
==== `log4jContextName`

[cols="1h,5"]
|===
| Type
| `String`

| Default value
| _automatically computed_
|===

Used to specify the name of the logger context.

If <<jndi-configuration,`JndiContextSelector`>> is used, this parameter **must** be explicitly provided. Otherwise, the default value is:

. the servlet context name, if present,
. the servlet context path, including the leading `/`, otherwise.

[id=isLog4jContextSelectorNamed]
==== `isLog4jContextSelectorNamed`

[cols="1h,5"]
|===
| Type
| `boolean`

| Default value
| `false`
|===

Must be set to `true` to use the <<jndi-configuration,JNDI configuration>>.

[id=log4jConfiguration]
==== `log4jConfiguration`

[cols="1h,5"]
|===
| Type
| https://docs.oracle.com/javase/8/docs/api/java/net/URI.html[`URI`]

| Default value
| `false`
|===

The location of a Log4j Core configuration file.
If the provided value is not an **absolute** URI, Log4j interprets it as:

. the path to an existing
https://jakarta.ee/specifications/servlet/5.0/apidocs/jakarta/servlet/servletcontext#getResource(java.lang.String)[servlet context resource],
. the path to an existing file,
. the path to a
https://docs.oracle.com/javase/8/docs/api/java/lang/ClassLoader.html#getResource-java.lang.String-[classpath resource].

If no value is provided:

. Log4j Web looks for a servlet context resource named `/WEB-INF/log4j2-<contextName>.<extension>`, where `<contextName>` is the name of the logger context,
. if no such file exists it looks for a servlet context resource named `/WEB-INF/log4j2.<extension>`,
. otherwise, it searches for a configuration file on the classpath using the usual https://logging.apache.org/log4j/2.x/manual/configuration.html#automatic-configuration[automatic configuration procedure].

[#async]
=== Asynchronous requests and threads

In order for the
xref:manual/lookups.adoc#WebLookup[web lookup]
to work correctly, Log4j must be able to always identify the `ServletContext` used by the current thread.
When standard requests, forwards, inclusions, and error resources are processed,
the `Log4jServletFilter` binds the `LoggerContext` to the thread handling the request,
and you don't have to do anything.

The handling of asynchronous requests is however more tricky, since it allows you to execute code on threads that were not prepared by `Log4jServletFilter`.
Such a situation occurs, for example, if your code was started using the
https://jakarta.ee/specifications/servlet/5.0/apidocs/jakarta/servlet/asynccontext#start(java.lang.Runnable)[`AsyncContext.start(Runnable)`]
method.

To successfully propagate the logger context along asynchronous calls, the
link:javadoc/log4j-jakarta-web/org/apache/logging/log4j/web/WebLoggerContextUtils.html[`WebLoggerContextUtils`]
helper class is made available.
Using this class you can either decorate a `Runnable` with method calls that bind the appropriate logger context to the thread:

.Snippet from an example {antora-examples-url}/manual/webapp/AsyncServlet.java[`AsyncServlet.java`]
[source,java,indent=0]
----
include::example$manual/webapp/AsyncServlet.java[tag=automatic]
----

or, if more flexibility is required, you can apply the same logic by using
link:javadoc/log4j-jakarta-web/org/apache/logging/log4j/web/Log4jWebSupport.html[`Log4jWebSupport`]:

.Snippet from an example {antora-examples-url}/manual/webapp/AsyncServlet.java[`AsyncServlet.java`]
[source,java,indent=0]
----
include::example$manual/webapp/AsyncServlet.java[tag=manual]
----

[#log4j-taglib]
=== Logging in JavaServer Pages

[WARNING]
====
*The Log4j Tag library is planned to be removed in the next major release!*
If you are using this library, please get in touch with the Log4j maintainers using link:{logging-services-url}/support.html[the official support channels].
====

To help users add logging statements to JavaServer Pages, Log4j provides a JSP tag library modeled after the
https://web.archive.org/web/20140215182415/http://jakarta.apache.org/taglibs/log/[Jakarta Commons Log Tag library].
To use it, you need to add the following runtime dependency to your web application project:

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

and add the following declaration to your JSP pages:

[source,jsp]
----
include::example$manual/webapp/taglib.jsp[tag=declaration]
----

[WARNING]
====
The Log4j Taglib component is deprecated and is scheduled for removal in Log4j 3.

Currently, it only works with JavaServer Pages 2.3 and previous releases,
and no version compatible with Jakarta Server Pages 3.0 is available.
====

The Log4j Taglib library defines a tag for most
link:javadoc/log4j-api/org/apache/logging/log4j/Logger.html[`Logger`]
methods, including:

* simple and parameterized log statements:
+
.Snippet from an example {antora-examples-url}/manual/webapp/taglib.jsp[`taglib.jsp`]
[source,xml,indent=0]
----
include::example$manual/webapp/taglib.jsp[tag=simple]
----

* flow tracing statements:
+
.Snippet from an example {antora-examples-url}/manual/webapp/taglib.jsp[`taglib.jsp`]
[source,xml,indent=0]
----
include::example$manual/webapp/taglib.jsp[tag=entry-exit]
----

* catching and throwing statements:
+
.Snippet from an example {antora-examples-url}/manual/webapp/taglib.jsp[`taglib.jsp`]
[source,xml,indent=0]
----
include::example$manual/webapp/taglib.jsp[tag=catching]
----

* tags to test the current log level:
+
[source,xml,indent=0]
----
include::example$manual/webapp/taglib.jsp[tag=if-enabled]
----

* tags to set the name of the logger used:
+
.Snippet from an example {antora-examples-url}/manual/webapp/taglib.jsp[`taglib.jsp`]
[source,xml,indent=0]
----
include::example$manual/webapp/taglib.jsp[tag=set-logger]
----

* a `dump` tag that prints the contents of a JSP scope:
+
.Snippet from an example {antora-examples-url}/manual/webapp/taglib.jsp[`taglib.jsp`]
[source,xml,indent=0]
----
include::example$manual/webapp/taglib.jsp[tag=dump]
----

[#web-application-specific]
=== Application server specific notes

[#web-application-wildfly]
WildFly::
+
WildFly implicitly adds a shared copy of `log4j-api` to each web application deployment.
This copy of `log4j-api` is configured to forward all events to WildFly's centralized logging system and does **not** use the copy of Log4j Core bundled with the web application.
+
To use Log4j Core, you need to set the `add-logging-api-dependencies` attribute of the logging subsystem to `false`.
See {wildfly-latest-url}/Admin_Guide.html#logging-attributes[WildFly documentation] for more details.

[#sharing]
== Sharing Log4j Core between Web Applications

Since Log4j Core supports multiple xref:manual/architecture.adoc#LoggerContext[logger contexts], it is possible to share a single instance of Log4j Core
without losing the ability to configure logging for each application separately.

Sharing Log4j Core has two main advantages:

* You can send log statements from multiple applications to the same log file.
Under the hood, Log4j Core will use a single
xref:manual/architecture.adoc#AbstractManager[manager]
per file, which will serialize concurrent access from multiple applications.
* You can capture log statements issued by other shared libraries, so you don't have to look for them in the global application server log.

[#sharing-setup]
=== Setup

To share Log4j Core between applications, you need to share at least these two JAR files:

* https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-api/{log4j-api-version}[`log4j-api`]
* https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-core/{log4j-core-version}[`log4j-core`]
* https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-jakarta-web/{log4j-core-version}[`log4j-jakarta-web`]
(or
https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-web/{log4j-core-version}[`log4j-web`]
if you use a Java EE application server)

Since sharing libraries between applications is not part of the Jakarta EE standard, the instructions are specific to each application server:

[tabs]
====
GlassFish::
In GlassFish, you can add those libraries to the _common classloader_.
See https://glassfish.org/docs/latest/application-development-guide.html#circumventing-class-loader-isolation[GlassFish documentation] for more details.

Jetty::
Recent versions of Jetty have a `logging-log4j2` module that can be easily enabled to share Log4j Core between applications and to use Log4j Core for the Jetty server itself.
See {jetty-latest-url}/operations-guide/modules/index.html[Jetty Modules documentation] for more details.

OpenLiberty::
In OpenLiberty, you can add Log4j as a _global library_.
See https://openliberty.io/docs/latest/class-loader-library-config.html#_configure_global_libraries[OpenLiberty documentation] for more details.

Payara::
See https://docs.payara.fish/community/docs/Technical%20Documentation/Payara%20Server%20Documentation/Server%20Configuration%20And%20Management/Classloading/Standard%20Classloading.html#common-libraries[Payara Common Libraries documentation].

Tomcat::
In Tomcat, you can use the _common classloader_.
See {tomcat-latest-url}/class-loader-howto.html[Tomcat classloader documentation] for more details.

WildFly::
You can install Log4j as a _global module_ or in a _global directory_.
See {wildfly-latest-url}/Admin_Guide.html#EE_Application_Deployment_Configuration[WildFly EE Application Deployment documentation] for more details.
+
Check also the <<web-application-wildfly,WildFly note above>>.
====

[WARNING]
=====
Web application classloaders (see
https://jakarta.ee/specifications/servlet/6.0/jakarta-servlet-spec-6.0#web-application-class-loader[Servlet Specification 10.7.2]
) use a _"parent last"_ delegation strategy, but prevent application from overriding implementation classes provided by the container.

If you share Log4j between applications and the applications themselves contain Log4j Core, the logging behavior depends on the application server.
Some application servers will use the shared instance (e.g., WildFly), while others will use the application instance (e.g., Tomcat).
There are two solutions to this problem:

* you can remove Log4j from the WAR or EAR archive:
+
[tabs]
====
Maven::
You can declare the
https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope[scope]
of all Log4j libraries as `provided`.

Gradle::
You can add `log4j-api` to the `providedCompile` configuration, while `log4j-core` to the `providedRuntime` configuration.
See the https://docs.gradle.org/current/userguide/war_plugin.html#sec:war_dependency_management[Gradle WAR plugin] for more details.
====

* you can use an application-server-specific configuration option to delegate the loading of Log4j API to the parent classloader.
=====

[#log-separation]
=== Log separation

When using a shared instance of Log4j Core,
you might be interested in identifying the application associated with a given log event.
Log4j Core provides a mechanism to split all
link:javadoc/log4j-api/org/apache/logging/log4j/Logger.html[`Logger`]
instances into logging domains called
xref:manual/architecture.adoc#LoggerContext[`LoggerContext`]s.
You have therefore two ways to separate log events:

. You can create a separate logger context for each web application and one context for the common libraries.
See <<multiple-contexts>> for more details.
. You can also use a **single** logger context for all log events, but use
xref:manual/lookups.adoc[lookups] to add context data to your log events.
See <<single-context>> for more details.

[IMPORTANT]
====
These two approaches deliver similar results for log events generated by the web applications themselves or the libraries
bundled in the WAR or EAR archive.

Differences between these approaches appear in the handling of **shared** libraries.
There are two kinds of shared libraries:

. Shared libraries that use **static** `Logger` fields.
These libraries will always use the same logger context, which will not be one of the per-application contexts.
+
This kind includes all the shared libraries, which were not written with Jakarta EE in mind.

. Shared libraries that use **instance** `Logger` fields.
These libraries will use the logger context associated with the web application that uses them.
+
Application server implementations usually use instance `Logger` fields.

Since the first kind of libraries is more common,
counterintuitively the <<single-context>> approach will usually give better results than the <<multiple-contexts>> approach.
====

[#single-context]
==== Single logger context

By default, Log4j Core creates a separate logger context per classloader.
To use a single logger context, you need to set the
xref:manual/systemproperties.adoc#log4j2.contextSelector[`log4j2.contextSelector`]
system property to:

* either
link:javadoc/log4j-core/org/apache/logging/log4j/core/selector/BasicContextSelector.html[`org.apache.logging.log4j.core.selector.BasicContextSelector`]
to use synchronous loggers,
* or
link:javadoc/log4j-core/org/apache/logging/log4j/core/async/BasicAsyncLoggerContextSelector.html[`org.apache.logging.log4j.core.async.BasicAsyncLoggerContextSelector`]
to use asynchronous loggers.

In this approach, you must use xref:manual/lookups.adoc[lookups] to register the application that generated a log event.
The most useful lookups in this case are:

Web lookup::
It does not require any setup, but it is available only after `Log4jServletFilter` has been executed.
Some log events pertinent to a web application can be unmarked.
See xref:manual/lookups.adoc#WebLookup[web lookup] for more information.

JNDI lookup::
It covers a larger part of the handling of a request, but it requires additional setup to export the name of the application via JNDI.
See xref:manual/lookups.adoc#JndiLookup[JNDI lookup] for more information.

When using a single logger context, you choose between:

* Logging all events to a single appender.
We strongly recommend using a structured layout (e.g., xref:manual/json-template-layout.adoc[]) with an additional field capturing the Servlet context name.
This would allow separation of application logs by filtering on the context name.
The following example demonstrates this scheme using a Socket Appender writing to Elasticsearch:
+
[tabs]
====
XML::
+
[source,xml,indent=0]
----
include::example$manual/webapp/log4j2-single.xml[tag=global]
----

JSON::
+
[source,json,indent=0]
----
include::example$manual/webapp/log4j2-single.json[tag=global]
----

YAML::
+
[source,yaml,indent=0]
----
include::example$manual/webapp/log4j2-single.yaml[tag=global]
----

Properties::
+
[source,properties,indent=0]
----
include::example$manual/webapp/log4j2-single.properties[tag=global]
----
====

* Logging events to a separate appender for each application.
In this case, you can use
xref:manual/appenders/delegating.adoc#RoutingAppender[routing appender]
to separate the events.
This kind of configuration might be used on the development server together with the
xref:manual/pattern-layout.adoc[human-friendly Pattern Layout]:
+
[tabs]
====
XML::
+
[source,xml,indent=0]
----
include::example$manual/webapp/log4j2-single.xml[tag=routing]
----

JSON::
+
[source,json,indent=0]
----
include::example$manual/webapp/log4j2-single.json[tag=routing]
----

YAML::
+
[source,yaml,indent=0]
----
include::example$manual/webapp/log4j2-single.yaml[tag=routing]
----

Properties::
+
[source,properties,indent=0]
----
include::example$manual/webapp/log4j2-single.properties[tag=routing]
----
====

[#multiple-contexts]
==== Multiple logger contexts

Since Log4j Core uses
link:javadoc/log4j-core/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.html[`ClassLoaderContextSelector`]
by default, no configuration is needed to achieve multiple logger contexts in your application server:
the classes of each classloader will use the logger context associated with the classloader.

[TIP]
====
To provide a different configuration file for each logger context, you can add files named `log4j2<contextName>.xml` to the classpath of your application server.

See <<log4jContextName>> and <<log4jConfiguration>> for more details.
====

Associating logger contexts to classloaders has, however, some limitations: **shared** libraries will not be able to use the per-application logger contexts.
To overcome this limitation, Log4j Core provides an alternative algorithm to determine the right logger context to choose: JNDI lookups.

[#jndi-configuration]
==== JNDI context selector

Application servers set up the correct JNDI context as soon as they determine which application will handle a request.
Log4j Core allows the usage of JNDI to coordinate the usage of logger contexts in a Jakarta EE application server.
To use this feature, you need to:

. Set the
xref:manual/systemproperties.adoc#log4j2.contextSelector[`log4j2.contextSelector`]
Log4j configuration property to ``org.apache.logging.log4j.core.selector.JndiContextSelector``,
. For security reasons you need to enable the selector, by setting the
xref:manual/systemproperties.adoc#log4j2.enableJndiContextSelector[`log4j2.enableJndiContextSelector`]
Log4j configuration property to `true`,
. Each web application needs to configure the servlet context parameter `isLog4jContextSelectorNamed` to `true` and provide a value for the `log4jContextName` servlet context parameter and `java:comp/env/log4j/context-name` JNDI environment entry:
+
.Snippet from an example {antora-examples-url}/manual/webapp/jndi.xml[`web.xml`]
[source,xml,indent=0]
----
include::example$manual/webapp/jndi.xml[tag=jndi]
----

[#replace]
== Replacing the application server logging subsystem

Some application servers allow administrators to replace the default logging subsystem of the application server with Log4j Core.
Known instructions are listed in the section.
If your application server is not listed here, check the documentation of the application server.

[#replace-tomcat]
=== Tomcat

Tomcat uses a modified version of Apache Commons Logging called
{tomcat-latest-url}/logging.html[Tomcat JULI]
as the internal logging system.
Tomcat JULI uses `java.util.logging` as default logging implementation,
but since Tomcat 8.5 you can replace it with a different backend.

To use Log4j Core as logging backend, you need to modify the
{tomcat-latest-url}/class-loader-howto.html#Class_Loader_Definitions[system classloader]
of the server.
Assuming `$CATALINA_BASE` is the main directory of your Tomcat instance you need to:

. Create a `$CATALINA_BASE/log4j` folder to contain Log4j dependencies,
. Download the following JAR files into `$CATALINA_BASE/log4j`:
* https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-appserver/{log4j-api-version}[`log4j-appserver`]: the bridge between Tomcat JULI and Log4j API,
* https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-api/{log4j-api-version}[`log4j-api`],
* https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-core/{log4j-core-version}[`log4j-core`].
. Add a Log4j Core xref:manual/configuration.adoc[configuration file] called either `log4j2.xml` or `log4j2-tomcat.xml` to the `$CATALINA_BASE/log4j` folder.
. Modify the system classloader classpath to include all the JAR files and the `$CATALINA_BASE/log4j` folder itself.
If you are starting Tomcat using the scripts in `$CATALINA_HOME/bin`, you can do it by creating a `$CATALINA_BASE/bin/setenv.sh` file with content:
+
[source,shell]
----
CLASSPATH="$CATALINA_HOME/log4j/*:$CATALINA_HOME/log4j/"
----
+
[TIP]
====
Windows users can modify the classpath using the
https://commons.apache.org/proper/commons-daemon/procrun.html[Procrun monitor application]
GUI application.
The application is traditionally located in `$CATALINA_HOME/bin/tomcat<n>w.exe`, where `<n>` is the major version number of Tomcat.
====

[#replace-jetty]
=== Jetty

In recent Jetty versions you just need to enable the `logging-log4j2` module.
See {jetty-latest-url}/operations-guide/modules/index.html[Jetty Modules documentation] for more details.

On Jetty 9.x or earlier you need to:

. Add the following JAR files to Jetty's classpath:
* https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-appserver/{log4j-api-version}[`log4j-appserver`],
* https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-api/{log4j-api-version}[`log4j-api`],
* https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-core/{log4j-core-version}[`log4j-core`].
. Set the system property `org.eclipse.jetty.util.log.class` to `org.apache.logging.log4j.appserver.jetty.Log4j2Logger`