FRA*_*JOL 2 java java.util.logging
为了在小型 Java 桌面应用程序中使用日志记录,我试图深入了解某些方法的操作。我使用一个非常愚蠢的小型 Java 程序来测试它们。
特别是,在测试 LogManager.readConfiguration() 方法的行为时,我发现了一些奇怪的东西。在所有测试中,LogManager 从位于 JRE 目录中 lib/logging.properties 的属性文件中读取其配置。这时候这个文件的内容如下:
handlers=java.util.logging.ConsoleHandler
myapp2.handlers=java.util.logging.ConsoleHandler
myapp2.MyApp2.handlers=java.util.logging.ConsoleHandler
.level=OFF
java.util.logging.ConsoleHandler.level=ALL
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n
myapp2.level=WARNING
myapp2.MyApp2.level=INFO
Run Code Online (Sandbox Code Playgroud)
java程序的代码是:
package myapp2;
import java.io.IOException;
import java.util.logging.LogManager;
import java.util.logging.Logger;
public class MyApp2 {
private static final Logger LOGGER = Logger.getLogger(MyApp2.class.getPackage().getName());
private static final Logger LOGGER1 = Logger.getLogger(MyApp2.class.getName());
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
LOGGER.severe("severe at MyApp2");
LOGGER.warning("warning at MyApp2");
LOGGER.info("info at MyApp2");
LOGGER1.severe("severe1 at MyApp2");
LOGGER1.warning("warning1 at MyApp2");
LOGGER1.info("info1 at MyApp2");
LOGGER1.setLevel(null);
LOGGER1.setUseParentHandlers(false);
LOGGER1.severe("severe2 at MyApp2");
LOGGER1.warning("warning2 at MyApp2");
LOGGER1.info("info2 at MyApp2");
try {
LogManager.getLogManager().readConfiguration();
} catch (IOException ex) {
System.out.println("I/O Exception found");
} catch (SecurityException ex) {
System.out.println("Error SecurityException found");
}
LOGGER.severe("severe3 at MyApp2");
LOGGER1.severe("severe4 at MyApp2");
}
}
Run Code Online (Sandbox Code Playgroud)
如果我们在没有围绕 readConfiguration() 的 try-catch 的情况下执行它,则按预期工作,输出如下:
SEVERE: severe at MyApp2 [dc. maig 08 14:45:38 CEST 2013]
SEVERE: severe at MyApp2 [dc. maig 08 14:45:38 CEST 2013]
WARNING: warning at MyApp2 [dc. maig 08 14:45:38 CEST 2013]
WARNING: warning at MyApp2 [dc. maig 08 14:45:38 CEST 2013]
SEVERE: severe1 at MyApp2 [dc. maig 08 14:45:38 CEST 2013]
SEVERE: severe1 at MyApp2 [dc. maig 08 14:45:38 CEST 2013]
SEVERE: severe1 at MyApp2 [dc. maig 08 14:45:38 CEST 2013]
WARNING: warning1 at MyApp2 [dc. maig 08 14:45:38 CEST 2013]
WARNING: warning1 at MyApp2 [dc. maig 08 14:45:38 CEST 2013]
WARNING: warning1 at MyApp2 [dc. maig 08 14:45:38 CEST 2013]
INFO: info1 at MyApp2 [dc. maig 08 14:45:38 CEST 2013]
INFO: info1 at MyApp2 [dc. maig 08 14:45:38 CEST 2013]
INFO: info1 at MyApp2 [dc. maig 08 14:45:38 CEST 2013]
SEVERE: severe2 at MyApp2 [dc. maig 08 14:45:38 CEST 2013]
WARNING: warning2 at MyApp2 [dc. maig 08 14:45:38 CEST 2013]
SEVERE: severe3 at MyApp2 [dc. maig 08 14:45:38 CEST 2013]
SEVERE: severe3 at MyApp2 [dc. maig 08 14:45:38 CEST 2013]
SEVERE: severe4 at MyApp2 [dc. maig 08 14:45:38 CEST 2013]
Run Code Online (Sandbox Code Playgroud)
但是,如果我们使用 try-catch 执行,输出是:
SEVERE: severe at MyApp2 [dc. maig 08 14:46:51 CEST 2013]
SEVERE: severe at MyApp2 [dc. maig 08 14:46:51 CEST 2013]
WARNING: warning at MyApp2 [dc. maig 08 14:46:51 CEST 2013]
WARNING: warning at MyApp2 [dc. maig 08 14:46:51 CEST 2013]
SEVERE: severe1 at MyApp2 [dc. maig 08 14:46:51 CEST 2013]
SEVERE: severe1 at MyApp2 [dc. maig 08 14:46:51 CEST 2013]
SEVERE: severe1 at MyApp2 [dc. maig 08 14:46:51 CEST 2013]
WARNING: warning1 at MyApp2 [dc. maig 08 14:46:51 CEST 2013]
WARNING: warning1 at MyApp2 [dc. maig 08 14:46:51 CEST 2013]
WARNING: warning1 at MyApp2 [dc. maig 08 14:46:51 CEST 2013]
INFO: info1 at MyApp2 [dc. maig 08 14:46:51 CEST 2013]
INFO: info1 at MyApp2 [dc. maig 08 14:46:51 CEST 2013]
INFO: info1 at MyApp2 [dc. maig 08 14:46:51 CEST 2013]
SEVERE: severe2 at MyApp2 [dc. maig 08 14:46:51 CEST 2013]
WARNING: warning2 at MyApp2 [dc. maig 08 14:46:51 CEST 2013]
SEVERE: severe3 at MyApp2 [dc. maig 08 14:46:51 CEST 2013]
Run Code Online (Sandbox Code Playgroud)
读取 readConfiguration() 方法的 API 应该重新初始化日志属性并从先前命名的文件中重新读取日志配置。如果是这样,为什么severe3 只显示一次(我希望显示两次,因为程序中存在两个LOGGER 和转发行为)而severe4 丢失(我希望显示一次)?。任何人都可以帮助我理解这一点吗?
据我所知,这些readConfiguration方法只有一个缺陷,这是我通过 JDK 代码调试发现的,因为我也错过了日志消息。它们不加载每个记录器的处理程序。如果您不使用 per-logger 处理程序,该readConfiguration方法应该可以正常工作。第readConfiguration一个重置所有记录器,删除处理程序等,然后忘记检查每个记录器的处理程序。因此,您会错过日志消息。您最初有三个处理程序,根处理程序、包上的处理程序和类上的处理程序。然后你在你的班级上关闭了 useParentHandlers 并调用了readConfiguration. 现在 - 因为 useParentHandlers 没有被重置,它可能应该 - 并且你的每个记录器处理程序不再设置,severe3 只通过根处理程序记录一次,而严重 4 根本没有记录,因为 useParentHandlers 是假的,所以没有回退到根处理程序完成。
顺便说一句,Dieter 描述的“更多错误”。完全一样的问题。
如果您更喜欢使用日志配置文件,您也可以轻松解决该错误。在您调用readConfiguration并为每个记录器调用 LogManager.loadLoggerHandlers之后,只需遍历已经存在的记录器。在 Groovy 中,这将是
def logManager = LogManager.logManager
logManager.loggerNames.each {
logManager.loadLoggerHandlers logManager.getLogger(it), it, "${it}.handlers"
}
Run Code Online (Sandbox Code Playgroud)
我测试了这个并且它有效。对于 Java,您必须使用反射,因为它是一种私有方法。应该是这样的
LogManager logManager = LogManager.getLogManager();
Method loadLoggerHandlers = LogManager.class.getDeclaredMethod("loadLoggerHandlers", Logger.class, String.class, String.class);
loadLoggerHandlers.setAccessible(true);
for (String loggerName : logManager.getLoggerNames()) {
loadLoggerHandlers.invoke(logManager, logManager.getLogger(loggerName), loggerName, loggerName + ".handlers");
}
Run Code Online (Sandbox Code Playgroud)