Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Log4j2 AsyncAppender thread does not die on tomcat undeploy causing memory leak

The stack I am using at the moment is:

  • log4j2 rc1
  • spring 3.2 core and web
  • tomcat 7.0.47
  • java 1.6.0_45
  • Windows 7

I don't have the ability to alter the tomcat version or java version and I would prefer not to alter the log4j version and spring version.

Essentially, when I undeploy my webapp I receieve a SEVERE warning saying:

SEVERE: The web application [/MyApp] appears to have started a thread named
[AsyncAppender-AsyncFile] but has failed to stop it. This is very likely to 
create a memory leak

I can confirm that it does create a memory leak and this is what I am trying to fix.

So far I have attempted to create a custom ServletContextListener which contains the following code:

@Override
public void contextDestroyed(ServletContextEvent event) {
    System.out.println("Shutting down logger");
    try {
        ((Log4jWebSupport) event.getServletContext().getAttribute(
            Log4jWebSupport.SUPPORT_ATTRIBUTE)).clearLoggerContext();
        ((LifeCycle) LogManager.getContext()).stop();
    } catch (Exception e) {

    }
}

None of these two lines seems to fix the problem, however I can confirm that this code is being executed due to my sysout statement appearing in the tomcat console.

I am using log4j2 through an interceptor which I am setting up using Spring

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**" />
        <ref bean="LoggingInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>
<bean id="LoggingInterceptor" class="MyLoggerClass">

The interceptor works correctly and the logs appear where I expect them to. My implementation of the logger is:

private static Logger log = LogManager.getLogger("MetricLogger");

public void log(LogPayload payload) {
    if(payload != null){
        log.info(payload.get());
    }
}

Where LogPayload.get() returns a String.

As I am using the logging facility across multiple webapps I have created a separate jar file containing this and the classes recording the measurements. I have included this using maven and I compile it into the final war file I deploy to tomcat. This war file is included on a per app basis and is not included in the global tomcat/lib folder.

Does anyone have any insight as to why I get my memory leak issue and what the possible solutions are to fixing this?

Many thanks for your help and please let me know if you need further information.

like image 777
kipper_t Avatar asked Dec 09 '25 03:12

kipper_t


2 Answers

The solution to this that I have so far found is that I need to include the following snippet in the web.xml.

<listener>
    <listener-class>org.apache.logging.log4j.core.web.Log4jServletContextListener</listener-class>
</listener>

<filter>
    <filter-name>log4jServletFilter</filter-name>
    <filter-class>org.apache.logging.log4j.core.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>
</filter-mapping>

This is specific to servlet spec 2.5. This seems to resolve the memory leak issue.

like image 84
kipper_t Avatar answered Dec 10 '25 15:12

kipper_t


In case you are facing this issue recently you should note the following:

If you are using servlet 3.0+ and tomcat > 7.0.43 or Tomcat 8 there are no configuration needed, you just have to provide the correct maven dependencies to make log4j2 aware of running in a webcontainer like the following (in addition we use slf4j):

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.30</version>
</dependency>

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.11.0</version>
</dependency>

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.11.0</version>
</dependency>

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-web</artifactId>
    <version>2.11.0</version>
</dependency>

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.11.0</version>
</dependency>

Since in our case the log4j-web artifact was missing we faced the same warning which said "... started a thread named [AsyncAppender-AsyncFile] but has failed to stop it ..."

After providing the mentioned dependencies the warning is gone.

you can read more about requirements here

like image 37
Matthias Avatar answered Dec 10 '25 16:12

Matthias



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!