Pourquoi les messages de journalisation Level.FINE ne s’affichent pas?

L’état JavaDocs pour java.util.logging.Level :


Les niveaux en 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(Ssortingng[] 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)); } } } 

Sortie

 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 Level sur FINER , donc je m’attendais à voir 2 messages pour chaque boucle. Au lieu de cela, je vois un seul message pour chaque boucle (les messages Level.FINE sont manquants).

Question

Que faut-il changer pour voir la sortie FINE ( FINER ou FINEST )?

Mise à jour (solution)

Grâce à la réponse de Vineet Reynolds , cette version fonctionne selon mes attentes. Il affiche 3 messages INFO et 3 messages FINE .

 import java.util.logging.*; class LoggingLevelsBlunder { public static void main(Ssortingng[] 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)); } } } 

Les enregistreurs consignent 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, qui sont pris en charge par les gestionnaires. La définition du niveau d’un enregistreur ne fait que créer des enregistrements de journal correspondant à ce niveau ou à un niveau supérieur.

Vous utilisez peut-être un ConsoleHandler (je ne peux 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 les enregistrements de journal de niveau Level.FINER et supérieur pour obtenir le résultat souhaité.

Je recommande de lire le guide Java Logging Overview afin de comprendre la conception sous-jacente. Le guide couvre la différence entre le concept d’un enregistreur et d’un manipulateur.

Modification du niveau de gestionnaire

1. Utilisation du fichier de configuration

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

 java.util.logging.ConsoleHandler.level = FINER 

2. Création de gestionnaires à l’exécution

Ceci n’est pas recommandé car cela entraînerait la substitution de la configuration globale. L’utilisation de ceci dans toute votre base de code entraînera une configuration de logger éventuellement impossible à gérer.

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

Le pourquoi

java.util.logging a un enregistreur racine par défaut Level.INFO , et un ConsoleHandler qui lui est Level.INFO par défaut, Level.INFO . FINE est 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 du nom de votre package ou utilisez Logger.getGlobal() et connectez-vous à votre propre ConsoleLogger. Ensuite, demandez à root logger de fermer (pour éviter la duplication des messages de niveau supérieur), ou demandez à votre enregistreur de ne pas transférer 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 réduisant le niveau global, vous pouvez commencer à voir des messages provenant de bibliothèques principales, telles que des composants Swing ou JavaFX. Dans ce cas, vous pouvez définir un filtre sur l’enregistreur racine pour filtrer les messages non issus de votre programme.

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 un aperçu complet des enregistreurs et des gestionnaires installés et des niveaux définis et du niveau dans la hiérarchie de journalisation.

POURQUOI

Comme mentionné par @Sheepy, la raison pour laquelle cela ne fonctionne pas est que java.util.logging.Logger a un enregistreur racine par défaut Level.INFO , et que ConsoleHandler attaché à cet enregistreur racine est également par défaut Level.INFO . Par conséquent, pour afficher la sortie FINE (, FINER ou FINEST ), vous devez définir la valeur par défaut de l’enregistreur racine et de son ConsoleHandler sur Level.FINE comme suit:

 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 INFO et au-dessus: d’abord par l’enregistreur anonyme, puis par son parent, l’enregistreur racine qui a également défini ConsoleHandler sur INFO par défaut. Pour désactiver l’enregistreur racine, vous devez append 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 du consignateur 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 fonctionnement de la Logger Hierarchy l’ Logger Hierarchy , je dessine le diagramme suivant:

entrer la description de l'image ici

public void setLevel(Level newLevel) définit le niveau de journalisation en spécifiant les niveaux de message qui seront consigné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 noeud doit hériter de son niveau de son ancêtre le plus proche avec une valeur de niveau spécifique (non nulle).

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 resockets dans la même suite de tests, perturbant la journalisation lors des tests ultérieurs.

Essayé d’autres variantes, cela peut être approprié

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

Cette solution me semble 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 resortingeveURLOfJarResource(Ssortingng resourceName) { return Thread.currentThread().getContextClassLoader().getResource(resourceName); } public synchronized void initializeLogger() { try (InputStream is = resortingeveURLOfJarResource("logging.properties").openStream()) { LogManager.getLogManager().readConfiguration(is); } catch (IOException e) { // ... } }