Description
Description
We have a web application packaged as a .war file deployed in Tomcat 11. The web application uses logback as the main logger and slf4j as a facade. Then we use log4j-to-slf4j as a bridge to redirect logs from dependencies using log4j2 to the logback logger. When we undeploy the web application or when we stop Tomcat, we see these line in the catalina.out file:
11-Jul-2025 15:29:48.796 SEVERE [Catalina-utility-5] org.apache.catalina.loader.WebappClassLoaderBase.checkThreadLocalMapForLeaks The web application [<app name>] created a ThreadLocal with key of type [java.lang.ThreadLocal.SuppliedThreadLocal] (value [java.lang.ThreadLocal$SuppliedThreadLocal@22437c61]) and a value of type [org.apache.logging.slf4j.SLF4JLogBuilder] (value [org.apache.logging.slf4j.SLF4JLogBuilder@2be61ed4]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
We investigated the issue and we found that in method org.apache.logging.slf4j.SLF4JLogger#getLogBuilder
there is this code:
protected LogBuilder getLogBuilder(final Level level) {
final SLF4JLogBuilder builder = logBuilder.get();
return Constants.ENABLE_THREADLOCALS && !builder.isInUse()
? builder.reset(this, level)
: new SLF4JLogBuilder(this, level);
}
Constants.ENABLE_THREADLOCALS
is false
since the code correctly detects that it is running in a web application, but still the line final SLF4JLogBuilder builder = logBuilder.get();
stores the initial value into the ThreadLocal. That value is then ignored and is never cleared.
The value of Constants.ENABLE_THREADLOCALS
should be checked before logBuilder.get()
is even called.
Configuration
Version: 2.25.0
Operating system: Any + Tomcat 11.0.6
JDK: 21.0.7
Logs
From catalina.out file:
11-Jul-2025 15:29:48.796 SEVERE [Catalina-utility-5] org.apache.catalina.loader.WebappClassLoaderBase.checkThreadLocalMapForLeaks The web application [<app name>] created a ThreadLocal with key of type [java.lang.ThreadLocal.SuppliedThreadLocal] (value [java.lang.ThreadLocal$SuppliedThreadLocal@22437c61]) and a value of type [org.apache.logging.slf4j.SLF4JLogBuilder] (value [org.apache.logging.slf4j.SLF4JLogBuilder@2be61ed4]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
Reproduction
- Create a web application using log4j-to-slf4j, slf4j and logback;
- Deploy the web application in tomcat 11.0.6 and execute code that uses a log4j2 logger to write something;
- Undeploy the web application and check the catalina.out file.
Metadata
Metadata
Assignees
Type
Projects
Status