【问题标题】:Java -How to get logger to work in shutdown hook?Java - 如何让记录器在关闭挂钩中工作?
【发布时间】:2012-11-29 07:26:03
【问题描述】:

我有一个专门的记录器类,它使用java.util.logging.Logger 类。我希望能够在另一个类的关闭挂钩中使用这个记录器。但是,它似乎没有在关机时登录。根据我的阅读,可能已经为记录器本身激活了一个关闭挂钩,这导致了问题。

我怎样才能让它工作?理想情况下,我希望在日志文件中看到我实际上在进程终止时执行了关闭挂钩。

【问题讨论】:

  • 我不确定这是否属实,但您无法控制关闭挂钩发生的顺序。
  • 是的,他们运行concurrently

标签: java logging shutdown-hook


【解决方案1】:

再次查看源代码,解决方案似乎是定义一个系统属性 java.util.logging.manager,它是 LogManager 的子类,它覆盖了 reset(); 方法,因此 Loggers 在关闭时继续工作。

import java.util.logging.LogManager;
import java.util.logging.Logger;

public class Main {
    static {
        // must be called before any Logger method is used.
        System.setProperty("java.util.logging.manager", MyLogManager.class.getName());
    }

    public static class MyLogManager extends LogManager {
        static MyLogManager instance;
        public MyLogManager() { instance = this; }
        @Override public void reset() { /* don't reset yet. */ }
        private void reset0() { super.reset(); }
        public static void resetFinally() { instance.reset0(); }
    }

    public static void main(String... args) {
        Logger logger1 = Logger.getLogger("Main1");
        logger1.info("Before shutdown");
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Logger logger2 = Logger.getLogger("Main2");
                    logger2.info("Shutting down 2");

                } finally {
                    MyLogManager.resetFinally();
                }
            }
        }));
    }
}

打印

Dec 11, 2012 5:56:55 PM Main main
INFO: Before shutdown
Dec 11, 2012 5:56:55 PM Main$1 run
INFO: Shutting down 2

从 LogManager 的这段代码中,您可以看到有一个关闭挂钩,它可以拆除处理程序并关闭它们。记录器只有在之前没有使用过的情况下才能在关机状态下工作,因此这段代码不会运行。

// This private class is used as a shutdown hook.
// It does a "reset" to close all open handlers.
private class Cleaner extends Thread {

    private Cleaner() {
        /* Set context class loader to null in order to avoid
         * keeping a strong reference to an application classloader.
         */
        this.setContextClassLoader(null);
    }

    public void run() {
        // This is to ensure the LogManager.<clinit> is completed
        // before synchronized block. Otherwise deadlocks are possible.
        LogManager mgr = manager;

        // If the global handlers haven't been initialized yet, we
        // don't want to initialize them just so we can close them!
        synchronized (LogManager.this) {
            // Note that death is imminent.
            deathImminent = true;
            initializedGlobalHandlers = true;
        }

        // Do a reset to close all active handlers.
        reset();
    }
}


/**
 * Protected constructor.  This is protected so that container applications
 * (such as J2EE containers) can subclass the object.  It is non-public as
 * it is intended that there only be one LogManager object, whose value is
 * retrieved by calling Logmanager.getLogManager.
 */
protected LogManager() {
    // Add a shutdown hook to close the global handlers.
    try {
        Runtime.getRuntime().addShutdownHook(new Cleaner());
    } catch (IllegalStateException e) {
        // If the VM is already shutting down,
        // We do not need to register shutdownHook.
    }
}

来自我自己的测试

Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            Logger logger2 = Logger.getLogger("Main2");
            logger2.info("Shutting down 2");
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }
}));

打印

Dec 11, 2012 5:40:15 PM Main$1 run
INFO: Shutting down 2

但如果你添加

Logger logger1 = Logger.getLogger("Main1");

在这个街区之外你什么也得不到。

【讨论】:

  • 即使使用 `Logger.getLogger(name) 我仍然看不到任何打印到我的日志。但是,设置 System 属性并复制 MyLogManager 代码,我能够让它工作。漂亮,谢谢!
  • 我使用了 MyLogManager 方法,但结果是所有日志突然重复,每个日志行都写了两次。我跟踪它并验证 MyLogManager 只实例化了一次。一旦我注释掉设置,系统属性就全部恢复正常,即每个日志行只被写入一次,没有登录shutdownHook。
【解决方案2】:

关闭钩子有什么好处吗?

最好在 main 的最后一行添加 Log 输出: finally 块将在所有情况下执行, 除非 VM 崩溃但随后关闭挂钩将 也不会被执行。

static void main(String[] argv) {

  try {
    startApp();
  } finally {
    LOGGER.info("shudown");
  }
}

【讨论】:

  • 我的流程是一整天都在运行的流程——坐着等待某事发生。不是一劳永逸的事情,我可以把我需要的东西放在代码的末尾。我确实相信关闭挂钩是正确的解决方案
  • 看来你不知道你的代码结束在哪里?你应该知道,并找出答案。这与一击无关!我们的软件运行了数周而没有关闭,但我们知道终点在哪里。使用spring或其他框架可能有点困难。但是您应该知道清理,因为通常需要有序关闭! LOGGING 需要关闭处理程序的原因是它是一个库并且无法知道“代码结束”
  • 进程不会自行退出,所以同样没有逻辑结束。
  • 在更复杂的程序中,可能会使用关闭挂钩来调用一些可能对记录此清理结果感兴趣的清理代码。
  • Finally 块不会被执行,以防应用程序逻辑调用内部的某些东西,例如 System.exit(1)。如我所见,@Marianna 的问题只能使用外部监控/日志工具可靠地解决...
猜你喜欢
  • 1970-01-01
  • 2014-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-19
相关资源
最近更新 更多