Perché i messaggi di registrazione Level.FINE non vengono visualizzati?


110

I JavaDoc per lojava.util.logging.Level stato:


I livelli in ordine decrescente sono:

  • SEVERE (valore più alto)
  • WARNING
  • INFO
  • CONFIG
  • FINE
  • FINER
  • FINEST (valore più basso)

fonte

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

Produzione

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 . . .

Dichiarazione problema

Il mio esempio imposta il Levela FINER, quindi mi aspettavo di vedere 2 messaggi per ogni ciclo. Invece vedo un singolo messaggio per ogni ciclo (i Level.FINEmessaggi mancano).

Domanda

Cosa deve cambiare per vedere l' output FINE(, FINERo FINEST)?

Aggiorna (soluzione)

Grazie alla risposta di Vineet Reynolds , questa versione funziona secondo le mie aspettative. Visualizza 3 INFOmessaggi x e 3 FINEmessaggi x .

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

10
Mi sembra che i messaggi verranno stampati due volte sulla console per INFO e superiori: prima dal logger anonimo, poi dal suo genitore, il logger globale che ha anche un ConsoleHandler impostato su INFO per impostazione predefinita. Per disabilitare il logger globale, è necessario aggiungere questa riga di codice: logger.setUseParentHandlers (false);
min.

Voglio solo confermare i minuti di commento sui doppi. otterrai due output a meno che non utilizzi .setUseParentHandlers (false);
xpagesbeast

Risposte:


124

I logger registrano solo il messaggio, cioè creano i record di log (o le richieste di logging). Non pubblicano i messaggi alle destinazioni, cosa che viene curata dagli Handlers. L'impostazione del livello di un logger determina solo la creazione di record di log corrispondenti a quel livello o superiore.

Potresti usare a ConsoleHandler(non sono riuscito a dedurre dove il tuo output è System.err o un file, ma presumo che sia il primo), che per impostazione predefinita pubblica i record di log del livello Level.INFO. Dovrai configurare questo gestore, per pubblicare i record di log di livello Level.FINERe superiore, per il risultato desiderato.

Consiglierei di leggere la guida Panoramica di Java Logging , al fine di comprendere il design sottostante. La guida copre la differenza tra il concetto di logger e handler.

Modifica del livello del gestore

1. Utilizzo del file di configurazione

Il file delle proprietà java.util.logging (per impostazione predefinita, questo è il logging.propertiesfile in JRE_HOME/lib) può essere modificato per cambiare il livello predefinito di ConsoleHandler:

java.util.logging.ConsoleHandler.level = FINER

2. Creazione di gestori in fase di esecuzione

Questo non è consigliato, poiché comporterebbe l'override della configurazione globale. L'utilizzo di questo in tutta la tua base di codice risulterà in una configurazione del logger forse non gestibile.

Handler consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(Level.FINER);
Logger.getAnonymousLogger().addHandler(consoleHandler);

2
Grazie. Quello ha funzionato. Mi rendo conto ora perché inizialmente ero così confuso. In precedenza avevo lavorato con i livelli di registrazione, ma la mia implementazione in quel momento semplicemente rilasciava ogni messaggio registrato in un elenco che veniva visualizzato senza riguardo al file Handler.
Andrew Thompson,

3
Prego. E sì, il progetto arriva a te, se uno ha scritto logger che hanno semplicemente scaricato le stringhe in un file, console ecc.
Vineet Reynolds,

7
E la modifica di logging.properties nella libreria JRE globale è gestibile?
James Anderson

2
Si noti che il livello del logger stesso deve essere meno restrittivo del livello del gestore. Prima della registrazione Java controlla il livello massimo tra logger e gestore. Quindi, se imposti solo handler.setLevel (Level.FINER); non vedrai nulla perché il livello di logger predefinito è Level.INFO. È necessario impostarlo su FINER, FINEST o ALL. Dire logger.setLevel (Level.ALL);
Jeff_Alieffson

Immagino che questo risolverà il problema come una riga, anche quando usi logger anonimi e con nome in un ambiente predefinito:Logger.getGlobal().getParent().getHandlers()[0].setLevel(Level.FINER);
Mark Jeronimus

27

Il perché

java.util.logging ha un logger di root il cui valore predefinito è Level.INFOe un ConsoleHandler collegato ad esso che è anch'esso predefinito Level.INFO. FINEè inferiore a INFO, quindi i messaggi fini non vengono visualizzati per impostazione predefinita.


Soluzione 1

Crea un logger per l'intera applicazione, ad esempio dal nome o dall'uso del tuo pacchetto Logger.getGlobal(), e aggancia il tuo ConsoleLogger ad esso. Quindi chiedi al logger di root di chiudersi (per evitare l'output duplicato di messaggi di livello superiore), oppure chiedi al tuo logger di non inoltrare i log a root.

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

Soluzione 2

In alternativa, puoi abbassare la barra del logger di root.

Puoi impostarli tramite codice:

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

O con il file di configurazione della registrazione, se lo stai utilizzando :

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

Abbassando il livello globale, potresti iniziare a vedere i messaggi dalle librerie principali, ad esempio da alcuni componenti Swing o JavaFX. In questo caso puoi impostare un filtro sul logger di root per filtrare i messaggi non dal tuo programma.


applog.setUseParentHandlers (false) questi codici mi aiutano, grazie mille.
aolphn

4

perché la mia registrazione java non funziona

fornisce un file jar che ti aiuterà a capire perché il tuo login non funziona come previsto. Fornisce un dump completo di quali logger e gestori sono stati installati e quali livelli sono impostati ea quale livello nella gerarchia di registrazione.


Questo è un commento, non una risposta.
hfontanez

4

PERCHÉ

Come accennato da @Sheepy, il motivo per cui non funziona è che java.util.logging.Loggerha un logger di root il cui valore predefinito è Level.INFO, e anche quello ConsoleHandlercollegato a quel logger di root è Level.INFO. Pertanto, al fine di vedere la FINE(, FINERo FINESTuscita), è necessario impostare il valore di default del logger principale e la sua ConsoleHandleral Level.FINEsegue come:

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


Il problema del tuo aggiornamento (soluzione)

Come accennato da @mins, i messaggi verranno stampati due volte sulla console per INFOe sopra: prima dal logger anonimo, poi dal suo genitore, il logger root che ha anche un ConsoleHandlerset di INFOdefault. Per disabilitare il logger di root, è necessario aggiungere questa riga di codice:logger.setUseParentHandlers(false);

Esistono altri modi per impedire che i log vengano elaborati dal gestore console predefinito del logger root menzionato da @Sheepy, ad esempio:

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

Ma Logger.getLogger("").setLevel( Level.OFF );non funzionerà perché blocca solo il messaggio passato direttamente al logger root, non il messaggio proviene da un logger figlio. Per illustrare come Logger Hierarchyfunziona, disegno il seguente diagramma:

inserisci qui la descrizione dell'immagine

public void setLevel(Level newLevel)impostare il livello di log specificando quali livelli di messaggio verranno registrati da questo logger. I livelli di messaggio inferiori a questo valore verranno scartati. Il valore di livello Level.OFF può essere utilizzato per disattivare la registrazione. Se il nuovo livello è nullo, significa che questo nodo deve ereditare il proprio livello dal suo antenato più vicino con un valore di livello specifico (non nullo).


0

Ho trovato il mio problema reale e non è stato menzionato in nessuna risposta: alcuni dei miei test unitari causavano l'esecuzione più volte del codice di inizializzazione della registrazione all'interno della stessa suite di test, incasinando la registrazione nei test successivi.


0

Ho provato altre varianti, questo può essere corretto

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

0

Questa soluzione mi sembra migliore per quanto riguarda la manutenibilità e il design for change:

  1. Creare il file delle proprietà di registrazione incorporandolo nella cartella del progetto della risorsa, da includere nel file jar:

    # Logging
    handlers = java.util.logging.ConsoleHandler
    .level = ALL
    
    # Console Logging
    java.util.logging.ConsoleHandler.level = ALL
  2. Carica il file delle proprietà dal codice:

    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) {
          // ...
       }
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.