Log4j2 AsyncAppender线程不会因tomcat取消部署而导致内存泄漏

时间:2014-04-29 08:40:18

标签: java tomcat spring-mvc memory-leaks log4j2

我目前使用的堆栈是:

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

我没有能力改变tomcat版本或java版本,我宁愿不改变log4j版本和spring版本。

基本上,当我取消部署我的webapp时,我收到了严重的警告:

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

我可以确认它确实会造成内存泄漏,这就是我要解决的问题。

到目前为止,我尝试创建一个包含以下代码的自定义ServletContextListener:

@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) {

    }
}

这两行似乎都无法解决问题,但是由于我的sysout语句出现在tomcat控制台中,我可以确认此代码正在执行。

我通过拦截器使用log4j2,我使用Spring设置

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

拦截器正常工作,日志出现在我期望的位置。我对记录器的实现是:

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

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

LogPayload.get()返回字符串。

当我在多个webapps上使用日志记录工具时,我创建了一个单独的jar文件,其中包含记录测量结果的类和记录测量的类。我已经使用maven包含了这个,我将它编译成我部署到tomcat的最终war文件。此war文件包含在每个应用程序的基础上,不包含在全局tomcat / lib文件夹中。

有没有人知道为什么我会遇到内存泄漏问题以及解决此问题的可能解决方案是什么?

非常感谢您的帮助,如果您需要更多信息,请与我们联系。

3 个答案:

答案 0 :(得分:1)

我到目前为止发现的解决方案是我需要在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>

这是servlet规范2.5特有的。这似乎解决了内存泄漏问题。

答案 1 :(得分:0)

添加以下代码片段有助于我摆脱内存泄漏,因为Async-Appender线程即使在应用程序关闭后也没有关闭。 谢谢@kipper_t。

<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>

答案 2 :(得分:0)

如果您最近遇到此问题,则应注意以下几点:

如果您使用的是Servlet 3.0+和tomcat> 7.0.43或Tomcat 8,则不需要进行配置,您只需要提供正确的maven依赖关系,即可使log4j2知道像以下所示在web容器中运行(此外,使用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>

由于在我们的案例中缺少log4j-web工件,因此我们面临着同样的警告,它说“ ...启动了一个名为[AsyncAppender-AsyncFile]的线程,但未能将其停止...” >

提供上述依赖项后,警告消失了。

您可以阅读有关要求here

的更多信息