Warum werden die Level.FINE-Protokollierungsnachrichten nicht angezeigt?

110

Die JavaDocs fürjava.util.logging.Level state:


Die Ebenen in absteigender Reihenfolge sind:

  • SEVERE (Höchster Wert)
  • WARNING
  • INFO
  • CONFIG
  • FINE
  • FINER
  • FINEST (niedrigster Wert)

Quelle

import java.util.logging.*;

class LoggingLevelsBlunder {

    public static void main(String[] args) {
        Logger logger = Logger.getAnonymousLogger();
        logger.setLevel(Level.FINER);
        System.out.println("Logging level is: " + logger.getLevel());
        for (int ii=0; ii<3; ii++) {
            logger.log(Level.FINE, ii + " " + (ii*ii));
            logger.log(Level.INFO, ii + " " + (ii*ii));
        }
    }
}

Ausgabe

Logging level is: FINER
Jun 11, 2011 9:39:23 PM LoggingLevelsBlunder main
INFO: 0 0
Jun 11, 2011 9:39:24 PM LoggingLevelsBlunder main
INFO: 1 1
Jun 11, 2011 9:39:24 PM LoggingLevelsBlunder main
INFO: 2 4
Press any key to continue . . .

Problemstellung

In meinem Beispiel ist das Levelauf gesetzt FINER, sodass ich erwartet hatte, dass für jede Schleife 2 Nachrichten angezeigt werden. Stattdessen sehe ich für jede Schleife eine einzelne Nachricht (die Level.FINENachrichten fehlen).

Frage

Was muss geändert werden, um die FINE( FINERoder FINEST) Ausgabe zu sehen?

Update (Lösung)

Dank der Antwort von Vineet Reynolds funktioniert diese Version gemäß meinen Erwartungen. Es werden 3 x INFONachrichten und 3 x FINENachrichten angezeigt .

import java.util.logging.*;

class LoggingLevelsBlunder {

    public static void main(String[] args) {
        Logger logger = Logger.getAnonymousLogger();
        // LOG this level to the log
        logger.setLevel(Level.FINER);

        ConsoleHandler handler = new ConsoleHandler();
        // PUBLISH this level
        handler.setLevel(Level.FINER);
        logger.addHandler(handler);

        System.out.println("Logging level is: " + logger.getLevel());
        for (int ii=0; ii<3; ii++) {
            logger.log(Level.FINE, ii + " " + (ii*ii));
            logger.log(Level.INFO, ii + " " + (ii*ii));
        }
    }
}
Andrew Thompson
quelle
10
Mir scheint, Sie werden die Nachrichten für INFO und höher zweimal auf der Konsole drucken lassen: zuerst vom anonymen Logger, dann vom übergeordneten Logger, dessen globaler Logger standardmäßig auch einen ConsoleHandler auf INFO gesetzt hat. Um den globalen Logger zu deaktivieren, müssen Sie folgende Codezeile hinzufügen: logger.setUseParentHandlers (false);
Minuten
Ich möchte nur den Kommentar von min zu den Doppel bestätigen. Sie erhalten zwei Ausgaben, sofern Sie nicht .setUseParentHandlers (false) verwenden.
xpagesbeast

Antworten:

124

Protokollierer protokollieren nur die Nachricht, dh sie erstellen die Protokolldatensätze (oder Protokollierungsanforderungen). Sie veröffentlichen die Nachrichten nicht an den Zielen, um die sich die Handler kümmern. Wenn Sie die Ebene eines Loggers festlegen, wird dieser nur erstellt Log - Aufzeichnungen , dass die Pegelanpassung oder höher.

Möglicherweise verwenden Sie eine ConsoleHandler(ich konnte nicht ableiten, wo Ihre Ausgabe System.err oder eine Datei ist, aber ich würde annehmen, dass es die erstere ist), die standardmäßig Protokolldatensätze der Ebene veröffentlicht Level.INFO. Sie müssen diesen Handler konfigurieren, um Protokolldatensätze der Ebene zu veröffentlichenLevel.FINER und höher für das gewünschte Ergebnis zu veröffentlichen.

Ich würde empfehlen, das Handbuch zur Java-Protokollierungsübersicht zu lesen , um das zugrunde liegende Design zu verstehen. Das Handbuch behandelt den Unterschied zwischen dem Konzept eines Loggers und eines Handlers.

Bearbeiten der Handler-Ebene

1. Verwenden der Konfigurationsdatei

Die Eigenschaftendatei java.util.logging (standardmäßig ist dies die logging.propertiesDatei in JRE_HOME/lib) kann geändert werden, um die Standardstufe des ConsoleHandler zu ändern:

java.util.logging.ConsoleHandler.level = FINER

2. Erstellen von Handlern zur Laufzeit

Dies wird nicht empfohlen, da dies dazu führen würde, dass die globale Konfiguration überschrieben wird. Die Verwendung dieser Option in Ihrer gesamten Codebasis führt zu einer möglicherweise nicht verwaltbaren Logger-Konfiguration.

Handler consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(Level.FINER);
Logger.getAnonymousLogger().addHandler(consoleHandler);
Vineet Reynolds
quelle
2
Vielen Dank. Das hat funktioniert. Mir ist jetzt klar, warum ich anfangs so verwirrt war. Ich hatte zuvor mit Protokollierungsstufen gearbeitet, aber meine Implementierung zu diesem Zeitpunkt hat einfach jede protokollierte Nachricht in eine Liste verschoben, die ohne Rücksicht auf die angezeigt wurde Handler.
Andrew Thompson
3
Bitte. Und ja, das Design kommt zu Ihnen, wenn man Logger geschrieben hat, die einfach Strings in eine Datei, eine Konsole usw. geschrieben haben
Vineet Reynolds
7
Und das Ändern der logging.properties in der globalen JRE-Bibliothek ist verwaltbar?
James Anderson
2
Bitte beachten Sie, dass die Ebene des Loggers selbst weniger restriktiv sein muss als die Ebene des Handlers. Vor dem Protokollieren überprüft Java die maximale Ebene zwischen Protokollierer und Handler. Wenn Sie also nur handler.setLevel (Level.FINER) festlegen; Sie sehen nichts, da die Standard-Logger-Ebene Level.INFO ist. Sie müssen FINER, FINEST oder ALL einstellen. Sagen Sie logger.setLevel (Level.ALL);
Jeff_Alieffson
Ich denke, dies wird das Problem als Einzeiler lösen, selbst wenn Sie anonyme und benannte Logger in einer Standardumgebung verwenden:Logger.getGlobal().getParent().getHandlers()[0].setLevel(Level.FINER);
Mark Jeronimus
27

Das Warum

java.util.logging verfügt standardmäßig über einen Root-Logger Level.INFOund einen ConsoleHandler, der ebenfalls standardmäßig verwendet wird Level.INFO. FINEist niedriger als INFO, daher werden feine Nachrichten standardmäßig nicht angezeigt.


Lösung 1

Erstellen Sie einen Logger für Ihre gesamte Anwendung, z. B. Logger.getGlobal()anhand Ihres Paketnamens oder Ihrer Verwendung , und hängen Sie Ihren eigenen ConsoleLogger daran an. Bitten Sie dann entweder den Root-Logger, den Computer herunterzufahren (um die doppelte Ausgabe von Nachrichten höherer Ebene zu vermeiden), oder bitten Sie Ihren Logger, die Protokolle nicht an den Root weiterzuleiten .

public static final Logger applog = Logger.getGlobal();
...

// Create and set handler
Handler systemOut = new ConsoleHandler();
systemOut.setLevel( Level.ALL );
applog.addHandler( systemOut );
applog.setLevel( Level.ALL );

// Prevent logs from processed by default Console handler.
applog.setUseParentHandlers( false ); // Solution 1
Logger.getLogger("").setLevel( Level.OFF ); // Solution 2

Lösung 2

Alternativ können Sie die Leiste des Root-Loggers senken.

Sie können sie per Code einstellen:

Logger rootLog = Logger.getLogger("");
rootLog.setLevel( Level.FINE );
rootLog.getHandlers()[0].setLevel( Level.FINE ); // Default console handler

Oder mit der Protokollierungskonfigurationsdatei, wenn Sie diese verwenden :

.level = FINE
java.util.logging.ConsoleHandler.level = FINE

Wenn Sie die globale Ebene senken, werden möglicherweise Nachrichten aus Kernbibliotheken angezeigt, z. B. von einigen Swing- oder JavaFX-Komponenten. In diesem Fall können Sie im Root-Logger einen Filter festlegen , um Nachrichten herauszufiltern, die nicht aus Ihrem Programm stammen.

Schafig
quelle
applog.setUseParentHandlers (false) Dieser Code hilft mir, vielen Dank.
Aolphn
4

Warum funktioniert meine Java-Protokollierung nicht?

Bietet eine JAR-Datei, mit der Sie herausfinden können, warum Ihre Anmeldung nicht wie erwartet funktioniert. Sie erhalten einen vollständigen Überblick darüber, welche Logger und Handler installiert wurden und welche Ebenen festgelegt wurden und auf welcher Ebene in der Protokollierungshierarchie.

Matthew
quelle
Dies ist ein Kommentar, keine Antwort.
Hfontanez
4

WARUM

Wie von @Sheepy erwähnt, funktioniert es nicht, java.util.logging.Loggerweil standardmäßig ein Root-Logger verwendet Level.INFOwird und der ConsoleHandleran diesen Root-Logger angehängte standardmäßig ebenfalls Level.INFO. Deshalb, um die zu sehen FINE(, FINERoder FINEST) ausgegeben, müssen Sie den Standardwert der Root - Logger setzen und seine ConsoleHandlerauf Level.FINEwie folgt:

Logger.getLogger("").setLevel(Level.FINE);
Logger.getLogger("").getHandlers()[0].setLevel(Level.FINE);


Das Problem Ihres Updates (Lösung)

Wie von @mins erwähnt, werden die Nachrichten für INFOund über zweimal auf der Konsole gedruckt : zuerst vom anonymen Logger, dann vom übergeordneten Logger, dem Root-Logger ConsoleHandler, auf INFOden standardmäßig ebenfalls festgelegt ist. Um den Root-Logger zu deaktivieren, müssen Sie folgende Codezeile hinzufügen:logger.setUseParentHandlers(false);

Es gibt andere Möglichkeiten, um zu verhindern, dass Protokolle vom Standard-Konsolenhandler des von @Sheepy erwähnten Root-Loggers verarbeitet werden, z.

Logger.getLogger("").getHandlers()[0].setLevel( Level.OFF );

Funktioniert Logger.getLogger("").setLevel( Level.OFF );jedoch nicht, da nur die direkt an den Root-Logger übergebene Nachricht blockiert wird und nicht die Nachricht von einem untergeordneten Logger stammt. Um zu veranschaulichen, wie das Logger Hierarchyfunktioniert, zeichne ich das folgende Diagramm:

Geben Sie hier die Bildbeschreibung ein

public void setLevel(Level newLevel)Legen Sie die Protokollebene fest und geben Sie an, welche Nachrichtenebenen von diesem Protokollierer protokolliert werden. Nachrichtenpegel, die unter diesem Wert liegen, werden verworfen. Mit dem Pegelwert Level.OFF können Sie die Protokollierung deaktivieren. Wenn die neue Ebene null ist, bedeutet dies, dass dieser Knoten seine Ebene von seinem nächsten Vorfahren mit einem bestimmten Wert (nicht null) erben sollte.

Joe Yichong
quelle
0

Ich habe mein eigentliches Problem gefunden und es wurde in keiner Antwort erwähnt: Einige meiner Komponententests führten dazu, dass der Protokollierungsinitialisierungscode innerhalb derselben Testsuite mehrmals ausgeführt wurde, was die Protokollierung bei den späteren Tests durcheinander brachte.

Alex R.
quelle
0

Versucht andere Varianten, kann dies richtig sein

    Logger logger = Logger.getLogger(MyClass.class.getName());        
    Level level = Level.ALL;
    for(Handler h : java.util.logging.Logger.getLogger("").getHandlers())    
        h.setLevel(level);
    logger.setLevel(level);
// this must be shown
    logger.fine("fine");
    logger.info("info");
zhen_khokh
quelle
0

Diese Lösung erscheint mir in Bezug auf Wartbarkeit und Design für Änderungen besser:

  1. Erstellen Sie die Protokollierungseigenschaftendatei, in die sie im Ressourcenprojektordner eingebettet ist, um sie in die JAR-Datei aufzunehmen:

    # Logging
    handlers = java.util.logging.ConsoleHandler
    .level = ALL
    
    # Console Logging
    java.util.logging.ConsoleHandler.level = ALL
  2. Laden Sie die Eigenschaftendatei aus dem Code:

    public static java.net.URL retrieveURLOfJarResource(String resourceName) {
       return Thread.currentThread().getContextClassLoader().getResource(resourceName);
    }
    
    public synchronized void initializeLogger() {
       try (InputStream is = retrieveURLOfJarResource("logging.properties").openStream()) {
          LogManager.getLogManager().readConfiguration(is);
       } catch (IOException e) {
          // ...
       }
    }
user2627331
quelle