You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tomcat.apache.org by Bruno Melloni <x....@melloni.com> on 2025/03/10 17:01:59 UTC

Has Tomcat 10 dropped support for using log4j2 as its default logger?

*TLDR Question*:  Has Tomcat 10 dropped support for using log4j2 as its 
default logger?

Details:

I am wondering because after easily a decade of using log4j with 
Tomcat... it is now virtually impossible to find instructions (_that 
actually work_) for how to configure Tomcat 10 to use *Apache Log4j* as 
its default logger. Since both are Tomcat projects, the only explanation 
I could come up with is that maybe Apache Log4j is considered a dead 
project by Apache even if not announced as so.

And even though I think I figured out a configuration that appears to 
work, when I call shutdown.bat (and in a few other scenarios) I get the 
error:

*class org.apache.logging.log4j.jul.LogManager cannot be cast to class 
java.util.logging.Handler (org.apache.logging.log4j.jul.LogManager is in 
unnamed module of loader 'app'; java.util.logging.Handler is in module 
java.logging of loader 'bootstrap')*

I am using Java 17, Tomcat 10.1.7 and log4j 2.20.0.

Re: SOLVED (90%): Re: Has Tomcat 10 dropped support for using log4j2 as its default logger?

Posted by "Piotr P. Karwasz" <pi...@mailing.copernik.eu>.
Hi Bruno,

On 13.03.2025 21:30, Bruno Melloni wrote:
> ·This configuration is focused on *Windows* and routes *BOTH Tomcat’s 
> logging and the web applications logging* to the Tomcat log4j2 config 
> file.Most examples on the internet are only for one or the other, and 
> are usually described in Linux/Unix terms.

To be precise, these instruction address a Windows installation that 
starts Tomcat using `bin\startup.bat` or `bin\catalina.bat`. From my 
experience, many Windows users use the Windows Service Installer, which 
runs Tomcat as a Windows service and ignores all the `bin\*.bat` files. 
Those users need slightly different instructions.


> *Tomcat Configuration*
>
> ·Create log4j2 folder structure in the tomcat folder
>
> %CATALINA_HOME%\log4j2
>
> .\conf
>
> .\lib
>
> ·Place log4j2-tomcat.xml in *.\conf*
>
> ·Place the desired version of jars in *.\lib*.Include log4j-api, 
> log4j-core, log4j-appserver, log4j-jul, log4j-web.

Since we are talking about Tomcat 10, you need to use 
`log4j-jakarta-web` instead of `log4j-web`.

Both `log4j-jakarta-web` and `log4j-web` depend on the Servlet API, so 
the need to be in the _Common_ Tomcat classloader[1]: place 
`log4j-jakarta-web` in `%CATALINA_HOME\lib` instead of 
`%CATALINA_HOME\log4j2\lib`.

[1] https://tomcat.apache.org/tomcat-10.1-doc/class-loader-howto.html

> ·Edit logging.properties:
>
> oComment the handlers line and replace it with:
>
> handlers=org.apache.logging.log4j.jul.LogManager
>
> ·Create setenv.bat with the following content.Edit the location of the 
> configuration file to match the one you are using.NOTE that we are 
> *NOT using %CATALINA_HOME% for the configurationFile location*, we are 
> explicitly spelling it out.This is to avoid conflicts between 
> Windows’s path format and Java’s.
>
> set "CLASSPATH=%CATALINA_HOME%\log4j2\lib\*;%CATALINA_HOME%\log4j2\conf"
>
> set CATALINA_OPTS=%CATALINA_OPTS% 
> -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager 
> -Dlog4j.configurationFile=file:/D:/work/soft/tomcat-10.1.7-j17corretto/log4j2/conf/log4j2-tomcat.xml

Now I believe I understand, why you are so insistent on modifying 
`logging.properties`. The `catalina.bat` script has a special variable 
to set the JUL LogManager: %LOGGING_MANAGER%.

If you use %CATALINA_OPTS% to set the `java.util.logging.manager` 
variable you'll end up setting the variable **twice** and the JVM 
options will look like:


-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager 
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager


I am not sure which definition will be used by the JVM, but my 
experiments show that the **first** one is used and second one ignored. 
This means that your system does **not** use Log4j Core for JUL, 
`logging.properties` is still used by some libraries and this causes 
your ClassCastException.

Solution: do not set `java.util.logging.manager` in %CATALINA_OPTS%, but 
use %LOGGING_MANAGER% instead:


set 
LOGGING_MANAGER=-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager


### Configuration location

If you want all installations of Log4j Core in your system to use the 
same configuration file, call it `log4j2.xml` instead of 
`log4j2-tomcat.xml`:

* Only `log4j-appserver` uses `log4j2-tomcat.xml` and it uses it to 
initialize the "%CATALINA_HOME%\log4j2\lib\log4j-core.jar" copy of Log4j 
Core. If `log4j-tomcat.xml` is not found, it falls back on the standard 
configuration file detection procedure[2]. This mean `log4j2.xml` will 
be discovered.

* The `log4j-jakarta-web` initializers in your web applications will use 
a different procedure[3] to initialize the embedded 
`WEB-INF\lib\log4j-core.jar` copies of Log4j Core.  This process also 
falls back on the standard procedure and `log4j2.xml` will be discovered.

[2] 
https://logging.apache.org/log4j/2.x/manual/configuration.html#automatic-configuration

[3] https://logging.apache.org/log4j/2.x/jakarta.html#log4jConfiguration

>
> *Webapp Configuration*
>
> (this example was tested with a simple SpringBoot REST service that 
> deploys as a WAR on Tomcat, but should also work for web applications)
>
> ·Maven POM should include the following in the appropriate locations
>
> ·<dependency>
> <groupId>org.apache.logging.log4j</groupId>
> <artifactId>log4j-api</artifactId>
> <version>${log4j2.version}</version>
> </dependency>
> <dependency>
> <groupId>org.apache.logging.log4j</groupId>
> <artifactId>log4j-core</artifactId>
> <version>${log4j2.version}</version>
> </dependency>
> <dependency>
> <groupId>org.apache.logging.log4j</groupId>
> <artifactId>log4j-slf4j-impl</artifactId>
> <version>${log4j2.version}</version>
> </dependency>
> <dependency>
> <groupId>org.slf4j</groupId>
> <artifactId>slf4j-api</artifactId>
> <version>${slf4j.version}</version>
> </dependency>
> <dependency>
> <groupId>org.apache.logging.log4j</groupId>
> <artifactId>log4j-jakarta-web</artifactId>
> <version>${log4j2.version}</version>
> <scope>runtime</scope>
> </dependency>

Are these web applications destined to be used on a generic application 
server or just the Log4j Core-enhanced installation Tomcat?

If it's the latter, you can move `log4j-api`, `log4j-core` and 
`log4j-jakarta-web` to the `provided` Maven scope. The libraries will 
not be packaged in the WAR file and you'll be able to profit from the 
advantages of using a single installation of Log4j Core: for example 
multiple applications will be able to use log into the same rolled log file.

If it's the former, I would recommend moving `log4j-core`, 
`log4j-slf4j-impl` and one of `log4j-api` or `slf4j-api` (the one you 
don't use in code) to the `runtime` Maven scope.

Note: if `slf4j.version` is greater or equal to `2.0.0`, you need to 
replace `log4j-slf4j-impl` with `log4j-slf4j2-impl`.

> ·Include the class Log4jContextListener below into the project.You may 
> drop the System.out output, but I found them to be convenient for 
> troubleshooting.
>
> ·@WebListener public class Log4jContextListener implements 
> ServletContextListener { @Override public void 
> contextInitialized(ServletContextEvent sce) { String log4jConfigFile = 
> System./getProperty/("log4j.configurationFile"); if(log4jConfigFile != 
> null && log4jConfigFile.startsWith("file:/")) { log4jConfigFile = 
> log4jConfigFile.substring(6); } File file = new File(log4jConfigFile); 
> if(!file.exists()) { System./out/.println("Log4j config file 
> "+log4jConfigFile+" does not exist"); }else{ 
> System./out/.println("Log4j config file "+log4jConfigFile+" exists"); 
> } LoggerContext ctx = (LoggerContext) LogManager./getContext/(false); 
> ctx.setConfigLocation(file.toURI()); } }

I wouldn't recommend this:

* Log4j Core already supports setting the location of the configuration 
file using the `log4j2.configurationFile`[4] system property 
(`log4j.configurationFile` is currently also supported for backward 
compatibility).

* The standard `log4j2.configurationFile` supports both file paths (e.g. 
"D:\work\soft\tomcat-10.1.7-j17corretto\log4j2\conf\log4j2-tomcat.xml") 
and URIs, so you don't need to express the location as URI.

* Your code will fail with a `ClassCastException`, if a user replaces 
Log4j Core with a different implementation of the Log4j API. This 
exception will prevent your application from starting.

Piotr


[4] 
https://logging.apache.org/log4j/2.x/manual/systemproperties.html#log4j2.configurationFile


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


SOLVED (90%): Re: Has Tomcat 10 dropped support for using log4j2 as its default logger?

Posted by Bruno Melloni <x....@melloni.com>.
I found a solution.  It is not perfect but it works.

Man, I miss the old days of simple log4j-tomcat configurations.

Feel free to use, reproduce, modify (especially if you have 
improvements) as you wish.


*Configuring Tomcat for log4j2 logging*

*NOTES*:

·A simpler configuration might be possible.

·This configuration is focused on *Windows* and routes *BOTH Tomcat’s 
logging and the web applications logging* to the Tomcat log4j2 config 
file.Most examples on the internet are only for one or the other, and 
are usually described in Linux/Unix terms.

·This configuration does not prevent the following exception when Tomcat 
is stops:

Handler error

java.lang.ClassCastException: class 
org.apache.logging.log4j.jul.LogManager cannot be cast to class 
java.util.logging.Handler (org.apache.logging.log4j.jul.LogManager is in 
unnamed module of loader 'app'; java.util.logging.Handler is in module 
java.logging of loader 'bootstrap')

*Environment variables*

·%CATALINA_HOME% refers to the environment variable that Tomcat 
instructs you to create.

*Tomcat Configuration*

·Create log4j2 folder structure in the tomcat folder

%CATALINA_HOME%\log4j2

.\conf

.\lib

·Place log4j2-tomcat.xml in *.\conf*

·Place the desired version of jars in *.\lib*.Include log4j-api, 
log4j-core, log4j-appserver, log4j-jul, log4j-web.

·Edit logging.properties:

oComment the handlers line and replace it with:

handlers=org.apache.logging.log4j.jul.LogManager

·Create setenv.bat with the following content.Edit the location of the 
configuration file to match the one you are using.NOTE that we are *NOT 
using %CATALINA_HOME% for the configurationFile location*, we are 
explicitly spelling it out.This is to avoid conflicts between Windows’s 
path format and Java’s.

set "CLASSPATH=%CATALINA_HOME%\log4j2\lib\*;%CATALINA_HOME%\log4j2\conf"

set CATALINA_OPTS=%CATALINA_OPTS% 
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager 
-Dlog4j.configurationFile=file:/D:/work/soft/tomcat-10.1.7-j17corretto/log4j2/conf/log4j2-tomcat.xml

*Webapp Configuration*

(this example was tested with a simple SpringBoot REST service that 
deploys as a WAR on Tomcat, but should also work for web applications)

·Maven POM should include the following in the appropriate locations

·<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jakarta-web</artifactId>
<version>${log4j2.version}</version>
<scope>runtime</scope>
</dependency>

·And note the <exclusions> portion of spring-boot-starter.It is 
necessary to avoid the default spring boot logging mechanism so that 
log4j can be used instead.

·<dependency> <groupId>org.springframework.boot</groupId> 
<artifactId>spring-boot-starter</artifactId> <exclusions> <exclusion> 
<groupId>org.springframework.boot</groupId> 
<artifactId>spring-boot-starter-logging</artifactId> </exclusion> 
</exclusions> </dependency>

·Include the class Log4jContextListener below into the project.You may 
drop the System.out output, but I found them to be convenient for 
troubleshooting.

·@WebListener public class Log4jContextListener implements 
ServletContextListener { @Override public void 
contextInitialized(ServletContextEvent sce) { String log4jConfigFile = 
System./getProperty/("log4j.configurationFile"); if(log4jConfigFile != 
null && log4jConfigFile.startsWith("file:/")) { log4jConfigFile = 
log4jConfigFile.substring(6); } File file = new File(log4jConfigFile); 
if(!file.exists()) { System./out/.println("Log4j config file "+log4jConfigFile+" does not exist"); }else{ 
System./out/.println("Log4j config file "+log4jConfigFile+" exists"); } 
LoggerContext ctx = (LoggerContext) LogManager./getContext/(false); 
ctx.setConfigLocation(file.toURI()); } }

*IntelliJ Configuration*

·With the above configuration there is no longer a need to use a special 
IntelliJ Run/Debug configuration for log4j2 to work properly.

·Not only should proper logging end up in the log files, the console 
display should also respect the logging levels and be formatted as per 
the patterns specified in log4j2-tomcat.xml.



----


On 3/11/2025 6:06 PM, Piotr P. Karwasz wrote:
> Hi Mark,
>
> On 11.03.2025 09:46, Mark Thomas wrote:
>> On 10/03/2025 21:58, Piotr P. Karwasz wrote:
>>
>> <snip/>
>>
>>> If you are looking for instructions on how to replace Tomcat's 
>>> default logging backend with Log4j Core, there is a dedicated 
>>> section[2] in our new Integrating Log4j Core with Jakarta EE Guide[3]
>>>
>>> [2] https://logging.apache.org/log4j/2.x/jakarta.html#replace-tomcat
>>
>> Thanks for this. I have added this link to Tomcat's logging 
>> documentation and it will be in the April release round.
>
> Thanks!
>
> If someone from the Tomcat team could review the "Integrating with 
> Jakarta EE"[1] page, I would appreciate it. For example I would like 
> to hear your thoughts about the log separation section[2]. Tomcat 
> documentation says:
>
> "Keep in mind that the alternative logging framework will need to be 
> capable of working in an environment where different loggers with the 
> same name may exist in different class loaders."
>
> However these days only a few logging frameworks support separate 
> loggers for each class loader. Logback disabled support for multiple 
> logger contexts a couple of years ago.
>
> The main problem is that libraries in the common classloader often use 
> static logger fields instead of having a different logger per web 
> application. Tomcat's own libraries seem to be an exception to this 
> rule and each Valve uses a per-application logger, but implementations 
> of many Jakarta specifications (e.g. Hibernate) do not follow this 
> pattern.
>
> It seems rather useless to fix those libraries, so my personal 
> recommendation is to use a single logger context for the entire JVM 
> and just add some context data to the logs.
>
> Piotr
>
> [1] https://logging.apache.org/log4j/2.x/jakarta.html
>
> [2] https://logging.apache.org/log4j/2.x/jakarta.html#log-separation
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
> For additional commands, e-mail: users-help@tomcat.apache.org
>

Re: Has Tomcat 10 dropped support for using log4j2 as its default logger?

Posted by "Piotr P. Karwasz" <pi...@mailing.copernik.eu>.
Hi Mark,

On 11.03.2025 09:46, Mark Thomas wrote:
> On 10/03/2025 21:58, Piotr P. Karwasz wrote:
>
> <snip/>
>
>> If you are looking for instructions on how to replace Tomcat's 
>> default logging backend with Log4j Core, there is a dedicated 
>> section[2] in our new Integrating Log4j Core with Jakarta EE Guide[3]
>>
>> [2] https://logging.apache.org/log4j/2.x/jakarta.html#replace-tomcat
>
> Thanks for this. I have added this link to Tomcat's logging 
> documentation and it will be in the April release round.

Thanks!

If someone from the Tomcat team could review the "Integrating with 
Jakarta EE"[1] page, I would appreciate it. For example I would like to 
hear your thoughts about the log separation section[2]. Tomcat 
documentation says:

"Keep in mind that the alternative logging framework will need to be 
capable of working in an environment where different loggers with the 
same name may exist in different class loaders."

However these days only a few logging frameworks support separate 
loggers for each class loader. Logback disabled support for multiple 
logger contexts a couple of years ago.

The main problem is that libraries in the common classloader often use 
static logger fields instead of having a different logger per web 
application. Tomcat's own libraries seem to be an exception to this rule 
and each Valve uses a per-application logger, but implementations of 
many Jakarta specifications (e.g. Hibernate) do not follow this pattern.

It seems rather useless to fix those libraries, so my personal 
recommendation is to use a single logger context for the entire JVM and 
just add some context data to the logs.

Piotr

[1] https://logging.apache.org/log4j/2.x/jakarta.html

[2] https://logging.apache.org/log4j/2.x/jakarta.html#log-separation



---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: Has Tomcat 10 dropped support for using log4j2 as its default logger?

Posted by Mark Thomas <ma...@apache.org>.
On 10/03/2025 21:58, Piotr P. Karwasz wrote:

<snip/>

> If you are looking for instructions on how to replace Tomcat's default 
> logging backend with Log4j Core, there is a dedicated section[2] in our 
> new Integrating Log4j Core with Jakarta EE Guide[3]
> 
> [2] https://logging.apache.org/log4j/2.x/jakarta.html#replace-tomcat

Thanks for this. I have added this link to Tomcat's logging 
documentation and it will be in the April release round.

Mark


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: Has Tomcat 10 dropped support for using log4j2 as its default logger?

Posted by "Piotr P. Karwasz" <pi...@mailing.copernik.eu>.
Hi Bruno,

On 10.03.2025 18:01, Bruno Melloni wrote:
> I am wondering because after easily a decade of using log4j with 
> Tomcat... it is now virtually impossible to find instructions (_that 
> actually work_) for how to configure Tomcat 10 to use *Apache Log4j* 
> as its default logger. Since both are Tomcat projects, the only 
> explanation I could come up with is that maybe Apache Log4j is 
> considered a dead project by Apache even if not announced as so.

The rumors about Apache Log4j death are greatly exaggerated. The easiest 
way to verify the status of an Apache project is to read the quarterly 
reports of the overseeing PMC[1].

There are two reasons, why Log4j and Tomcat are not more closely integrated:

1. Apache PMCs are very independent and there is no Foundation-wide rule 
that obliges a PMC to prefer dependencies produced by other Apache projects.

2. There are not many user requests to more closely integrate the two 
projects.

[1] https://whimsy.apache.org/board/minutes/Logging_Services.html


If you are looking for instructions on how to replace Tomcat's default 
logging backend with Log4j Core, there is a dedicated section[2] in our 
new Integrating Log4j Core with Jakarta EE Guide[3]

[2] https://logging.apache.org/log4j/2.x/jakarta.html#replace-tomcat

[3] https://logging.apache.org/log4j/2.x/jakarta.html


The above instructions only redirect server logs to Log4j Core, while 
web applications will use whatever logging backend they bundle. If you 
are looking for a tighter integration between server logs and webapp 
logs, I wrote a couple of Log4j Core Plugins and Tomcat Components[4] to 
better integrate the two. Basically what you need to do is:

1. Use a special web application classloader[5] that delegates the 
loading of `org.apache.logging.log4j.*` classes to the parent 
classloader (the standard classloader only delegates the loading of 
`jakarta.*` and `org.apache.tomcat.*` classes to the parent).

2. Use some strategy to distinguish between the logs sent by different 
web applications. There are basically two strategies available:

     * You can split your loggers in logger contexts using an 
appropriate Log4j Core context selector[6]. This way each web 
application can have its own configuration file (but doesn't need to). 
The naming strategy for logging configuration files is inspired by 
Tomcat's context configuration naming convention.

     * You can use a single logger context and use a Log4j Core context 
data provider[7] to enrich each message with the name of the context 
that generated it. This is probably the best strategy if you forward 
your logs to ElasticSearch or similar.

TL;DR: There is a short guide for my extensions at [8].

[4] https://oss.copernik.eu/tomcat/3.x/

[5] https://oss.copernik.eu/tomcat/3.x/components/tomcat-log4j#classloaders

[6] 
https://oss.copernik.eu/tomcat/3.x/components/log4j-tomcat#TomcatContextSelector

[7] 
https://oss.copernik.eu/tomcat/3.x/components/log4j-tomcat#TomcatContextDataProvider

[8] https://oss.copernik.eu/tomcat/3.x/guide

> And even though I think I figured out a configuration that appears to 
> work, when I call shutdown.bat (and in a few other scenarios) I get 
> the error:
>
> *class org.apache.logging.log4j.jul.LogManager cannot be cast to class 
> java.util.logging.Handler (org.apache.logging.log4j.jul.LogManager is 
> in unnamed module of loader 'app'; java.util.logging.Handler is in 
> module java.logging of loader 'bootstrap')*
>
> I am using Java 17, Tomcat 10.1.7 and log4j 2.20.0.

You are probably trying to use `o.a.l.l.jul.LogManager` in the 
`logging.properties` configuration file. To replace the default 
implementation of `java.util.logging.LogManager` you need to add:

LOGGING_MANAGER="org.apache.logging.log4j.jul.LogManager"

to your `setenv.sh` file. See the documentation on Log4j's website[9].


As alternative you can keep the default `o.a.juli.ClassLoaderLogManager` 
and use a single `org.apache.logging.log4j.jul.Log4jBridgeHandler` in 
your `logging.properties` file. This method introduces a considerable 
overhead to logging, so you should also set the `propagateLevels` 
property to `true` to mitigate it. See [10] for more details.

[9] https://logging.apache.org/log4j/2.x/log4j-jul.html#bridge-logmanager

[10] https://logging.apache.org/log4j/2.x/log4j-jul.html#bridge-handler


Piotr


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org