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

时间:2015-05-24 17:54:37

标签: 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: Test

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

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

请注意,日志记录记录到文件+标准输出,我不知道这是否会影响任何内容。对于其他无数千行代码,日志记录工作正常,但不是这样。

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

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

3 个答案:

答案 0 :(得分:4)

我认为归结为此,来自Runtime.addShutdownHook

  

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

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

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

但是,如果您在独立环境中运行,则可以向Log4j 2 shutdownHook="disable"标记添加<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);

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

答案 1 :(得分:1)

我没有足够的声誉来添加评论,所以我会在这里回答:

根据JavaEE API javadoc:

  • 在非拦截器类上定义的方法必须具有以下签名:void()
  • 应用PreDestroy的方法可以是public,protected,package private或private。
  • 方法绝不能是静态的。
  • 方法可能是最终的。
  • 如果方法抛出未经检查的异常,则会被忽略,除非EJB可以处理异常的EJB。

检查是否在其他地方抛出异常&#34;静默&#34;,这可能是一个原因。

答案 2 :(得分:0)

与@Devon_C_Miller答案完全相同,但已更新以反映log4j2的最新版本

public class MonolithApplication {
  public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.refresh();
    Runtime.getRuntime().addShutdownHook(new Log4j2AwareShutdownHook(context));
  }
}

class Log4j2AwareShutdownHook extends Thread {
  private AbstractApplicationContext ctx;
  Log4j2AwareShutdownHook(AbstractApplicationContext ctx) {
    this.ctx = ctx;
  }
  public void run() {
    ctx.close();
    LogManager.shutdown();
  }
}

以及在log4j2配置中

<?xml version="1.0" encoding="UTF-8"?>
<Configuration shutdownHook="disable">
    <Properties>
        <Property name="LOG_EXCEPTION_CONVERSION_WORD">%xEx</Property>
        <Property name="LOG_LEVEL_PATTERN">%5p</Property>
        <Property name="LOG_DATEFORMAT_PATTERN">yyyy-MM-dd HH:mm:ss.SSS</Property>
        <Property name="CONSOLE_LOG_PATTERN">%d{${LOG_DATEFORMAT_PATTERN}} ${LOG_LEVEL_PATTERN} %pid --- [%t] %-40.40c{1.} : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}</Property>
    </Properties>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="${sys:CONSOLE_LOG_PATTERN}"/>
        </Console>
    </Appenders>
    <Loggers>
        <Logger name="com.acme" level="debug"/>
        <Root level="info">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>