API Logging Journalisation Journalisation des des évènements évènements © Philippe GENOUD UJF Janvier 2007 1 Journalisation Conserver sur un support sûr des événements survenus dans un système ou une application Un ou plusieurs fichiers journaux (log files) générés en cours d'exécution enregistrent des informations sur le déroulement de l'application. Nature de l'événement Date et heure Gravité Utilisateur…. Utilisés pour : Produire des statistiques sur utilisation du système (ex: log du serveur Apache) Détecter des problèmes d'exécution Reprise après pane © Philippe GENOUD UJF Novembre 2008 2 Api Api java.util.logging java.util.logging Java depuis la version 1.4 propose une API standard pour journaliser les événements. Définie dans le package java.util.logging D'usage simple Permet de journaliser des événements dans un fichier au format texte ou XML Différents niveaux de sévérité applicables aux messages journalisés API conçue pour rendre les opérations de logging le moins coûteuses possible Possibilité de changer dynamiquement quels niveaux de messages sont journalisés Permet de faire des logging détaillés lorsque nécessaire tout en minimisant l'impact sur l'application en mode normal Extensible © Philippe GENOUD 3 Novembre 2008 UJF Api Api java.util.logging java.util.logging Principale classes de l'API java.util.logging Génère log messages Logger Logger LogManager LogManager Object Object LogRecord LogRecord Handler Handler Encapsule contenu d'un log message MemoryHandler MemoryHandler ConsoleHandler ConsoleHandler © Philippe GENOUD Associés aux handlers pour formater les LogRecords Destination pour les LogRecords StreamHandler StreamHandler FileHandler FileHandler UJF Formatter Formatter SimpleFormatter SimpleFormatter SocketHandler SocketHandler Novembre 2008 XMLFormatter XMLFormatter <interface> <interface> Filter Filter 4 Principes Principes généraux généraux de de l'API l'API Logging Logging Filter À un Logger peuvent être associés plusieurs handlers Application 1 4 2 Logger 3 Formatter Handler 5 Filter 7 Handler Monde extérieur Fichier Console Socket 6 Filter Formatter 1 L'application demande à un Logger d'enregistrer une information dans le journal 2 Le Logger crée un objet LogRecord qui représente cette demande 3 Le Logger transmet éventuellement le LogRecord à un filtre pour décider si il doit être traité ou non 4 Si le filtre autorise son traitement le LogRecord est transmis à un Handler. 5 Le Handler transmet éventuellement le LogRecord à un filtre pour décider si il doit être traité ou non 6 Si le filtre autorise son traitement le LogRecord est transmis à un Formatter qui se charge de sa mise en forme et de son éventuelle localisation 7 Le Handler publie lerésultat produit par le Formatter sur un flux d'entrée/sortie © Philippe GENOUD UJF Novembre 2008 5 Création Création d'un d'un journal journal Journal représenté par un objet instance de la classe Logger Doit être créé qu'une seule fois et partagé par les instances des différentes classes de l'application Accès au journal dans une classe Référence statique pour accéder au journal Nom du journal private static Logger logger = Logger.getLogger("monLogger"); Méthode statique de la classe Logger. © Philippe GENOUD si plusieurs classes appellent getLogger() avec le même nom, le journal est créé au premier appel ; pour les prochains appels le journal n'est pas recréé, seule sa référence est retournée . UJF Novembre 2008 6 Création Création d'un d'un journal journal Une fois le journal (Logger) créé il faut l'associer à un ou plusieurs handlers qui géreront les messages (LoggerRecords) Pour rediriger les messages du logger vers un fichier on utilise un objet FileHandler Création d'un file Handler qui redirigera les LoggerRecords vers vers le fichier myLog.log Handler fh = new FileHandler("myLog.log"); Plusieurs constructeurs permettent de spécifier le ou les fichiers utilisés et la manière dont ils le sont. Handler fh = new FileHandler(); Système choisit lui-même le nom du fichier Handler fh = new FileHandler("myLog.log", true); Le fichier est repris tel quel (true) ou recrée (false) Handler fh = new FileHandler("myLog.log", 5000, 4); logger.addHandler(fh); © Philippe GENOUD Le journal est divisé en 4 fichiers de 5000 octets, utilisés de manière cyclique,de nom myLog.log.i avec i de 0 à 3 (motif par défaut) Associe le FileHanlder au logger UJF Novembre 2008 7 Création Création d'un d'un journal journal public FileHandler(String pattern,int limit,int count, boolean append) pattern : motif du nom du fichier ou des fichiers à créer limit : nombre maximum d'octets pouvant être écrits dans le fichier (0 pour pas de limite). Si cette limite est dépassée, le FileHandler ferme le fichier, le renomme et débute un nouveau fichier de log avec le nom original count : nombre de fichiers à utiliser append : le fichier est repris tel quel (true) ou recrée (false) Lorsque ces arguments ne sont pas spécifiés dans le constructeur, une valeur par défaut définie par le LogManager est utilisée. pattern : %h/java%u.log limit : 0 count : 1 append : false © Philippe GENOUD UJF Novembre 2008 8 Création Création du du journal journal Des caractères spéciaux peuvent être utilisés pour définir le nom du (des) fichier(s) de log Caractère Séparateur de répertoire pour la plateforme / Signification %h Répertoire de connexion de l'utilisateur (équivalent de la propriété système "user.home") %t Répertoire temporaire du système %u Un nombre unique utilisé pour distinguer le fichier log d'autres fichiers log qui auraient le même motif %g Le nombre généré automatiquement pour la rotation cyclique des fichiers quand limit non nul et count > 1 %% '%' Exemple Handler fh = new FileHandler("%h/myApps.%g.log", 5000, 3); 3 fichiers logs dans le "homedir" de l'utilisateur nommés mYApps.0.log, myApps.1.log et myApps.2.log © Philippe GENOUD UJF Novembre 2008 9 Ecriture Ecriture d'un d'un message message Pour poster un message dans le journal on utilise la méthode log() de l'objet Logger. public void log(Level level, String msg) Le message Niveau du message. Si le logger est activé pour ce niveau le message est dirigé vers tous les handlers associés au logger, sinon le message est ignoré Le type Level définit 7 + 2 niveaux pour les messages. Les niveaux sont ordonnés : lorsque le Logger est activé pour un niveau tous les niveaux supérieurs le sont également Les niveaux dans l'ordre décroissant Niveau par défaut Niveau OFF SEVERE WARNING INFO CONFIG FINE FINER FINEST ALL Description Aucun niveau Pour indiquer un problème sérieux Pour signaler un problème potentiel Message d'information Configuration Trace d'exécution Trace d'exécution plus précise Trace d'exécution encore plus précise Tous les niveaux © Philippe GENOUD UJF La méthode void setLevel(Level newLevel) de la classe Logger permet de modifier le niveau pour lequel le journal est activé. Exemple : pour logger tous les messages logger.setLevel(Level.ALL) Novembre 2008 10 Ecriture Ecriture d'un d'un message message pour simplifier l'envoi de message dans le journal il existe pour chaque niveau une méthode qui évite d'avoir à spécifier le niveau en paramètre. public void severe(String msg) public void warning(String msg) public void info(String msg) public void config(String msg) public void fine(String msg) public void finer(String msg) public void finest(String msg) Exemple : logger.info("connection closed"); est équivalent à Logger.log(LEVEL.INFO,"connection closed"); © Philippe GENOUD UJF 11 Novembre 2008 Formatage des messages A un handler est associé un formateur qui se charge de la mise en forme des messages avant leur écriture dans le journal L'API logging propose deux classes de formatage de base SimpleFormatter XMLFormatter (défaut pour les FileHandler) Il est possible de créer son propre "formateur" en étendant la classe Formatter Utilisation du message setFormatter pour fixer le formateur d'un objet Handler Syntaxe : Associés aux handlers pour formater les LogRecords Formatter Formatter SimpleFormatter SimpleFormatter XMLFormatter XMLFormatter MyFormatter MyFormatter void setFormatter(Formatter newFormatter) Exemple : fh.setFormatter(new myFormatter()); © Philippe GENOUD UJF Novembre 2008 12 Formatage Formatage des des messages messages public static void main(String[] args) throws SecurityException, IOException { Logger logger = Logger.getLogger("monLogger"); Handler fh = new FileHandler("myLog.log"); Fichier myLog.log logger.addHandler(fh); logger.log(Level.INFO, "coucou"); <?xml version="1.0" encoding="windows-1252" standalone="no"?> } <!DOCTYPE log SYSTEM "logger.dtd"> <log> <record> <date>2008-11-13T19:32:13</date> <millis>1226601133062</millis> <sequence>0</sequence> Informations extraites <logger>monJournal</logger> <level>INFO</level> de l'objet LogRecord <class>poo.cours.logging.TestAPILogging</class> <method>main</method> <thread>10</thread> <message>coucou</message> </record> </log> Utilisation du formateur par défaut : XMLFormatter public static void main(String[] args) throws SecurityException, IOException { Logger logger = Logger.getLogger("monLogger"); Handler fh = new FileHandler("myLog.log"); Handler fh.setFormatter(new SimpleFormatter()); logger.addHandler(fh); logger.log(Level.INFO, "coucou"); } Fichier myLog.log Utilisation d'un formateur SimpleFormatter 13 nov. 2008 19:39:35 poo.cours.logging.TestAPILogging main INFO: coucou © Philippe GENOUD UJF 13 Novembre 2008 Formatage Formatage des des messages messages Définition d'une nouvelle classe de Formatter @Override public String getHead(Handler h) { return "debut du log\n------------\n"; } package poo.cours.logging; import import import import java.util.Date; java.util.logging.Formatter; java.util.logging.Handler; java.util.logging.LogRecord; public class MonFormateur extends Formatter { } @Override public String format(LogRecord record) { return "le message '" + record.getMessage() + "' est arrivé à " + new Date(record.getMillis()) + "\n"; } @Override public String getTail(Handler h) { return "------------\nfin du log\n"; } Écriture d'un entête et d'une fin au journal Formatage d'un enregistrement public static void main(String[] args) throws SecurityException, IOException { Logger logger = Logger.getLogger("monLogger"); Handler fh = new FileHandler("myLog.log"); Handler fh.setFormatter(new MonFormateur()); logger.addHandler(fh); logger.log(Level.INFO, "coucou"); } Utilisation d'un formateur MonFormateur Fichier myLog.log debut du log -----------le message 'coucou' est arrivé à Thu Nov 13 22:52:12 CET 2008 -----------fin du log © Philippe GENOUD UJF Novembre 2008 14 Filtrage Filtrage Les filtres permettent à l'application de définir éventuelleent ses propres règles de filtrage des LogRecords en plus des niveaux de message Un filtre peut être associé à Logger ou à un Handler Méthode void setFilter(Filter newFilter) des classes Logger et Handler Un filtre est définit en implémentant l'interface Filter. public boolean isLoggable(LogRecord record) Exemple : import java.util.logging.Filter; import java.util.logging.LogRecord; public class myFilter implements Filter { // décide si l’enregistrement doit être publié ou pas public boolean isLoggable(LogRecord record) { return record.getMessage().startsWith("DEBUG");; } } © Philippe GENOUD UJF 15 Novembre 2008 Hiérarchie Hiérarchie des des Loggers Loggers public static void main(String[] args) throws SecurityException, IOException { Logger logger = Logger.getLogger("monLogger"); Handler fh = new FileHandler("myLog.log"); Handler fh.setFormatter(new MonFormateur()); logger.addHandler(fh); logger.log(Level.INFO, "coucou"); } Utilisation d'un formateur MonFormateur Fichier myLog.log debut du log -----------le message 'coucou' est arrivé à Thu Nov 13 22:52:12 CET 2008 -----------fin du log Sur la console : P:\CoursAPILogging\bin>java poo.cours.logging.TestAPILogging 14 nov. 2008 18:01:41 poo.cours.logging.TestAPILogging main INFO: coucou P:\CoursAPILogging\bin> D'où provient cette sortie ? Le logger spécifié dans le programme n'a qu'un handler de type FileHandler © Philippe GENOUD UJF Novembre 2008 16 Hiérarchie Hiérarchie des des Loggers Loggers Explication : Les Loggers sont organisés en une structure arborescente Logger racine toujours défini – sans nom – ConsoleHandler par défaut Chaque Logger a un Logger parent Par défaut un Logger transmet les LogRecords à ses Handlers mais aussi à son Logger parent qui les transmet à ses propres Handlers (de manière récursive jusqu'au Logger racine) Console "" root Logger 14 nov. 2008 18:01:41 poo.cours.logging.TestAPILogging main INFO: coucou ConsoleHandler Le Logger parent ne fait pas de filtrage ou de contrôle de niveau du LogRecord Fichier myLog.log "monLogger" debut du log -----------le message 'coucou' est arrivé à Thu Nov 13 22:52:12 CET 2008 -----------fin du log FileHandler logger.log(Level.INFO, "coucou"); © Philippe GENOUD UJF 17 Novembre 2008 Hiérarchie Hiérarchie des des Loggers Loggers La hiérarchie des loggers est construite à partir de leurs noms Dans un nom de logger un '.' correspond à un niveau de hiérarchie Niveau 0 Logger l1 = Logger.getLogger("l1"); Niveau 1 Logger l1_1 = Logger.getLogger("l1.1"); Niveau 2 Logger l1_2 = Logger.getLogger("l1.2"); Logger l1_1_1 = Logger.getLogger("l1.1.1"); Niveau 3 Logger racine "" l1 l1.1 l1.2 l1.1.1 Un Logger hérite un certain nombre d'attributs de ses parents LogingLevel : si est null, le Logger hérite du premier LoggingLevel non null parmi ses parents Handlers : par défaut le Logger transmet récursivement à tous parents les LogRecord qu'il génère ResourceBundle : pour la localisation (comme pour le LogingLevel, si le ResourceBundle est null, il est alors hérité au travers de la hiérarchie des loggers) C'est joli, mais à quoi cela sert tout cela ? © Philippe GENOUD UJF Novembre 2008 18 Hiérarchie Hiérarchie des des Loggers Loggers La hiérarchie des Loggers et le forwarding des LogRecords permet d'avoir un contrôle fin sur la journalisation. En utilisant des noms de logger qui correspondent à la hiérarchie des packages on peut facilement contrôler le logging package par package (voire classe par classe) ce qui peut être très utile pour une analyse fine d'une application package package1.subPackage1; ... public class MyClass1 { private static Logger logger = Logger.getLogger("package1.subPackage1.MyClass1"); ... "" } package par défaut Recommandation de la documentation de l'API logging - Nom complet (fully qualified name) de subPackage1 subPackage2 MyClass1 … MyClassN la classe (une journal par classe) - Nom du package (toutes les classes du package utilisent le même journal) package2 package1 … … LogManager.getLogManager(). getLogger("package1.subPackage1") .setLevel(Level.ALL) Fixe un niveau de journal ALL pour toutes les classes du package package1.subPackage1 © Philippe GENOUD UJF Novembre 2008 19 Configuration Configuration des des journaux journaux La configuration par défaut du LogManager est initialisée à partir d'un fichier de propriétés logging.properties défini dans le répertoire lib du JRE de la JVM Cette configuration par défaut peut être changée en modifiant ce fichier Possibilité d'utiliser une autre fichier de configuration à l'exécution java -Djava.util.logging.config.file=myLoggingconfigurationFile # Global properties handlers= java.util.logging.ConsoleHandler, java.util.logging.FileHandler .level= INFO # # Handler specific properties. # #default file output is in user's home directory. java.util.logging.FileHandler.pattern = %h/java%u.log java.util.logging.FileHandler.limit = 50000 java.util.logging.FileHandler.count = 1 java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter # Limit the message that are printed on the console to INFO and above. java.util.logging.ConsoleHandler.level = INFO java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter # extra control for each logger. # set the com.xyz.foo logger to only log SEVERE messages. com.xyz.foo.level = SEVERE © Philippe GENOUD UJF Novembre 2008 20 Conclusion Conclusion Java.util.logging fourni une API standard pour la journalisation Simple, flexible, efficace Il existe d'autres alternatives qui offrent plus de fonctionnalités: Log4j du projet Apache http://logging.apache.org/log4j/ Extrait de la FAQ : Why should I use log4j when JDK 1.4 already ships with a logging API? Although both APIs are conceptually similar, the log4j API is significantly more flexible and offers many more features, too numerous to be listed here. Commons Logging du projet Jakarta Commons http://commons.apache.org/logging/ jLO http://jlo.jzonic.org/ Monolog développée par le consortium ObjectWeb http://monolog.objectweb.org/ © Philippe GENOUD UJF Novembre 2008 21 Références Références An Introduction to the Java Logging API, Brian R. Gilstrap http://www.onjava.com/pub/a/onjava/2002/06/19/log.html Java Logging Technology http://java.sun.com/javase/6/docs/technotes/guides/logging/index.html © Philippe GENOUD UJF Novembre 2008 22