记录在少数特定情况下丢失的消息

时间:2010-10-29 09:32:29

标签: java logging exception-handling

我正在使用java.util.logging来记录我的应用程序。

直到最近,我才使用日志工具而没有任何特定配置。一切都按预期工作,所有日志都在控制台中显示(stderr)

现在,我想自定义日志的配置。我希望日志显示在控制台上,但我希望它们也可以写在文件中。我提出了以下解决方案:

public static void main(String[] args) {
    System.setProperty("java.util.logging.config.file", "log.config");
    Logger defLogger = Logger.getLogger("fr.def"); // all loggers I use begin by "fr.def"
    defLogger.setLevel(Level.ALL);
    defLogger.addHandler(new ConsoleHandler());
    defLogger.addHandler(new FileHandler());
    // real code here ...

以下是log.config文件的内容:

java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.FileHandler.count=10
java.util.logging.FileHandler.pattern=logs/visiodef2.%g.log

此解决方案主要有效:我可以在控制台和文件中查看日志。除此之外,在某些情况下,一些日志消息只是丢失(对于控制台和文件)。日志丢失的情况示例:

  • 在JVM的关闭挂钩上
  • 在默认的未捕获异常处理程序
  • 在EDT的异常处理程序
  • 在主JFrame的windowClosing事件上(使用默认关闭操作EXIT_ON_CLOSE配置)

除了上面描述的内容之外没有其他配置。不涉及日志级别:我可以看到一些INFO日志,但有些丢失的日志是严重的。

我还尝试添加一个关闭钩子来刷新所有处理程序,但没有成功。

所以,问题是:按照我的方式配置日志记录是否安全?你能看出为什么有些日志会丢失的原因吗?

2 个答案:

答案 0 :(得分:6)

我发现了问题。这很奇怪。

实际上,我的问题与日志发生在异常处理程序或Frame事件中的事实完全无关。问题是垃圾收集器在创建后几秒钟就会销毁“fr.def”记录器!因此,FileHandler也被销毁。 GC可以这样做,因为LogManager只保留对它创建的Logger的弱引用。

Logger.getLogger的javadoc并没有说明任何内容,但前者调用的LogManager.addLogger的javadoc明确表示:

应用程序应保留自己对Logger对象的引用,以避免被垃圾回收。 LogManager可能只保留弱引用。

因此,解决方法是保留对Logger.getLogger("fr.def")返回的对象的引用。

修改

似乎选择使用弱引用来自bug report

答案 1 :(得分:2)

如果您深入研究LogManager源代码,您会看到它安装了自己的关闭钩子LogManager.Cleaner,它会关闭所有记录器处理程序。

由于所有关闭挂钩同时运行,因此挂钩与通过日志记录注册的挂钩之间存在竞争。如果日志记录首先完成,则不会输出。

没有干净的方法。如果您不想更改源代码,可以破解某种非便携式预关闭钩子:https://gist.github.com/735322

或者使用未在LogManager中注册的Logger.getAnonymousLogger(),因此在关闭钩子中不会关闭。您必须添加自己的处理程序并调用Logger#setUseParentHandlers(false)以避免重复的消息。