在伐木活动使用readConfiguration方法(Use of readConfiguratio

2019-09-02 06:24发布

为了在小的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

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");
    }
}

如果我们执行它而不围绕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]

但是,如果我们在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]

读取readConfiguration()方法的API它应该重新初始化日志属性并重新读取从先前指定的文件中的记录的配置。 如果是这样,为什么severe3只显示一次(我希望显示,因为这两个记录器的两倍存在于程序和转发行为),并severe4丢失(我期望能够显示一次)? 可能有人帮我这个已了解?

Answer 1:

我曾经遇到过很多失误较多与readConfiguration方法。 它不是你所期望它做的。 我创建了一个小单元测试来说明这一点:

package com.demo;

import java.io.IOException;
import java.io.InputStream;
import java.util.logging.LogManager;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author dhubau
 */
public class LogTest {

    private static final Logger logger = LoggerFactory.getLogger(LogTest.class.getCanonicalName());

    @Test
    public void testLogs() throws IOException {
        logger.trace("BEFORE");
        logger.debug("BEFORE");
        logger.info("BEFORE");
        logger.warn("BEFORE");
        logger.error("BEFORE");

        InputStream is = LogTest.class.getResourceAsStream("/logging.properties");

        LogManager.getLogManager().readConfiguration(is);

        logger.trace("AFTER");
        logger.debug("AFTER");
        logger.info("AFTER");
        logger.warn("AFTER");
        logger.error("AFTER");
    }
}

当启动单元测试中,默认logging.properties读取(默认一切信息记录)。 我登录各个层面一次,然后在自己的logging.properties文件阅读:

# Specify the handlers to create in the root logger
handlers = java.util.logging.ConsoleHandler

# Set the default logging level for the root logger
.level = INFO

# Do not use the root handlers
com.demo.useParentHandlers = false

# DEMO log handlers
com.demo.handlers = java.util.logging.ConsoleHandler

# DEMO log level
com.demo.level = ALL

# Set the default logging level for new ConsoleHandler instances
java.util.logging.ConsoleHandler.level = ALL

这之后,我得到以下的输出:

May 24, 2013 11:27:29 AM com.demo.LogTest testLogs
INFO: BEFORE
May 24, 2013 11:27:29 AM com.demo.LogTest testLogs
WARNING: BEFORE
May 24, 2013 11:27:29 AM com.demo.LogTest testLogs
SEVERE: BEFORE
May 24, 2013 11:27:29 AM com.demo.LogTest testLogs
INFO: AFTER
May 24, 2013 11:27:29 AM com.demo.LogTest testLogs
WARNING: AFTER
May 24, 2013 11:27:29 AM com.demo.LogTest testLogs
SEVERE: AFTER

所以你看,com.demo包未记录TRACE或DEBUG级别。 当我通过下列参数的单元测试:

java.util.logging.config.file = C:\ TEMP \ logging.properties

它给了我下面的输出:

May 24, 2013 11:27:29 AM com.demo.LogTest testLogs
FINEST: BEFORE
May 24, 2013 11:27:29 AM com.demo.LogTest testLogs
FINE: BEFORE
May 24, 2013 11:27:29 AM com.demo.LogTest testLogs
INFO: BEFORE
May 24, 2013 11:27:29 AM com.demo.LogTest testLogs
WARNING: BEFORE
May 24, 2013 11:27:29 AM com.demo.LogTest testLogs
SEVERE: BEFORE
May 24, 2013 11:27:29 AM com.demo.LogTest testLogs
INFO: AFTER
May 24, 2013 11:27:29 AM com.demo.LogTest testLogs
WARNING: AFTER
May 24, 2013 11:27:29 AM com.demo.LogTest testLogs
SEVERE: AFTER


Answer 2:

据我所看到的, readConfiguration方法只有一个缺点,我发现通过在JDK代码调试,因为我也错过了日志消息。 他们不加载每个记录器处理程序。 如果你不使用每个记录器处理程序,该readConfiguration方法应该只是罚款。 该readConfiguration第一重置所有记录器,删除除其他事物的处理程序,然后忘记检查每个记录器处理程序。 因此,你想念你的日志信息。 你本来有三个处理,根处理,对你的包处理程序,并在你的类的处理程序。 然后你关掉useParentHandler为你的类和一个名为readConfiguration 。 现在 - 因为useParentHandler为未复位,可能是它应该 - 与您的每记录器处理程序设置不下去了,severe3只能通过根处理记录一次,并在所有的useParentHandler为是假的没有登录severe4,因此没有退回到根处理完成。

在“更多的错误”由Dieter描述的BTW。 一模一样的问题。

您也可以轻松地工作周围的错误,如果你喜欢使用一个日志配置文件。 只是遍历你叫后已经存在的记录器readConfiguration和呼叫LogManager.loadLoggerHandlers为他们每个人。 在Groovy中,这将是

def logManager = LogManager.logManager
logManager.loggerNames.each {
    logManager.loadLoggerHandlers logManager.getLogger(it), it, "${it}.handlers"
}

我测试了这一点,它的工作原理。 对于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");
}


Answer 3:

在JDK 9, JDK-8033661:readConfiguration不干净重新初始化日志系统作为解决已标记。 该LogManager.readConfiguration LogManager中的初始化期间已respecified只安装记录仪。

要更新初始化之后记录器LogManager.updateConfiguration(java.util.function.Function)假设方法中使用。 一个例子再映射函数可以在找到HandlersOnComplexResetUpdate.java试验:

static void updateConfigurationWith(Properties propertyFile, boolean append) {
    try {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        propertyFile.store(bytes, propertyFile.getProperty("test.name"));
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray());
        Function<String, BiFunction<String,String,String>> remapper =
            append ? (x) -> ((o, n) -> n == null ? o : n)
                   : (x) -> ((o, n) -> n);
        LogManager.getLogManager().updateConfiguration(bais, remapper);
     } catch (IOException ex) {
         throw new RuntimeException(ex);
     }
}


文章来源: Use of readConfiguration method in logging activities