Java 弹簧&x27;s@PreDestroy导致随机记录而不是记录

Java 弹簧&x27;s@PreDestroy导致随机记录而不是记录,java,spring,logging,Java,Spring,Logging,我正在使用Spring,在终止时,我会让@PreDestroy清理bean。我不明白为什么日志记录有时会随机成功,而另一些则会失败 // Using Log4j2 Logger log = LogManager.getLogger(MyClass.class); @PreDestroy public void close() { log.warn("Test"); } 有时我什么也得不到(没有记录“测试”),其他时候我会得到: [13:48:44] INFO MyClass: Te

我正在使用Spring,在终止时,我会让@PreDestroy清理bean。我不明白为什么日志记录有时会随机成功,而另一些则会失败

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

@PreDestroy
public void close() {
    log.warn("Test");
}
有时我什么也得不到(没有记录“测试”),其他时候我会得到:

[13:48:44] INFO  MyClass: Test
如果我包括
System.out.println(“这是在运行吗?”)
在close()方法中,它将始终打印

我不确定到底发生了什么。我不知道这是否是因为JVM正在关闭,而记录器被杀死。。。但我认为这会引发某种例外

注意,日志记录都记录到一个文件+stdout,我不知道这是否会影响任何事情。对于其他无数行代码,日志记录可以很好地工作,但不是这样

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


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

我没有足够的声誉来添加评论,因此我将在这里回答:

根据JavaEE API javadoc:

  • 在非侦听器类上定义的方法必须具有以下签名:void()
  • 应用预销毁的方法可以是公共的、受保护的、包私有的或私有的
  • 方法不能是静态的
  • 方法可能是最终的
  • 如果该方法引发未经检查的异常,则会忽略该异常,但EJB可以处理异常的EJB除外。

检查是否在其他地方“无声”抛出异常,这可能是一个原因。

我认为可以归结为以下几点:

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

因此,只要LogManager和Spring IOC容器都被JVM关闭挂钩关闭,就无法确保消息被记录下来。如果首先关闭LogManager,则消息将丢失。如果先关闭IOC容器,则会记录消息

如果你在一个JEE容器中运行,你可能无法改变这一点

但是,如果您在独立环境中运行,则可以向Log4j 2
标记添加
shutdownHook=“disable”
。这将防止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);
类MyShutdownHook扩展线程{
私有抽象应用上下文ctx;
公共MyShutdownHook(AbstractApplicationContext ctx){
this.ctx=ctx;
}
公开募捐{
ctx.close();
Set contexts=new HashSet();
对于(记录器:LoggerContext.getLoggers()){
add(logger.getContext());
}
for(LoggerContext ctx:context){
Configurator.shutdown(LogManager.getContext());
}
}
}
AbstractApplicationContext ctx=/*创建上下文*/
Runtime.getRunTime().addShutdownHook(新的MyShutdownHook(ctx);
更新:已更正关闭Log4j2的过程。

警告:我没有使用我常用的构建机器,所以我没有编译这个,但我相信它符合正确的API。

与@Devon_C_Miller answer完全相同,但经过更新以反映最新版本的log4j2

公共类应用程序{
公共静态void main(字符串[]args){
AnnotationConfigApplicationContext上下文=新的AnnotationConfigApplicationContext();
context.refresh();
Runtime.getRuntime().addShutdownHook(新的Log4j2AwareShutdownHook(上下文));
}
}
类Log4j2AwareShutdownHook扩展线程{
私有抽象应用上下文ctx;
log4j2awarshutdownhook(AbstractApplicationContext ctx){
this.ctx=ctx;
}
公开募捐{
ctx.close();
LogManager.shutdown();
}
}
在log4j2配置中


%xEx
%5p
yyyy MM dd HH:MM:ss.SSS
%d{${LOG\u DATEFORMAT\u PATTERN}${LOG\u LEVEL\u PATTERN}%pid----[%t]-40.40c{1.}:%m%n${sys:LOG\u EXCEPTION\u CONVERSION\u WORD}

你在使用异步日志记录吗?@KErlandsson我不知道有这样一个选项,我很确定我没有。在谷歌搜索之后,我可以告诉你,我的配置文件中没有任何行
“immediateFlush=”false"
,如果有帮助的话,更不用说任何地方的命令了。你的bean的作用域是什么?@Skizzo这是一个单音它发生在我身上,当对spring数据存储库函数调用执行一个疯狂的try catch块时,只是保存了一个对象,在另一个对象和函数调用中引发了一个未检查的异常,触发了catch,首先看,它们似乎没有关联,但不知何故,这就是为什么我建议将其作为原因。我将我的代码包装在一个try/catch for everything中,它显示没有异常被抛出,代码仍将随机打印,或无法打印。我很欣赏这个答案,但我不知道这一点。到目前为止,这看起来不错,尽管没有LogManager.shutdown()在Log4j2 api中为我提供函数。目前,由于我有一个标准输出和文件附加器,我担心我会让它们保持打开状态。有什么办法解决这个问题吗?如果有,这看起来可能是正确的答案,并且会得到赏金。@Water:我想我有正确的api,但我手头没有构建环境,所以我实际上没有comp我明白了。这对我来说很有效,很遗憾,我在尝试之前没有看到你的答案——但是,你的答案仍然包含了你的答案。感谢你的解决方案,到目前为止它看起来很棒。截至Log4j 2.6,LogManager有关闭方法。