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