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-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org

Reply via email to