Integrating with Jakarta EE
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 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 Integrating with web applications 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 Log4j Core between Web Applications for more details.
Integrating with web applications
To avoid problems, some Log4j API and Log4j Core features are automatically disabled when running in a Jakarta EE environment. Most notably:
See |
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.
Installation
To install Log4j Web in your web application, you need to add it as a runtime dependency:
-
Maven
-
Gradle
We assume you use log4j-bom
for dependency management.
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jakarta-web</artifactId>
<scope>runtime</scope>
</dependency>
We assume you use log4j-bom
for dependency management.
runtimeOnly 'org.apache.logging.log4j:log4j-jakarta-web'
Click here if you are you using Jakarta EE 8 or any version of Java EE?
Jakarta EE 8 and all Java EE applications servers use the legacy javax
package prefix instead of jakarta
.
If you are using those application servers, you should replace the dependencies above with:
-
Maven
-
Gradle
We assume you use log4j-bom
for dependency management.
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<scope>runtime</scope>
</dependency>
We assume you use log4j-bom
for dependency management.
runtimeOnly 'org.apache.logging.log4j:log4j-web'
If you are writing a Servlet 3.0 or later application, Apache Log4j Web will register a
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 web lookup.
See also Application server specific notes.
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 Section 8.2.3). If other context listeners in your application use logging, you need to make sure that Snippet from an example
web.xml
xml |
Manual installation
If you are maintaining an older Servlet 2.5 (or earlier) application, or if you disabled the servlet container initializer.
web.xml
<listener>
<description>Handles Log4j Core lifecycle</description>
<listener-class>
org.apache.logging.log4j.web.Log4jServletContextListener
</listener-class>
</listener>
<filter>
<description>Adds Log4j Core specific attributes to each request</description>
<filter-name>log4jServletFilter</filter-name>
<filter-class>org.apache.logging.log4j.web.Log4jServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>log4jServletFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
<!-- Servlet 3.0 with disabled auto-initialization; not supported in 2.5
<dispatcher>ASYNC</dispatcher>
-->
</filter-mapping>
Configuration
Log4j Web provides many configuration options to finely tune its installation. These configuration options should be specified as servlet context initialization parameters.
isLog4jAutoInitializationDisabled
Type |
|
---|---|
Default value |
|
If set to true
, the Log4jServletContainerInitializer
will be disabled, which prevents the automatic registration of both the Log4jServletContextListener
and Log4jServletFilter
.
isLog4jAutoShutdownDisabled
Type |
|
---|---|
Default value |
|
If set to true
, the Log4jServletContextListener
will not register a Log4jServletContextListener
to handle the web application shut down.
log4jContextName
Type |
|
---|---|
Default value |
automatically computed |
Used to specify the name of the logger context.
If 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.
isLog4jContextSelectorNamed
Type |
|
---|---|
Default value |
|
Must be set to true
to use the JNDI configuration.
log4jConfiguration
Type |
|
---|---|
Default value |
|
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 servlet context resource,
-
the path to an existing file,
-
the path to a 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 automatic configuration procedure.
Asynchronous requests and threads
In order for the
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
AsyncContext.start(Runnable)
method.
To successfully propagate the logger context along asynchronous calls, the
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:
AsyncServlet.java
AsyncContext asyncContext = req.startAsync();
asyncContext.start(WebLoggerContextUtils.wrapExecutionContext(getServletContext(), () -> {
// Put your logic here
}));
or, if more flexibility is required, you can apply the same logic by using
Log4jWebSupport
:
AsyncServlet.java
AsyncContext asyncContext = req.startAsync();
Log4jWebSupport webSupport = WebLoggerContextUtils.getWebLifeCycle(getServletContext());
asyncContext.start(() -> {
try {
webSupport.setLoggerContext();
// Put your logic here
} finally {
webSupport.clearLoggerContext();
}
});
Logging in JavaServer Pages
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 the official support channels. |
To help users add logging statements to JavaServer Pages, Log4j provides a JSP tag library modeled after the Jakarta Commons Log Tag library. To use it, you need to add the following runtime dependency to your web application project:
-
Maven
-
Gradle
We assume you use log4j-bom
for dependency management.
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-taglib</artifactId>
<scope>runtime</scope>
</dependency>
We assume you use log4j-bom
for dependency management.
runtimeOnly 'org.apache.logging.log4j:log4j-taglib'
and add the following declaration to your JSP pages:
<%@ taglib prefix="log4j" uri="http://logging.apache.org/log4j/tld/log" %>
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
Logger
methods, including:
-
simple and parameterized log statements:
Snippet from an exampletaglib.jsp
<log4j:debug message="Simple message"/> <log4j:info message="Hello {}!" p0="${param.who}"/> <log4j:warn message="Message with marker" marker="${requestScope.marker}"/>
xml -
flow tracing statements:
Snippet from an exampletaglib.jsp
<log4j:entry p0="${param.who}"/> <log4j:exit/>
xml -
catching and throwing statements:
Snippet from an exampletaglib.jsp
<c:catch var="exception"> <%= 5 / 0 %> </c:catch> <c:if test="${exception != null}"> <log4j:catching exception="${exception}"/> </c:if>
xml -
tags to test the current log level:
<log4j:ifEnabled level="INFO"> <code>INFO</code> is enabled. </log4j:ifEnabled>
xml -
tags to set the name of the logger used:
Snippet from an exampletaglib.jsp
<log4j:setLogger logger="example.jsp"/>
xml -
a
dump
tag that prints the contents of a JSP scope:Snippet from an exampletaglib.jsp
<log4j:dump scope="request"/>
xml
Application server specific notes
- WildFly
-
WildFly implicitly adds a shared copy of
log4j-api
to each web application deployment. This copy oflog4j-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 tofalse
. See WildFly documentation for more details.
Sharing Log4j Core between Web Applications
Since Log4j Core supports multiple 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 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.
Setup
To share Log4j Core between applications, you need to share at least these two JAR files:
-
log4j-jakarta-web
(orlog4j-web
if you use a Java EE application server)
Since sharing libraries between applications is not part of the Jakarta EE standard, the instructions are specific to each application server:
-
GlassFish
-
Jetty
-
OpenLiberty
-
Payara
-
Tomcat
-
WildFly
In GlassFish, you can add those libraries to the common classloader. See GlassFish documentation for more details.
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 Modules documentation for more details.
In OpenLiberty, you can add Log4j as a global library. See OpenLiberty documentation for more details.
In Tomcat, you can use the common classloader. See Tomcat classloader documentation for more details.
You can install Log4j as a global module or in a global directory. See WildFly EE Application Deployment documentation for more details.
Check also the WildFly note above.
Web application classloaders (see 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:
|
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
Logger
instances into logging domains called
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 logger contexts for more details.
-
You can also use a single logger context for all log events, but use lookups to add context data to your log events. See Single logger context for more details.
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:
Since the first kind of libraries is more common, counterintuitively the Single logger context approach will usually give better results than the Multiple logger contexts approach. |
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
log4j2.contextSelector
system property to:
-
either
org.apache.logging.log4j.core.selector.BasicContextSelector
to use synchronous loggers, -
or
org.apache.logging.log4j.core.async.BasicAsyncLoggerContextSelector
to use asynchronous loggers.
In this approach, you must use 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 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 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., JSON Template Layout) 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:
-
XML
-
JSON
-
YAML
-
Properties
<File name="GLOBAL" fileName="logs/global.log"> <JsonTemplateLayout> <EventTemplateAdditionalField key="contextName" value="$${web:contextName}"/> </JsonTemplateLayout> </File>
xml"File": { "name": "GLOBAL", "fileName": "logs/global.log", "JsonTemplateLayout": { "EventTemplateAdditionalField": { "key": "contextName", "value": "$${web:contextName}" } } },
jsonFile: name: "GLOBAL" fileName: "logs/global.log" JsonTemplateLayout: EventTemplateAdditionalField: key: "contextName", value: "$${web:contextName"
yamlappender.0.type = File appender.0.name = GLOBAL appender.0.fileName = logs/global appender.0.layout.type = JsonTemplateLayout appender.0.layout.0.type = EventTemplateAdditionalField appender.0.layout.0.key = contextName appender.0.layout.0.value = $${web:contextName}
properties -
-
Logging events to a separate appender for each application. In this case, you can use routing appender to separate the events. This kind of configuration might be used on the development server together with the human-friendly Pattern Layout:
-
XML
-
JSON
-
YAML
-
Properties
<Routing name="ROUTING"> <Routes pattern="$${web:contextName:-common}"> <Route> <File name="${web:contextName:-common}" fileName="logs/${web:contextName:-common}.log"> <PatternLayout pattern="%d [%t] %-5p %c - %m%n"/> </File> </Route> </Routes> </Routing>
xml"Routing": { "name": "ROUTING", "Routes": { "pattern": "$${web:contextName:-common}", "Route": { "File": { "name": "${web:contextName:-common}", "fileName": "logs/${web:contextName:-common}.log", "PatternLayout": { "pattern": "d [%t] %-5p %c - %m%n" } } } } }
jsonRouting: name: "ROUTING" Routes: pattern: "$${web:contextName:-common}" File: name: "${web:contextName:-common}" fileName: "logs/${web:contextName:-common}.log" PatternLayout: pattern: "%d [%t] %-5p %c - %m%n"
yamlappender.1.type = Routing appender.1.name = ROUTING appender.1.route.type = Routes appender.1.route.pattern = $${web:contextName:-common} appender.1.route.0.type = Route appender.1.route.0.appender.type = File appender.1.route.0.appender.name = ${web:contextName:-common} appender.1.route.0.appender.fileName = logs/${web:contextName:-common}.log appender.1.route.0.appender.layout.type = PatternLayout appender.1.route.0.appender.layout.pattern = %d [%t] %-5p %c - %m%n
properties -
Multiple logger contexts
Since Log4j Core uses
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.
To provide a different configuration file for each logger context, you can add files named See |
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 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
log4j2.contextSelector
Log4j configuration property toorg.apache.logging.log4j.core.selector.JndiContextSelector
, -
For security reasons you need to enable the selector, by setting the
log4j2.enableJndiContextSelector
Log4j configuration property totrue
, -
Each web application needs to configure the servlet context parameter
isLog4jContextSelectorNamed
totrue
and provide a value for thelog4jContextName
servlet context parameter andjava:comp/env/log4j/context-name
JNDI environment entry:Snippet from an exampleweb.xml
<context-param> <param-name>isLog4jContextSelectorNamed</param-name> <param-value>true</param-value> </context-param> <context-param> <param-name>log4jContextName</param-name> <param-value>your_application_name</param-value> </context-param> <env-entry> <env-entry-name>log4j/context-name</env-entry-name> <env-entry-value>your_application_name</env-entry-value> <env-entry-type>java.lang.String</env-entry-type> </env-entry>
xml
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.
Tomcat
Tomcat uses a modified version of Apache Commons Logging called
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
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
:-
log4j-appserver
: the bridge between Tomcat JULI and Log4j API,
-
-
Add a Log4j Core configuration file called either
log4j2.xml
orlog4j2-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:CLASSPATH="$CATALINA_HOME/log4j/*:$CATALINA_HOME/log4j/"
shellWindows users can modify the classpath using the 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.
Jetty
In recent Jetty versions you just need to enable the logging-log4j2
module.
See Jetty Modules documentation for more details.
On Jetty 9.x or earlier you need to:
-
Add the following JAR files to Jetty’s classpath:
-
Set the system property
org.eclipse.jetty.util.log.class
toorg.apache.logging.log4j.appserver.jetty.Log4j2Logger