log4j2无法注册关闭挂钩,因为JVM正在关闭。

9

我正在尝试在基于Tomcat的Web应用程序中使用log4j2,因此我添加了log4j web模块以及其他必要的jar包。但是当停止这个Web应用程序时,我会遇到以下异常。

FATAL Unable to register shutdown hook because JVM is shutting down

为什么会出现这个错误,我该如何避免?谢谢!
3个回答

16

正如Pouriya的回答所解释的那样,您可能在没有适当的关闭挂钩的情况下尝试使用Log4j2。 由于您正在谈论Tomcat Web应用程序,我假设您正在使用Servlets。(如果不是,请参见下面的第二部分)。然后,您必须在ContextListener中声明这个:

public class ContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent evt) {
        String appenderName = evt.getServletContext().getInitParameter("log4jContextName");
        File file = new File(evt.getServletContext().getInitParameter("log4jConfiguration"));
        if(!file.exists()){
            logger = LogManager.getRootLogger();
        } else{
            logger = LogManager.getLogger(appenderName);
        }
    }
}

@Override
public void contextDestroyed(ServletContextEvent evt) {
    // You don't really need to do anything here, about the logger.
    // The log4j2 JAR will handle it properly.
}

通常情况下,使用Web应用程序时,Log4J2的配置文件必须在web.xml文件中指定,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<web-app ... >
    <context-param>
        <param-name>log4jContextName</param-name>
        <param-value>myApplication</param-value>
    </context-param>

    <context-param>
        <param-name>log4jConfiguration</param-name>
        <param-value>/the/path/to/your/log4j.properties.file</param-value>
    </context-param>
</web-app>

如果您使用的是普通应用程序,则需要显式添加关闭挂钩:
public static void main( String[] args ) {

    ...

    File file = new File(logFileName);
    if(!file.exists()){
        logger = LogManager.getRootLogger();
    } else {
        System.setProperty("log4j.configurationFile", file.toURI().toURL().toString());
        logger = LogManager.getLogger("yourProgramName");
    }

    Runtime.getRuntime().addShutdownHook(new Thread() {
        public void run() {
            logger.info("Shutting down - closing application");
            // Shut down everything (e.g. threads) that you need to.

            // then shut down log4j
            if( LogManager.getContext() instanceof LoggerContext ) {
                logger.debug("Shutting down log4j2");
                Configurator.shutdown((LoggerContext)LogManager.getContext());
            } else
                logger.warn("Unable to shutdown log4j2");

            // logger not usable anymore
            System.out.println("Done.");
        }
    });
}

当你的应用程序结束时(例如,调用System.exit(0)),关闭挂钩将被调用。

你还需要在Log4J2配置文件中添加以下内容:

XML版本:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration shutdownHook="disable">
...
</Configuration>

JSON版本:

{"configuration":
    {
        "shutdownHook":"disable",
        ....
    }
}

这将告诉Log4j2,你自己处理Logger的关闭。(由于在正常应用程序中没有web.xml文件,你必须以其他方式检索配置文件)。

4
如果您在停止Web应用程序时收到该错误,这意味着您的钩子没有在正确的时间注册。根据定义,它应该在之前就已经注册,以便可以在关闭期间实际调用它。

我需要为log4j2 web模块手动创建关闭挂钩吗?您能否稍微解释一下,因为我对log4j还很陌生。 - Grant
你需要添加以下代码: Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { //进行清理操作 } }); - Pouriya Zarbafian
但我认为当log4j web模块在类路径中时,关闭挂钩将自动禁用。 - Grant
Log4j 2所解释的那样,Log4j 2 Web JAR _正确初始化和去初始化Log4j配置_。 - Pouriya Zarbafian

3
异常是由于在Apache Issues中报告的ID - LOG4J2-658下存在一个bug所导致的。截至我上次检查,它仍然未解决。
与此同时,为解决这个问题,在你的配置文件中添加 shutdownHook =“disable” 作为额外参数。这将禁用关闭挂钩。
下面显示完整的格式: <Configuration status="WARN" monitorInterval="30" shutdownHook="disable"> ... </ Configuration> 有很多说明关于如何使用关闭挂钩及其好处。
但简要版本是:关闭挂钩是一种特殊构造,允许开发人员插入一段代码以在JVM关闭时执行。在VM关闭时需要进行特殊清除操作的情况下,这非常有用。
因此,Log4J2尝试使用关闭挂钩正确地关闭日志记录服务,但由于该bug而引发异常。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接