3

小さな 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 が 1 回しか表示されず (プログラムに 2 つの LOGGER が存在し、転送動作が行われるため、2 回表示されると予想される)、severe4 が表示されない (1 回表示されると予想される) のはなぜですか? 誰かがこれを理解するのを手伝ってくれますか?.

4

3 に答える 3

5

私が見た限りでは、これらのreadConfigurationメソッドには 1 つの欠陥しかありませんでした。これは、ログ メッセージも見逃していたため、JDK コードを介してデバッグすることによって発見されました。ロガーごとのハンドラーをロードしません。ロガーごとのハンドラーを使用しない場合、readConfigurationメソッドは正常に機能するはずです。最初はreadConfigurationすべてのロガーをリセットし、特にハンドラーを削除してから、ロガーごとのハンドラーをチェックするのを忘れます。そのため、ログ メッセージを見逃すことになります。最初は、ルート ハンドラー、パッケージのハンドラー、クラスのハンドラーの 3 つのハンドラーがありました。次に、クラスで useParentHandlers をオフにして呼び出しましたreadConfiguration. 現在-useParentHandlersはリセットされていないため、おそらくそうすべきです-ロガーごとのハンドラーはもうセットアップされていません.severe3はルートハンドラーを介して1回だけログに記録され、useParentHandlersがfalseであるためルートへのフォールバックがないため、severe4はまったくログに記録されませんハンドラーができました。

ディーターが説明した「さらに多くの間違い」は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");
}
于 2014-01-27T00:31:57.680 に答える
2

JDK 9 では、JDK-8033661: readConfiguration がロギング システムをきれいに再初期化しないで、解決済みとしてマークされました。LogManager.readConfigurationは、LogManager の初期化中にのみロガーをセットアップするように再指定されました。

初期化後にロガーを更新するには、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);
     }
}
于 2016-03-17T18:43:39.840 に答える