Log4j2 AsyncAppender线程在tomcat取消部署时未终止,导致内存泄漏



我目前使用的堆栈是:

  • log4j2 rc1
  • 弹簧3.2型芯和腹板
  • tomcat 7.0.47
  • java 1.6.0_45
  • Windows 7

我没有能力更改tomcat版本或java版本,我更希望不更改log4j版本和spring版本。

本质上,当我取消部署我的网络应用程序时,我会收到一个严重的警告,上面写着:

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

我可以确认它确实造成了内存泄漏,这就是我正在努力解决的问题。

到目前为止,我已经尝试创建一个自定义Servlet上下文侦听器,其中包含以下代码:

@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()返回一个字符串。

当我在多个web应用程序中使用日志记录功能时,我创建了一个单独的jar文件,其中包含这个和记录测量的类。我已经使用maven将其包含在内,并将其编译到部署到tomcat的最终war文件中。此war文件按应用程序包含,不包含在全局tomcat/lib文件夹中。

有人知道我为什么会出现内存泄漏问题,以及解决这个问题的可能解决方案吗?

非常感谢您的帮助,如果您需要更多信息,请告诉我。

到目前为止,我找到的解决方案是,我需要在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特有的。这似乎解决了内存泄漏问题。

如果你最近遇到这个问题,你应该注意以下几点:

如果您使用的是servlet 3.0+和tomcat>7.0.43或Tomcat8不需要任何配置,您只需要提供正确的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工件丢失,我们面临着相同的警告,上面写着";。。。已启动名为[AncAppender AsyncFile]的线程,但未能停止它"

在提供了上述依赖关系之后,警告就消失了。

您可以在这里阅读更多关于需求的信息

添加以下代码段使我摆脱了由于异步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>

最新更新