Pourquoi les messages de journalisation Level.FINE ne s'affichent-ils pas?

110

Les JavaDocs pour l'java.util.logging.Level état:


Les niveaux par ordre décroissant sont:

  • SEVERE (valeur la plus élevée)
  • WARNING
  • INFO
  • CONFIG
  • FINE
  • FINER
  • FINEST (valeur la plus basse)

La source

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

Production

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

Énoncé du problème

Mon exemple définit le Levelsur FINER, donc je m'attendais à voir 2 messages pour chaque boucle. Au lieu de cela, je vois un seul message pour chaque boucle (les Level.FINEmessages sont manquants).

Question

Que faut-il changer pour voir la sortie FINE(, FINERou FINEST)?

Mettre à jour (solution)

Grâce à la réponse de Vineet Reynolds , cette version fonctionne selon mes attentes. Il affiche 3 x INFOmessages et 3 x FINEmessages.

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
la source
10
Il me semble que vous aurez les messages imprimés deux fois sur la console pour INFO et au-dessus: d'abord par l'enregistreur anonyme, puis par son parent, l'enregistreur global qui a également un ConsoleHandler réglé sur INFO par défaut. Pour désactiver le journal global, vous devez ajouter cette ligne de code: logger.setUseParentHandlers (false);
min
Je veux juste confirmer un commentaire sur les doubles. vous obtiendrez deux sorties sauf si vous utilisez .setUseParentHandlers (false);
xpagesbeast

Réponses:

124

Les enregistreurs enregistrent uniquement le message, c'est-à-dire qu'ils créent les enregistrements de journal (ou les demandes de journalisation). Ils ne publient pas les messages vers les destinations, ce qui est pris en charge par les gestionnaires. La définition du niveau d'un enregistreur entraîne uniquement la création d'enregistrements de journal correspondant à ce niveau ou plus.

Vous utilisez peut-être un ConsoleHandler(je ne pourrais pas en déduire où votre sortie est System.err ou un fichier, mais je suppose que c'est le premier), qui par défaut publie les enregistrements de journal du niveau Level.INFO. Vous devrez configurer ce gestionnaire, pour publier des enregistrements de journal de niveau Level.FINERet supérieur, pour le résultat souhaité.

Je recommanderais de lire le guide de présentation de Java Logging , afin de comprendre la conception sous-jacente. Le guide couvre la différence entre le concept d'un enregistreur et d'un manipulateur.

Modifier le niveau du gestionnaire

1. Utilisation du fichier de configuration

Le fichier de propriétés java.util.logging (par défaut, il s'agit du logging.propertiesfichier dans JRE_HOME/lib) peut être modifié pour changer le niveau par défaut du ConsoleHandler:

java.util.logging.ConsoleHandler.level = FINER

2. Création de gestionnaires lors de l'exécution

Ce n'est pas recommandé, car cela entraînerait le remplacement de la configuration globale. L'utilisation de ceci tout au long de votre base de code entraînera une configuration de journalisation éventuellement ingérable.

Handler consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(Level.FINER);
Logger.getAnonymousLogger().addHandler(consoleHandler);
Vineet Reynolds
la source
2
Merci. Cela a fait l'affaire. Je réalise maintenant pourquoi j'étais si confus au départ. J'avais précédemment travaillé avec des niveaux de journalisation, mais ma mise en œuvre à ce moment-là a simplement déposé chaque message journalisé dans une liste qui était affichée sans égard au Handler.
Andrew Thompson du
3
De rien. Et oui, la conception vous parvient, si l'on a écrit des enregistreurs qui ont simplement vidé des chaînes dans un fichier, une console, etc.
Vineet Reynolds
7
Et modifier le logging.properties dans la bibliothèque JRE globale est-il gérable?
James Anderson
2
Veuillez noter que le niveau de l'enregistreur lui-même doit être moins restrictif que le niveau du gestionnaire. Avant de journaliser, Java vérifie le niveau maximum entre l'enregistreur et le gestionnaire. Donc, si vous définissez uniquement handler.setLevel (Level.FINER); vous ne verrez rien car le niveau d'enregistrement par défaut est Level.INFO. Vous devez le régler sur FINER, FINEST ou ALL. Dites logger.setLevel (Level.ALL);
Jeff_Alieffson
Je suppose que cela résoudra le problème en tant que one-liner, même lorsque vous utilisez des enregistreurs anonymes et nommés dans un environnement par défaut:Logger.getGlobal().getParent().getHandlers()[0].setLevel(Level.FINER);
Mark Jeronimus
27

Le pourquoi

java.util.logging a un enregistreur racine qui prend la valeur par défaut Level.INFO, et un ConsoleHandler qui lui est attaché qui prend également la valeur par défaut Level.INFO. FINEest inférieur à INFO, les messages fins ne sont donc pas affichés par défaut.


Solution 1

Créez un enregistreur pour l'ensemble de votre application, par exemple à partir de votre nom de package ou de votre utilisation Logger.getGlobal(), et connectez-y votre propre ConsoleLogger. Ensuite, demandez à l'enregistreur racine de se fermer (pour éviter la sortie dupliquée des messages de niveau supérieur), ou demandez à votre enregistreur de ne pas transmettre les journaux à la racine.

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

Solution 2

Vous pouvez également abaisser la barre de l'enregistreur racine.

Vous pouvez les définir par code:

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

Ou avec le fichier de configuration de journalisation, si vous l' utilisez :

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

En abaissant le niveau global, vous pouvez commencer à voir les messages des bibliothèques principales, telles que certains composants Swing ou JavaFX. Dans ce cas, vous pouvez définir un filtre sur l'enregistreur racine pour filtrer les messages ne provenant pas de votre programme.

Sheepy
la source
applog.setUseParentHandlers (false) ces codes m'aident, merci beaucoup.
aolphn
4

pourquoi ma journalisation java ne fonctionne pas

fournit un fichier jar qui vous aidera à comprendre pourquoi votre connexion ne fonctionne pas comme prévu. Il vous donne une image complète des enregistreurs et des gestionnaires installés, des niveaux définis et à quel niveau dans la hiérarchie de journalisation.

Matthieu
la source
Ceci est un commentaire, pas une réponse.
hfontanez le
4

POURQUOI

Comme mentionné par @Sheepy, la raison pour laquelle cela ne fonctionne pas est que la valeur java.util.logging.Loggerpar défaut d'un enregistreur racine est Level.INFO, et ConsoleHandlerque l'enregistreur racine est également par défaut Level.INFO. Par conséquent, afin de voir la FINE(, FINERou FINEST) sortie, vous devez définir la valeur par défaut de l'enregistreur de racine et de son ConsoleHandlerà Level.FINEsuit comme:

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


Le problème de votre mise à jour (solution)

Comme mentionné par @mins, les messages seront imprimés deux fois sur la console pour INFOet au-dessus: d'abord par le logger anonyme, puis par son parent, le logger racine qui a également un ConsoleHandlerset à INFOpar défaut. Pour désactiver l'enregistreur racine, vous devez ajouter cette ligne de code:logger.setUseParentHandlers(false);

Il existe d'autres moyens d'empêcher le traitement des journaux par le gestionnaire de console par défaut de l'enregistreur racine mentionné par @Sheepy, par exemple:

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

Mais Logger.getLogger("").setLevel( Level.OFF );ne fonctionnera pas car il ne bloque que le message transmis directement à l'enregistreur racine, et non le message provient d'un enregistreur enfant. Pour illustrer le Logger Hierarchyfonctionnement, je dessine le schéma suivant:

entrez la description de l'image ici

public void setLevel(Level newLevel)définir le niveau de journalisation en spécifiant les niveaux de message qui seront enregistrés par cet enregistreur. Les niveaux de message inférieurs à cette valeur seront ignorés. La valeur de niveau Level.OFF peut être utilisée pour désactiver la journalisation. Si le nouveau niveau est nul, cela signifie que ce nœud doit hériter de son niveau de son ancêtre le plus proche avec une valeur de niveau spécifique (non nulle).

Joe Yichong
la source
0

J'ai trouvé mon problème réel et il n'a été mentionné dans aucune réponse: certains de mes tests unitaires provoquaient l'exécution du code d'initialisation de la journalisation plusieurs fois dans la même suite de tests, perturbant la journalisation des tests ultérieurs.

Alex R
la source
0

J'ai essayé d'autres variantes, cela peut être correct

    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
la source
0

Cette solution me paraît meilleure, en ce qui concerne la maintenabilité et la conception pour le changement:

  1. Créez le fichier de propriétés de journalisation en l'incorporant dans le dossier du projet de ressources, à inclure dans le fichier jar:

    # Logging
    handlers = java.util.logging.ConsoleHandler
    .level = ALL
    
    # Console Logging
    java.util.logging.ConsoleHandler.level = ALL
  2. Chargez le fichier de propriétés à partir du 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
la source