Spring 的 @PreDestroy 导致随机记录而不记录

Wat*_*ter 5 java logging spring

我正在使用 Spring,并且在终止时我让 @PreDestroy 清理 bean。我不明白为什么随机记录有时会成功,而在其他情况下会失败。

// Using Log4j2
Logger log = LogManager.getLogger(MyClass.class);

@PreDestroy
public void close() {
    log.warn("Test");
}
Run Code Online (Sandbox Code Playgroud)

有时我什么也得不到(没有记录“测试”),有时我会得到:

[13:48:44] INFO  MyClass: Test
Run Code Online (Sandbox Code Playgroud)

如果我包含System.out.println("Is this run?");在 close() 方法中,它将始终打印。

我实际上不确定发生了什么。我不知道是不是因为 JVM 正在关闭并且记录器被杀死......但我认为这会引发某种异常?

请注意,日志记录同时记录到文件 + 标准输出中,我不知道这是否会影响任何事情。日志记录适用于其他无数的数千行代码,但不是这个。

注意:如果它最终成为这个特定的库,我愿意切换日志库。

编辑: MyClass 将是 spring.xml 文档中的一个 bean。

Dev*_*ler 6

我认为这归结为Runtime.addShutdownHook:

当虚拟机开始其关闭序列时,它将以某种未指定的顺序启动所有已注册的关闭挂钩,并让它们同时运行。

所以,只要 LogManager 和 Spring IOC 容器都被 JVM 关闭钩子关闭,就没有办法确保消息会被记录下来。如果先关闭 LogManager,消息就会丢失。如果首先关闭 IOC 容器,则会记录该消息。

如果您在 JEE 容器中运行,则可能无法更改此设置。

但是,如果您在独立环境中运行,则可以将 a 添加shutdownHook="disable"到 Log4j 2<configuration>标记。这可以防止 Log4j 2 注册它自己的关闭钩子。然后,不是调用ctx.registerShutdownHook()(推荐的关闭 IOC 的方法),而是注册自己的关闭钩子。就像是:

class MyShutdownHook extends Thread {
    private AbstractApplicationContext ctx;
    public MyShutdownHook(AbstractApplicationContext ctx) {
        this.ctx = ctx;
    }
    public void run() {
        ctx.close();
        Set<LoggerContext> contexts = new HashSet<>();
        for (Logger logger : LoggerContext.getLoggers()) {
            contexts.add(logger.getContext());
        }
        for (LoggerContext ctx : contexts) {
            Configurator.shutdown(LogManager.getContext());
        }
    }
}

AbstractApplicationContext ctx = /* create context */
Runtime.getRunTime().addShutdownHook(new MyShutdownHook(ctx);
Run Code Online (Sandbox Code Playgroud)

更新:更正了关闭 Log4j2 的过程。
警告:我远离我常用的构建机器,所以我没有编译它,但我相信它正在击中正确的 API。