|
|
|
|
|
|
Développons en Java v 1.60 | |
| Copyright (C) 1999-2011 Jean-Michel DOUDOUX |
![]() |
![]() |
![]() |
Le logging consiste à ajouter des traitements dans les applications pour permettre l'émission et le stockage de messages suite à des événements.
Le logging est utile pour tous les types d'applications en permettant notamment par exemple de conserver une trace des exceptions qui sont levées dans l'application et des différents événements anormaux ou normaux liés à l'exécution de l'application.
Le logging permet de gérer des messages émis par une application durant son exécution et de permettre leur exploitation immédiate ou à postériori. Ces messages sont d'ailleurs très utiles lors de la mise en point d'une application ou lors de son exploitation pour comprendre son fonctionnement ou résoudre une anomalie.
Ce chapitre contient plusieurs sections :
Le logging est une activité technique utile et nécessaire dans une application pour :
L'importance du logging croit avec la taille et la complexité de l'application qui l'utilise.
Une API de logging fait généralement intervenir trois composants principaux :
Le logging doit faire partie intégrante des fonctionnalités d'une application. Bien sûre le niveau de gravité des messages n'est pas le même en développement et en production mais le code de l'application doit rester le même. Seule la configuration du logging doit changer dans les différents environnements.
Généralement la configuration peut être externalisée dans un fichier ce qui rend l'utilisation de l'API plus souple et flexible.
La modification de la configuration du logging en cours d'exécution de l'application (soit dynamiquement soit par rechargement de la configuration) est importante pour permettre d'avoir couramment un niveau de log acceptable et d'avoir au besoin un niveau de log plus fin sans avoir à relancer l'application.
Les API de logging ont plusieurs inconvénients :
Le logging est particulièrement important dans une application notamment côté serveur mais une utilisation à outrance ou une mauvaise utilisation de cette fonctionnalité peut dégrader les performances générales de l'application.
Les frameworks de logging sont conçus pour limiter la consommation en ressources nécessaires à leur mise en oeuvre mais elle existe tout même et croit naturellement avec le nombre de messages émis.
L'utilisation d'une API de Logging implique donc une surcharge de consommation de ressources (CPU, mémoire, ...) mais elle se justifie par l'apport des informations fournies en cas de problème sous réserve que ces informations aient été judicieusement choisies.
Voici quelques règles pour une bonne mise en oeuvre du logging :
Pour des traces d'exécution, il est pratique d'émettre un message en début d'une méthode qui affiche les paramètres en entrée et un message à la fin de la méthode avec la valeur de retour
Il est fortement recommandé d'utiliser une API de logging plutôt que d'utiliser la méthode System.out.println() pour plusieurs raisons :
Sur des applications utilisées par plusieurs utilisateurs, par exemple une application web, il peut très utile de faire figurer dans le message une identité sur le responsable de l'action (par exemple, l'adresse IP d'une requête http).
De nombreux frameworks existent pour mettre en oeuvre le logging dont :
Log4j du groupe Apache Jakarta est sûrement l'API la plus répandue et la plus populaire.
Les qualités de Log4j notamment sa simplicité de mise en oeuvre, ses fonctionnalités, sa fiabilité et son évolutivité lui permettent d'être le standard de facto pour le logging.
Depuis la version 1.4 du JDK, Java intègre une API de logging qui est le standard officiel pour le logging mais qui est légèrement moins riche en fonctionnalité que Log4J mais possède l'avantage d'être fournie dans les API de base.
Afin de faciliter l'utilisation du logging, le groupe Jakarta a développé un wrapper nommé JCL (JakartaCommon Logging) qui permet d'utiliser de façon transparente Log4j ou l'API Logging du JDK en utilisant le tronc commun de ces deux API.
|
|
Log4j est un projet open source distribué sous la licence Apache Software initialement créé par Ceki Gülcü et maintenu par le groupe Jakarta. Cette API permet aux développeurs d'utiliser et de paramétrer un système de gestion de journaux (logs). Il est possible de fournir les paramètres dans un fichier de configuration ce qui rend sa configuration facile et souple. Log4j est compatible avec le JDK 1.1. et supérieur. |
Log4j gère plusieurs niveaux de gravités et les messages peuvent être envoyés dans plusieurs flux : un fichier sur disque, le journal des événements de Windows, une connexion TCP/IP, une base de données, un message JMS, etc ...
Log4j utilise trois composants principaux pour assurer l'envoi de messages selon un certain niveau de gravité et contrôler à l'exécution le format et la ou les cibles de destination des messages :
Ces trois types de composants sont utilisés ensemble pour émettre des messages vers différentes cibles de stockage.
Ceci permet au framework de déterminer les messages qui doivent être loggués, la façon dont ils seront formatés et vers quelle cible les messages seront envoyés.
La popularité de Log4J est largement liée à sa facilité d'utilisation, ses nombreuses fonctionnalités extensibles et sa fiabilité. Comme le logging n'est jamais une fonctionnalité principale d'une application, Log4j se veut facile à mettre en oeuvre.
Les principales caractéristiques de Log4j sont :
Un autre avantage de log4J est de pouvoir être utilisé avec toutes les versions du JDK depuis la 1.1.
L'externalisation de la configuration de Log4j dans un fichier externe permet de modifier la configuration des traitements de logging sans avoir à modifier le code source de l'application.
La hiérarchie des loggers permet un contrôle très fin de la granularité des messages ce qui permet de réduire le volume de données des logs.
Log4j propose en standard plusieurs destinations de stockage des messages : fichiers, gestion d'événements Windows, Syslog Unix, base de données, email, message JMS, ...
L'API Log4j est regroupée dans plusieurs packages :
|
Package |
Rôle |
|
org.apache.log4j |
Contient les principales classes et interfaces |
|
org.apache.log4j.spi |
System Programming Interface pour étendre Log4j |
|
org.apache.log4j.chainsaw |
Application Swing Chainsaw pour visualiser les logs formatée par un XMLLayout ou émise par un SocketAppender |
|
org.apache.log4j.config |
Classes pour la gestion des propriétés des composants |
|
org.apache.log4j.helpers |
Utilitaires |
|
org.apache.log4j.jdbc |
Classes pour stocker les messages dans une base de données |
|
org.apache.log4j.jmx |
Classes pour permettre la configuration de Log4j via JMX |
|
org.apache.log4j.lf5 |
Application Swing Log Force 5 pour visualiser les logs |
|
org.apache.log4j.net |
Classes pour envoyer les messages au travers le réseau (JMS, SMTP, Sockets, ...) |
|
org.apache.log4j.nt |
Classes pour envoyer les messages dans le système de gestion des événements de Windows |
|
org.apache.log4j.or |
Utilitaires pour formater des objets |
|
org.apache.log4j.performance |
Classes de tests des performances |
|
org.apache.log4j.xml |
Classes pour permettre la configuration de Log4j avec un fichier XML |
|
org.apache.log4j.varia |
Classes diverses |
Le site officiel de Log4j est à l'url : http://logging.apache.org/log4j/
Log4j est disponible dans trois versions majeures :
Cette section fournit des informations et un premier exemple pour la mise en oeuvre de Tomcat.
Il faut télécharger le fichier apache-log4j-1.2.xx.zip à l'url http://logging.apache.org/log4j/1.2/download.html
Il suffit ensuite de décompresser l'archive dans un répertoire du système. L'archive contient entre autre les sources, la documentation, des exemples et la bibliothèque log4j-1.2.x.jar.
Pour utiliser Log4j, il suffit d'ajouter le fichier log4j-1.2.x.jar dans le classpath de l'application.
Il faut définir un fichier de configuration : configuration des loggers, définition des appenders, association des appenders aux loggers avec un layout.
Dans le code source des classes, il faut :
Cette section va mettre en oeuvre Log4j dans un exemple très simple.
Il faut créer un fichier log4j.properties stocké dans le classpath de l'application : ce fichier contient la configuration de Log4j pour l'application.
| Exemple : |
log4j.rootLogger=DEBUG, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d [%-5p] (%F:%M:%L) %m%n |
Cette configuration définit le niveau de gravité DEBUG pour le logger racine et lui associe un logger nommé arbitrairement stdout. Par héritage, tous les loggers de l'application vont hériter de cette configuration.
L'appender nommé stdout est de type ConsoleAppender : il envoie les messages sur la console standard.
Un layout personnalisé est associé à l'appender nommé stdout pour formater les messages. Chaque séquence commençant par le caractère % sera remplacé dynamiquement par sa valeur correspondante. Par exemple : %d correspond à la date/heure, %p au niveau de gravité, %m le message, %n un retour chariot, ...
Pour mettre en oeuvre l'API dans le code source, il faut tout d'abord obtenir une instance du logger à utiliser en utilisant la méthode getLogger() de la classe Logger.
Chaque message est émis en utilisant la méthode correspondant au niveau de gravité choisi de la classe Logger.
| Exemple : |
package com.jmdoudoux.test.log4j;
import org.apache.log4j.Logger;
public class TestLog4j1 {
private static Logger logger = Logger.getLogger(TestLog4j1.class);
public static void main(String[] args) {
logger.debug("msg de debogage");
logger.info("msg d'information");
logger.warn("msg d'avertissement");
logger.error("msg d'erreur");
logger.fatal("msg d'erreur fatale");
}
} |
L'exécution de cette classe permet d'afficher sur la console les différents messages
| Résultat : |
2008-06-08 10:16:21,546 [DEBUG] (TestLog4j1.java:main:13) msg de debogage 2008-06-08 10:16:21,546 [INFO ] (TestLog4j1.java:main:14) msg d'information 2008-06-08 10:16:21,546 [WARN ] (TestLog4j1.java:main:15) msg d'avertissement 2008-06-08 10:16:21,546 [ERROR] (TestLog4j1.java:main:16) msg d'erreur 2008-06-08 10:16:21,546 [FATAL] (TestLog4j1.java:main:17) msg d'erreur fatale |
Une simple modification du fichier de configuration permet de changer le niveau de gravité des messages pris en compte. Par exemple en remplaçant DEBUG par ERROR
La réexécution de la classe qui n'a pas été modifiée et donc pas recompilée permet d'afficher sur la console uniquement les messages dont la gravité est supérieure ou égale à ERROR.
| Résultat : |
2008-06-08 10:18:47,530 [ERROR] (TestLog4j1.java:main:13) msg d'erreur 2008-06-08 10:18:47,530 [FATAL] (TestLog4j1.java:main:14) msg d'erreur fatale |
Les versions antérieures à la 1.2 de Log4J utilisent la classe Category pour gérer les messages et la classe Priority pour encapsuler les niveaux de gravité.
Log4j gère des priorités pour permettre à une instance de la classe Category de déterminer si le message sera envoyé dans la log ou non. Il existe cinq priorités qui possèdent un ordre hiérarchique croissant :
La classe org.apache.log4j.Priority encapsule ces priorités.
Chaque Category est associée à une priorité qui peut être changée dynamiquement. La catégorie détermine si un message doit être envoyé dans la log en comparant sa priorité avec la priorité du message. Si celle-ci est supérieure ou égale à la priorité de la Category, alors le message est envoyé vers la cible de destination de la log.
La méthode setPropriety() de la classe Category permet de préciser la priorité.
Si aucune priorité n'est donnée à une catégorie, elle "hérite" de la priorité de la première catégorie en remontant dans la hiérarchie dont la priorité est renseignée.
Exemple
: soit trois catégories
root associée à la priorité INFO
categorie1 nommée "org" sans priorité particulière
categorie2 nommée "org.moi" associée à la priorité ERROR
categorie3 nommée "org.moi.projet" sans priorité particulière
Une
demande d'émission de message avec la priorité DEBUG sur categorie1 n'est pas
traitée car la priorité INFO héritée est supérieure à DEBUG.
Une demande avec la priorité WARN sur categorie1 est traitée car la priorité
INFO héritée est inférieure à WARN .
Une demande avec la priorité DEBUG sur categorie3 n'est pas traitée car la priorité
ERROR héritée est supérieure à DEBUG.
Une demande avec la priorité FATAL sur categorie3 est traitée car la priorité
ERROR héritée est inférieure à FATAL.
En fait dans l'exemple, aucune demande avec la priorité DEBUG ne sera traitée.
Au niveau applicatif, il est possible d'interdire le traitement d'une priorité et de celle inférieure en utilisant le code suivant : Category.getDefaultHierarchy().disable(). Il faut fournir la priorité à la méthode disable().
Il est possible d'annuler ce traitement dynamiquement en positionnement la propriété système log4j.disableOverride.
La classe org.apache.log4j.Category détermine si un message doit être envoyé dans la ou les logs qui lui sont associés.
Chaque Category possède un nom qui est sensible à la casse. Pour créer une instance de la classe Category il faut utiliser la méthode statique getInstance() qui attend en paramètre le nom de la Category. Si une Category existe déjà avec le nom fourni, alors la méthode getInstance() renvoie l'instance existante.
Il est pratique de fournir le nom complet de la classe comme nom de la Category dans laquelle elle est instanciée mais ce n'est pas une obligation. Il est ainsi possible de créer une hiérarchie spécifique différente de celle de l'application, par exemple basée sur des aspects fonctionnels. L'inconvénient d'associer le nom de la classe au nom de la catégorie est qu'il faut instancier un objet Category dans chaque classe : le plus pratique est de déclarer cet objet static.
| Exemple : |
public class Classe1 {
static Category category = Category.getInstance(Classe1.class.getName());
...
} |
La méthode log(Priority, Object) permet de demander l'émission d'un message associé au niveau de gravité fourni en paramètre. Plusieurs méthodes sont des raccourcis qui évitent d'avoir à préciser le niveau de gravité car celui utilisé sera automatiquement celui associé à la méthode (debug(Object), info(Object), warn(Object), error(Object), fatal(Object)).
Toutes ces méthodes possèdent une surcharge qui attend en paramètre supplémentaire un objet de type Throwable. Ces méthodes ajouteront automatiquement au message la pile d'appels (stacktrace) de l'exception.
La demande est traitée en fonction de la hiérarchie de la Category et de la priorité du message.
Pour éviter d'éventuels traitements inutiles de création du message, il est possible d'utiliser la méthode isEnabledFor(Priority) pour savoir si la catégorie prend en compte la priorité ou non.
| Exemple : |
import org.apache.log4j.*;
public class TestIsEnabledFor {
static Category cat1 = Category.getInstance(TestIsEnabledFor.class.getName());
public static void main(String[] args) {
int i=1;
int[] occurrence={10,20,30};
BasicConfigurator.configure();
cat1.setPriority(Priority.WARN) ;
cat1.warn("message de test");
if(cat1.isEnabledFor(Priority.INFO)) {
System.out.println("traitement du message de priorité INFO");
cat1.info("La valeur de l'occurrence "+i+" = " + String.valueOf(occurrence[i]));
}
if(cat1.isEnabledFor(Priority.WARN)) {
System.out.println("traitement du message de priorité WARN");
cat1.warn("La valeur de l'occurrence "+i+" = " + String.valueOf(occurrence[i]));
}
}
} |
| Résultat : |
0 [main] WARN TestIsEnabledFor - message de test traitement du message de priorit_ WARN 50 [main] WARN TestIsEnabledFor - La valeur de l'occurrence 1 = 20 |
Le nom de la Category permet d'établir une hiérarchie dans les Categorys : ce nom est composé de mots séparés par un caractère point comme pour les packages. D'ailleurs par simplicité et par convention c'est le nom pleinement qualifié de la classe qui est utilisé.
Il existe toujours une catégorie racine créée par Log4J : pour obtenir une instance de cette Category, il faut utiliser la méthode getRoot() de la classe Category car elle ne possède pas de nom.
La méthode getInstance() de la classe Category renvoie toujours la même instance pour un même nom de catégorie. Si cette instance n'existe pas la méthode la créée sinon elle retourne celle existante.
Le message n'est pris en compte que si son niveau de gravité est supérieur ou égal à celui de la catégorie.
Par défaut, une Category hérite du niveau de gravité de sa Category mère selon la hiérarchie des catégories basée sur leurs noms. Ceci est possible car la Category racine à un niveau de gravité par défaut initialisé à DEBUG.
Par exemple, la catégorie com.jmdoudoux.test.log4j hérite des caractéristiques de la catégorie com.jmdoudoux.test.
Il est possible d'associer un niveau de gravité à la Category de façon statique en utilisant la méthode setPriority().
Le nom de la catégorie permet de la placer dans une hiérarchie dont la racine est une catégorie spéciale nommée root qui est créée par défaut sans nom.
La classe Category possède une classe statique getRoot() pour obtenir la catégorie racine.
La hiérarchie des noms est établie grâce à la notation par point comme pour les packages. D'ailleurs par convention, le nom de la catégorie correspond généralement au nom pleinement qualifié de la classe qui va utiliser la catégorie.
Exemple : soit trois catégories
categorie1 nommée "org"
categorie2 nommée "org.moi"
categorie3 nommée "org.moi.projet"
Categorie3 est fille de categorie2, elle même fille de categorie1.
Cette relation hiérarchique est importante car la configuration établie pour une catégorie est automatiquement propagée par défaut aux catégories enfants.
L'ordre de la création des catégories de la hiérarchie ne doit pas obligatoirement respecter l'ordre de la hiérarchie. Celle-ci est constituée au fur et à mesure de la création des catégories.
Les classes Category et Priority sont deprecated et remplacées respectivement par les classes Logger et Level.
A partir de la version 1.2 de Log4j, la classe Priority ne doit plus être utilisée : il est préférable d'utiliser sa classe fille Level.
Attention la classe Priority n'est pas marquée deprecated car la classe Level en hérite.
La classe org.apache.log4j.Level encapsule donc un niveau de gravité.
Log4j définit plusieurs niveaux de gravité en standard possédant un ordre hiérarchique :
Deux autres niveaux particuliers sont définis et utilisés dans la configuration :
Il est possible de définir ces propres niveaux de gravité en créant une classe qui hérite de la classe Level.
Le choix du niveau de gravité associé à un message est très important. Voici quelques exemples d'utilisation selon chaque niveau de gravité :
|
Niveau de gravité |
Exemple d'utilisation |
|
TRACE |
Entrée et sortie de méthodes |
|
DEBUG |
Affichage de valeur de données |
|
INFO |
Chargement d'un fichier de configuration, début et fin d'exécution d'un traitement long |
|
WARN |
Erreur de login, données invalides |
|
ERROR |
Toutes les exceptions capturées qui n'empêchent pas l'application de fonctionner |
|
FATAL |
Indisponibilité d'une base de données, toutes les exceptions qui empêchent l'application de fonctionner |
A partir de la version 1.2 de Log4j, la classe Category ne doit plus être utilisée : il est préférable d'utiliser sa classe fille Logger.
Attention la classe Category n'est pas marquée deprecated car la classe Logger en hérite.
La classe org.apache.log4j.Logger permet donc comme la classe Category de demander l'envoi d'un message dans le système de logs. Un logger compare son niveau de gravité avec celui du message : si ce dernier est supérieur ou égal à celui du logger alors le message est traité.
Un logger est associé à un ou plusieurs appenders : si le message est à traité, celui-ci est envoyé par le logger à ses appenders.
La classe Logger héritant de la classe Category, elle possède toutes ses méthodes notamment celles permettant l'émission d'un message. L'émission de messages se fait donc en utilisant la méthode log() ou une des méthodes utilisant implicitement un niveau de gravité (debug(), info(), warn(), error(), fatal()).
Exemple : les deux lignes de code sont équivalentes
| Exemple : |
logger.log(Level.INFO, "mon message");
logger.info("mon message"); |
Pour obtenir une instance de la classe Logger, il faut utiliser sa méthode statique getLogger(). Cette méthode attend en paramètre le nom du logger.
| Exemple : |
package com.jmdoudoux.test.log4j;
import org.apache.log4j.Logger;
public class MaClasse {
private static final Logger logger = Logger.getLogger("com.jmdoudoux.test.log4j.MaClasse");
} |
Comme généralement ce nom correspond au nom pleinement qualifié de la classe, une version surchargée de la méthode getLogger() attend en paramètre un objet de type Class pour en extraire le nom.
| Exemple : |
package com.jmdoudoux.test.log4j;
import org.apache.log4j.Logger;
public class MaClasse {
private static final Logger logger = Logger.getLogger(MaClasse.class);
} |
La méthode getLogger() permet de s'assurer que pour un même nom cela soit toujours la même instance qui est retournée.
| Exemple : |
package com.jmdoudoux.test.log4j;
import org.apache.log4j.Logger;
public class TestLog4j9 {
public static void main(String[] args) {
Logger loggerA = Logger.getLogger("com.jmdoudoux.test.log4j");
Logger loggerB = Logger.getLogger("com.jmdoudoux.test.log4j");
System.out.println("loggerA == loggerB : "+(loggerA==loggerB));
}
} |
| Résultat : |
loggerA == loggerB : true |
Le nom de chaque Logger permet de définir une hiérarchie dans les loggers pour permettre de faciliter leur configuration. Cette hiérarchie sur les noms repose sur l'utilisation du caractère point comme pour les packages. Il est dès lors pratique d'utiliser le nom pleinement qualifié de la classe comme nom de logger pour une classe.
Le nom des logger est sensible à la casse.
La hiérarchie commence toujours par un Logger fournit par Log4j : le RootLogger. Pour obtenir une instance de ce logger racine, il faut utiliser la méthode getRootLogger() de la classe Logger.
Le rootLogger a deux caractéristiques distinctives par rapport aux autres loggers :
Lors de la création de l'instance d'un Logger, la hiérarchie est parcourue pour déterminer le Logger le plus proche de la hiérarchie qui est à défaut le rootLogger pour obtenir ces caractéristiques et les reporter sur le nouveau logger.
L'ordre de création des loggers n'a pas d'importance : il n'est pas obligatoire de créer les 1oggers dans leur ordre hiérarchique
Chaque Logger et chaque message possèdent un niveau de gravité. Le Logger compare son niveau de gravité avec celui du message : si le niveau de gravité du message est égal ou supérieur au niveau de gravité du Logger, alors le message est traité par le framework sinon il est ignoré.
Exemple : le message ne sera jamais pris en compte
| Exemple : |
Logger logger = Logger.getLogger("com.jmdoudoux.test.log4j");
logger.setLevel(Level.INFO);
logger.debug("mon message"); |
Chaque logger est associé à un niveau de gravité soit directement soit indirectement par héritage du niveau de gravité de son père dans la hiérarchie. Si le logger ne possède par de niveau de gravité explicite alors c'est celui de son ancêtre le plus proche dans la hiérarchie des loggers.
Comme le logger racine à un niveau de gravité par défaut, cela implique qu'un logger à toujours un niveau de gravité qui lui est associé.
Si aucun logger ne possède de niveau de gravité explicite dans la hiérarchie alors le niveau du logger racine (rootLogger) qui est utilisé. Le rootLogger est toujours définit avec un niveau de gravité qui par défaut est debug.
Il est possible de configurer un logger par programmation.
Il est possible d'associer de façon statique un niveau de gravité au logger en utilisant la méthode setLevel(). Il est cependant préférable d'utiliser la configuration dynamique en utilisant un fichier de configuration qui permet de modifier les paramètres sans modifier le code source.
La migration de l'utilisation des classes Category vers Logger et Priority vers Level peut généralement être fait grâce à un rechercher/remplacer dans le code source :
|
Rechercher |
Remplacer par |
|
Category.getInstance |
Logger.getLogger |
|
Category.getRoot |
Logger.getRootLogger |
|
Category |
Logger |
|
Priority |
Level |
La cible de destination de messages est encapsulée dans un objet de type Appender.
L'interface org.apache.log4j.Appender désigne un flux qui représente la log et se charge de l'envoi de messages formatés dans le flux. Le formatage proprement dit est réalisé par un objet de type Layout. Ce layout peut être fourni dans le constructeur adapté ou par la méthode setLayout().
Une Category ou un Logger peut avoir plusieurs appenders. Si la Category ou le Logger décide de traiter la demande d'un message, le message est envoyé à chacun des appenders. Pour ajouter manuellement un appender à une Category, il suffit d'utiliser la méthode addAppender() qui attend en paramètre un objet de type Appender.
L'interface Appender est directement implémentée par la classe abstraite AppenderSkeleton.
Cette classe est la classe mère de toutes les classes fournies avec Log4j pour représenter un type de log. Log4J propose plusieurs appenders en standard :
Pour créer un appender par programmation, il suffit d'instancier un objet d'une de ces classes.
Chaque appender possède des paramètres de configuration dédiés.
Comme un Logger peut avoir plusieurs appenders, un même message peut être envoyé vers plusieurs appenders selon la configuration. La méthode addAppender() de la classe Logger permet d'ajouter manuellement un appender au logger.
Comme pour les niveaux de gravité, les appenders d'une catégorie ou d'un logger sont hérités implicitement par défaut de la hiérarchie des loggers.
Il est possible d'inhiber cet héritage pour une partie de la hiérarchie en utilisant la méthode setAdditivity() avec le paramètre false sur l'instance du logger concerné. Ce logger et sa hiérarchie descendante n'hériteront pas des caractéristiques de leur parent.
| Exemple : |
package com.jmdoudoux.test.log4j;
import java.io.IOException;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;
import org.apache.log4j.xml.XMLLayout;
public class TestLog4j10 {
public static void main(
String[] args) {
Logger logRoot = Logger.getRootLogger();
ConsoleAppender ca = new ConsoleAppender();
ca.setName("console");
ca.setLayout(new SimpleLayout());
ca.activateOptions();
logRoot.addAppender(ca);
logRoot.setLevel(Level.DEBUG);
logRoot.debug("message 1");
Logger log = Logger.getLogger(TestLog4j10.class);
log.setAdditivity(false);
try {
FileAppender fa = new FileAppender(new XMLLayout(), "c:/log.txt");
fa.setName("FichierLog");
log.addAppender(fa);
} catch (IOException e) {
e.printStackTrace();
}
log.debug("message 2");
Logger logTest = Logger.getLogger("com.jmdoudoux.test.log4j");
logTest.debug("message 3");
}
} |
| Résultat dans la console : |
DEBUG - message 1 DEBUG - message 3 |
| Résultat dans le fichier de log : |
<log4j:event logger="com.jmdoudoux.test.log4j.TestLog4j10" timestamp="1231923298709"
level="DEBUG" thread="main">
<log4j:message><![CDATA[message 2]]></log4j:message>
</log4j:event>
|
La plupart des appenders nécessitent un appel à leur méthode activateOptions() lorsqu'ils sont configurés par programmation avant qu'ils ne puissent être utilisés.
Il est possible de définir son propre appender en définissant une classe qui implémente l'interface Appender ou qui hérite de la classe AppenderSkeleton.
La classe org.apache.log4j.AsyncAppender envoie les messages vers différents appenders de façon périodique et asynchrone. Cet appender utilise son propre thread.
Cet appender n'est configurable que dans un fichier de configuration au format XML.
Un tag fils <appender-ref> permet de préciser un appender vers lequel les messages seront envoyés. L'attribut ref permet de préciser le nom de l'appender concerné.
L'attribut bufferSize permet de préciser le nombre de messages qui seront stockés dans le tampon.
| Exemple : |
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="false"
xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.SimpleLayout" />
</appender>
<appender class="org.apache.log4j.FileAppender" name="file">
<param name="file" value="c:/monapp.log" />
<layout class="org.apache.log4j.SimpleLayout" />
</appender>
<appender class="org.apache.log4j.AsyncAppender" name="async">
<param name="bufferSize" value="2" />
<appender-ref ref="file" />
<appender-ref ref="console" />
</appender>
<root>
<level value="info" />
<appender-ref ref="async" />
</root>
</log4j:configuration>
|
La classe org.apache.log4j.jdbc.JDBCAppender envoie les messages dans une base de données.
Cet appender possède plusieurs propriétés notamment pour préciser les paramètres de connexion à la base de données.
|
Nom |
Rôle |
|
BufferSize |
Nombre de messages stockés dans le tampon avant l'insertion dans la base de données |
|
Driver |
Pilote JDBC pour l'accès à la base de données |
|
Url |
Url de connexion à la base de données |
|
Password |
mot de passe de connexion |
|
User |
utilisateur de connexion |
|
Sql |
requête SQL pour insérer une occurrence dans la base de données |
La propriété Sql permet de définir la requête SQL qui permet l'insertion des informations sur le message dans la base de données. La requête doit utiliser les séquences utilisées par le layout PatternLayout
| Exemple : |
INSERT INTO log(dthr, niveau, message) VALUES('%d', '%p', '%m');". |
Attention : l'utilisation de cet appender fourni par Log4J n'est pas recommandée. Pour plus d'informations consultez la documentation de l'API.
La classe org.apache log4j.net.JMSAppender envoie les message vers une destination JMS.
La classe org.apache.log4j.lf5.LF5Appender envoie les messages sur une application Swing dédiée.
La classe org.apache.log4j.nt.NTEventLogAppender envoie les messages dans la log des événements système sur Windows à partir de Windows NT
La classe org.apache.log4j.varia.NullAppender ignore les messages qui lui sont envoyés.
La seule propriété d'un NullAppender est :
|
Nom |
Rôle |
|
Threshold |
Limiter les messages pris en compte par l'appender à ceux dont le niveau de gravité est supérieur ou égal à celui de threshold. Ceci vient en plus du niveau de gravité associé au logger. Héritée de AppenderSkeleton |
La classe org.apache.log4j.net.SMTPAppender envoie les messages par mail.
La classe SMTPAppender possède plusieurs attributs :
|
Nom |
Rôle |
|
BufferSize |
Nombre de message inclus dans un mail |
|
SMTPHost |
nom de la machine qui héberge le serveur SMTP |
|
From |
email de l'émetteur du mail |
|
To |
email du ou des destinataires du mail |
|
Subject |
sujet du mail |
|
Cc |
email du ou des destinataires en copie du mail |
|
Bcc |
email du ou des destinataires en copie caché du mail |
|
SMTPPassword |
mot de passe |
|
SMTPUsername |
utilisateur |
Par défaut, seuls les messages avec un niveau de gravité supérieur ou égal à ERROR sont traités par cet appender.
Cet appender requiert les bibliothèques JavaBeans Activation Framework et JavaMail pour fonctionner.
La classe org.apache.log4j.net.SocketAppender envoie les messages dans une socket utilisant TCP/IP.
Les données envoyées sont des objets de type LoggingEvent sérialisés.
La classe SocketAppender possède plusieurs attributs :
|
Nom |
Rôle |
|
LocationInfo |
Booléen qui précise si des informations de localisation sont envoyées. Par défaut la valeur est false. |
|
Port |
port de la machine hôte à utiliser. |
|
RemoteHost |
chaîne de caractères qui précise la machine hôte |
La méthode activateOptions() permet de réaliser la connexion.
| Exemple : |
package com.jmdoudoux.test.log4j;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;
import org.apache.log4j.net.SocketAppender;
public class TestLog4j18 {
static Logger logger = Logger.getLogger(TestLog4j18.class);
public static void main(
String args[]) {
SocketAppender appender = null;
try {
appender = new SocketAppender();
appender.setPort(10256);
appender.setRemoteHost("localhost");
appender.setLocationInfo(true);
appender.setLayout(new SimpleLayout());
appender.activateOptions();
} catch (Exception e) {
e.printStackTrace();
}
logger.addAppender(appender);
while (true) {
System.out.println("envoie log");
logger.debug("msg de debogage");
logger.info("msg d'information");
logger.warn("msg d'avertissement");
logger.error("msg d'erreur");
logger.fatal("msg d'erreur fatale");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} |
La configuration dans le fichier properties est similaire aux autres appenders.
| Exemple : |
log4j.appender.socket=org.apache.log4j.net.SocketAppender log4j.appender.socket.RemoteHost=localhost log4j.appender.socket.Port=10256 log4j.appender.socket.LocationInfo=true |
La configuration dans le fichier XML est similaire aux autres appenders.
| Exemple : |
...
<appender name="socket" class="org.apache.log4j.net.SocketAppender">
<param name="Port" value="10256"/>
<param name="RemoteHost" value="localhost"/>
<param name="LocationInfo" value="true"/>
</appender>
...
|
La classe org.apache.log4j.net.SocketHubAppender envoie les messages dans plusieurs sockets.
La classe org.apache.log4j.net.SyslogAppender envoie les messages dans le démon syslog d'un système Unix
La classe org.apache.log4j.net.TelnetAppender envoie les messages dans une socket en lecture seule facilement consultable avec l'outil telnet.
La classe org.apache.log4j.WriterAppender possède deux classes filles : ConsoleAppender et FileAppender. La classe FileAppender possède deux classes filles : DailyRollingAppender et RollingFileAppender.
Elle possède plusieurs propriétés dont :
|
Nom |
Rôle |
Valeur par défaut |
|
Encoding |
Préciser le jeu de caractères à utiliser. |
null |
|
ImmediateFlush |
Préciser si le tampon doit être vidé à chaque opération (pas de mise dans un tampon). |
true |
| Exemple : |
package com.jmdoudoux.test.log4j;
import java.io.FileOutputStream;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.WriterAppender;
import org.apache.log4j.xml.XMLLayout;
public class TestLog4j11 {
public static void main(
String[] args) {
Logger logRoot = Logger.getRootLogger();
WriterAppender appender = null;
try {
appender = new WriterAppender(new XMLLayout(), new FileOutputStream("c:/malog.txt"));
} catch (Exception e) {
e.printStackTrace();
}
logRoot.addAppender(appender);
logRoot.setLevel(Level.DEBUG);
logRoot.debug("mon message");
}
} |
| Résultat : le contenu du fichier c:\malog.txt |
<log4j:event logger="root" timestamp="1219683683344" level="DEBUG" thread="main"> <log4j:message><![CDATA[mon message]]></log4j:message> </log4j:event> |
La classe org.apache.log4j.ConsoleAppender envoie les messages sur la console : soit sur la sortie standard (System.out) par défaut soit vers la sortie d'erreurs (System.err).
Les propriétés d'un ConsoleAppender sont :
|
Nom |
Rôle |
Valeur par défaut |
|
Encoding |
Préciser le jeu de caractères à utiliser. Héritée de WriterAppender |
null |
|
ImmediateFlush |
Envoyer les messages immédiatement vers la console (pas de mise dans un tampon). Héritée de WriterAppender |
true |
|
Target |
System.out ou System.err |
System.out |
|
Threshold |
Limiter les messages pris en compte par l'appender à ceux dont le niveau de gravité est supérieur ou égal à celui de threshold. Ceci vient en plus du niveau de gravité associé au logger. Héritée de AppenderSkeleton |
| Exemple : |
package com.jmdoudoux.test.log4j;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;
public class TestLog4j12 {
public static void main(
String[] args) {
Logger logRoot = Logger.getRootLogger();
ConsoleAppender ca = new ConsoleAppender();
ca.setName("console");
ca.setLayout(new SimpleLayout());
ca.activateOptions();
logRoot.addAppender(ca);
logRoot.setLevel(Level.DEBUG);
logRoot.info("mon message");
}
} |
| Résultat : |
2008-06-15 10:22:02,925 [INFO ] (TestLog4j12.java:main:20) mon message INFO - mon message |
La classe org.apache.log4j.FileAppender envoie les messages dans un fichier.
Les propriétés d'un FileAppender sont :
|
Nom |
Rôle |
Valeur par défaut |
|
ImmediateFlush |
Envoyer les messages immédiatement vers la console (pas de mise dans un tampon). Héritée de WriterAppender |
true |
|
Threshold |
Limiter les messages pris en compte par l'appender à ceux dont le niveau de gravité est supérieur ou égal à celui de threshold. Ceci vient en plus du niveau de gravité associé au logger. Héritée de AppenderSkeleton |
|
|
Append |
Ajouter le message à la fin du fichier ou remplacer le contenu du fichier |
True |
|
Encoding |
Jeu de caractères utilisé pour l'encodage |
|
|
BufferedIO |
Préciser si un tampon doit être utilisé |
False |
|
BufferSize |
Préciser la taille du tampon s'il est utilisé |
|
|
File |
Nom du fichier |
| Exemple : |
package com.jmdoudoux.test.log4j;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;
public class TestLog4j13 {
public static void main(
String[] args) {
Logger logRoot = Logger.getRootLogger();
FileAppender appender = null;
try {
appender = new FileAppender();
appender.setLayout(new SimpleLayout());
appender.setFile("c:/app_log.txt");
appender.activateOptions();
logRoot.addAppender(appender);
logRoot.setLevel(Level.DEBUG);
logRoot.info("mon message");
} catch (Exception e) {
e.printStackTrace();
}
}
} |
La classe org.apache.log4j.DailyRollingFileAppender envoie les messages dans un fichier à rotation périodique (qui n'est pas obligatoirement journalière).
Les propriétés d'un DailyRollingFileAppender sont :
|
Nom |
Rôle |
Valeur par défaut |
|
ImmediateFlush |
Envoyer les messages immédiatement vers le fichier (pas de mise dans un tampon). Héritée de WriterAppender |
true |
|
Threshold |
Limiter les messages pris en compte par l'appender à ceux dont le niveau de gravité est supérieur ou égal à celui de threshold. Ceci vient en plus du niveau de gravité associé au logger. Héritée de AppenderSkeleton |
|
|
Append |
Ajouter le message à la fin du fichier ou remplacer le contenu du fichier. Héritée de FileAppender |
True |
|
Encoding |
Préciser le jeu de caractères utilisé pour l'encodage. Héritée de WriterAppender |
|
|
BufferedIO |
Préciser si un tampon doit être utilisé. Héritée de FileAppender |
False |
|
BufferSize |
Préciser la taille du tampon s'il est utilisé. Héritée de FileAppender |
|
|
File |
Nom du fichier. Héritée de FileAppender |
|
|
DatePattern |
Définir la périodicité de rotation et le suffixe des noms des fichiers créés à chaque rotation |
La valeur de la propriété DatePattern suit le format utilisé par la classe SimpleDateFormat.
Exemple :
'.'yyyy-MM: rotation chaque mois
'.'yyyy-ww: rotation chaque semaine
'.'yyyy-MM-dd: rotation chaque jour à minuit
'.'yyyy-MM-dd-a: rotation chaque jour à midi et à minuit
'.'yyyy-MM-dd-HH: rotation chaque heure
| Exemple : |
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="false"
xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="LoggerFile"
class="org.apache.log4j.DailyRollingFileAppender">
<param name="File"
value="c:/monapp.log" />
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-8p [%t]:%C - %m%n" />
</layout>
</appender>
<root>
<level value="info" />
<appender-ref ref="LoggerFile" />
</root>
</log4j:configuration>
|
La classe org.apache.log4j.DailyRollingFileAppender envoie les messages dans un fichier à rotation selon sa taille.
Le fichier est créé et rempli avec les différents messages. Une fois que la taille du fichier à atteint celle précisée, le fichier est renommé avec le suffixe .1 et le fichier est recréé. Une fois qu'il est de nouveau rempli, le fichier avec le suffixe .1 est renommé avec .2, le fichier est renommé avec le suffixe .1 et un nouveau fichier est créé.
Si le fichier le plus ancien possède un suffixe supérieur à celui précisé, alors il est supprimé.

Les propriétés d'un RollingFileAppender sont :
|
Nom |
Rôle |
Valeur par défaut |
|
ImmediateFlush |
Envoyer les messages immédiatement vers le fichier (pas de mise dans un tampon). Héritée de WriterAppender |
true |
|
Threshold |
Limiter les messages pris en compte par l'appender à ceux dont le niveau de gravité est supérieur ou égal à celui de threshold. Ceci vient en plus du niveau de gravité associé au logger. Héritée de AppenderSkeleton |
|
|
Append |
Ajouter le message à la fin du fichier ou remplacer le contenu du fichier. Héritée de FileAppender |
True |
|
Encoding |
Préciser le jeu de caractères utilisé pour l'encodage. Héritée de WriterAppender |
|
|
BufferedIO |
Préciser si un tampon doit être utilisé. Héritée de FileAppender |
False |
|
BufferSize |
Préciser la taille du tampon s'il est utilisé. Héritée de FileAppender |
|
|
File |
Nom du fichier. Héritée de FileAppender |
|
|
MaxFileSize |
Taille maximale du fichier avant sa
rotation. La taille peut être fournie en KB, MB ou GB |
|
|
MaxIndexBackup |
Indiquer le nombre de fichier de sauvegarde conservé. Une fois ce nombre dépassé, le dernier fichier de sauvegarde est supprimé |
La classe org.apache.log4j.ExternalyRollingFileAppender envoie les messages dans un fichier à rotation déclenchée par la réception dans une socket de la chaîne de caractères "RollOver" en respectant la casse .
L'appender envoi en retour un accusé de traitement ou d'erreur via la socket.
La classe ExternalyRolledFileAppender possède plusieurs attributs :
|
Nom |
Rôle |
Valeur par défaut |
|
ImmediateFlush |
Envoyer les messages immédiatement vers le fichier (pas de mise dans un tampon). Héritée de WriterAppender |
true |
|
Threshold |
Limiter les messages pris en compte par l'appender à ceux dont le niveau de gravité est supérieur ou égal à celui de threshold. Ceci vient en plus du niveau de gravité associé au logger. Héritée de AppenderSkeleton |
|
|
Append |
Ajouter le message à la fin du fichier ou remplacer le contenu du fichier. Héritée de FileAppender |
True |
|
Encoding |
Préciser le jeu de caractères utilisé pour l'encodage. Héritée de WriterAppender |
|
|
BufferedIO |
Préciser si un tampon doit être utilisé. Héritée de FileAppender |
False |
|
BufferSize |
Préciser la taille du tampon s'il est utilisé. Héritée de FileAppender |
|
|
File |
Nom du fichier. Héritée de FileAppender |
|
|
MaxFileSize |
Taille maximale du fichier avant sa
rotation. La taille peut être fournie en KB, MB ou GB |
|
|
MaxIndexBackup |
Indiquer le nombre de fichier de sauvegarde conservé. Une fois ce nombre dépassé, le dernier fichier de sauvegarde est supprimé |
|
|
Port |
Port d'écoute utilisé par la socket |
Ces composants représentés par la classe org.apache.log4j.Layout permettent de définir le format du message avant leur envoie vers leur cible de destination. Un layout est associé à un Appender lors de son instanciation.
Il existe plusieurs layouts définis par log4j :
Il est possible de créer ses propres layouts en dérivant de la classe Layout.
La classe org.apache.log4j.SimpleLayout format le message de façon basique en incluant
| Exemple : |
package com.jmdoudoux.test.log4j;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;
import org.apache.log4j.FileAppender;
public class TestLog4j8 {
static Logger logger = Logger.getLogger(TestLog4j8.class);
public static void main(
String args[]) {
SimpleLayout layout = new SimpleLayout();
FileAppender appender = null;
try {
appender = new FileAppender(layout, "c:/monapp.log", false);
} catch (Exception e) {
e.printStackTrace();
}
logger.addAppender(appender);
logger.setLevel((Level) Level.DEBUG);
logger.debug("msg de debogage");
logger.info("msg d'information");
logger.warn("msg d'avertissement");
logger.error("msg d'erreur");
logger.fatal("msg d'erreur fatale");
}
} |
| Résultat : le fichier monapp.log |
DEBUG - msg de debogage INFO - msg d'information WARN - msg d'avertissement ERROR - msg d'erreur FATAL - msg d'erreur fatale |
La classe org.apache.log4j.DateLayout format les messages dans un tableau HTML.
|
Propriété |
Rôle |
Valeur par défaut |
|
LocationInfo |
Inclure des informations sur la classe |
False |
|
Title |
Précise le titre de la page web |
Log4j Log Messages |
| Exemple : |
package com.jmdoudoux.test.log4j;
import java.io.*;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.HTMLLayout;
import org.apache.log4j.WriterAppender;
public class TestLog4j16 {
static Logger logger = Logger.getLogger(TestLog4j16.class);
public static void main(
String args[]) {
HTMLLayout layout = new HTMLLayout();
WriterAppender appender = null;
try {
FileOutputStream output = new FileOutputStream("c:/log_monapp.htm");
appender = new WriterAppender(layout, output);
} catch (Exception e) {
e.printStackTrace();
}
logger.addAppender(appender);
logger.setLevel((Level) Level.DEBUG);
logger.debug("msg de debogage");
logger.info("msg d'information");
logger.warn("msg d'avertissement");
logger.error("msg d'erreur");
logger.fatal("msg d'erreur fatale");
}
} |

La classe org.apache.log4j.DateLayout format les messages en XML.
| Exemple : |
<log4j:event logger="com.jmdoudoux.test.log4j.TestLog4j10" timestamp="1219683683344" level="DEBUG" thread="main"> <log4j:message><![CDATA[mon message]]></log4j:message> </log4j:event> |
Le PatternLayout permet de préciser le format du message grâce à un motif dont certaines séquences seront dynamiquement remplacées par leur valeur correspondante à l'exécution.
Les séquences commencent par un caractère % suivi d'une lettre :
|
Motif |
Rôle |
|
%c |
Le nom de la catégorie ou du logger qui a émis le message |
|
%C |
Le nom de la classe qui a émis le message : l'utilisation de ce motif est coûteuse en ressources |
|
%d |
Le timestamp de l'émission du message. Il est possible de fournir un format pour la date/heure en utilisant les motifs de la classe SimpleDateFormat. Exemple : %d{dd MMM yyyy HH:MM:ss } Pour améliorer les performances, il est possible d'utiliser des formateurs de dates en précisant ABSOLUTE, RELATIVE ou ISO8601 Exemple : %d{ABSOLUTE} Sans format précisé, c'est le format défini dans la norme ISO8601 qui est utilisé. |
|
%m |
Le message |
|
%n |
Un retour chariot dépendant de la plate-forme |
|
%p |
Le niveau de gravité du message |
|
%r |
Le nombre de millisecondes écoulées entre le lancement de l'application et l'émission du message |
|
%t |
Le nom du thread |
|
%x |
NDC (Nested Diagnostic Context) du thread. Ceci est particulièrement utile pour les applications de type web. |
|
%% |
Le caractère % |
|
%L |
Le numéro de ligne dans le code émettant le message : l'utilisation de ce motif est coûteuse en ressources |
|
%F |
Le nom du fichier émettant le message : l'utilisation de ce motif est coûteuse en ressources |
|
%M |
Le nom de la méthode émettant le message : l'utilisation de ce motif est coûteuse en ressources |
|
%l |
Des informations sur l'origine du message dans le code source (C'est un raccourci dépendant de la JVM qui correspond généralement à %C.%M(%F:%L)): l'utilisation de ce motif est coûteux en ressources |
Il est possible de préciser le formatage de chaque motif grâce à un alignement et/ou une troncature. Dans le tableau ci-dessous, le caractère # représente une des lettres du tableau ci-dessus, n représente un nombre de caractères.
|
Motif |
Rôle |
|
%# |
Aucun formatage (par défaut) |
|
%n# |
Alignement à droite, des blancs sont ajoutés si la taille du motif est inférieure à n caractères |
|
%-n# |
Alignement à gauche, des blancs sont ajoutés si la taille du motif est inférieure à n caractères |
|
%.n |
Tronque le motif s'il est supérieur à n caractères |
|
%-n.n# |
Alignement à gauche, taille du motif obligatoirement de n caractères (troncature ou complément avec des blancs) |
Le motif par défaut du PatternLayout est %m%n.
Le motif permet donc une grande souplesse dans le formatage du message.
| Exemple : |
package com.jmdoudoux.test.log4j;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.ConsoleAppender;
public class TestLog4j15 {
static Logger logger = Logger.getLogger(TestLog4j15.class);
public static void main(
String args[]) {
StringBuilder motif = new StringBuilder();
motif.append("Date/heure : %d{yyyy-MM-dd HH:mm:ss.SSS} %n");
motif.append("Classe emettrice : %C %n");
motif.append("Localisation : %l %n");
motif.append("Message: %m %n");
motif.append("%n");
PatternLayout layout = new PatternLayout(motif.toString());
ConsoleAppender appender = new ConsoleAppender(layout);
logger.addAppender(appender);
logger.setLevel((Level) Level.DEBUG);
logger.debug("msg de debogage");
logger.info("msg d'information");
logger.warn("msg d'avertissement");
logger.error("msg d'erreur");
logger.fatal("msg d'erreur fatale");
}
} |
| Résultat : |
Date/heure : 2008-08-03 11:26:13.705 Classe emettrice : com.jmdoudoux.test.log4j.TestLog4j15 Localisation : com.jmdoudoux.test.log4j.TestLog4j15.main(TestLog4j15.java:26) Message: msg de debogage Date/heure : 2008-08-03 11:26:13.705 Classe emettrice : com.jmdoudoux.test.log4j.TestLog4j15 Localisation : com.jmdoudoux.test.log4j.TestLog4j15.main(TestLog4j15.java:27) Message: msg d'information Date/heure : 2008-08-03 11:26:13.705 Classe emettrice : com.jmdoudoux.test.log4j.TestLog4j15 Localisation : com.jmdoudoux.test.log4j.TestLog4j15.main(TestLog4j15.java:28) Message: msg d'avertissement Date/heure : 2008-08-03 11:26:13.705 Classe emettrice : com.jmdoudoux.test.log4j.TestLog4j15 Localisation : com.jmdoudoux.test.log4j.TestLog4j15.main(TestLog4j15.java:29) Message: msg d'erreur Date/heure : 2008-08-03 11:26:13.705 Classe emettrice : com.jmdoudoux.test.log4j.TestLog4j15 Localisation : com.jmdoudoux.test.log4j.TestLog4j15.main(TestLog4j15.java:30) Message: msg d'erreur fatale |
Voici un exemple de configuration dans un fichier de configuration XML.
| Exemple : |
...
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-8p [%t]:%C - %m%n" />
</layout>
...
|
| Résultat : |
2008-08-03 09:42:19.342 DEBUG [main]:com.jmdoudoux.test.log4j.TestLog4j1 - msg de debogage 2008-08-03 09:42:19.342 INFO [main]:com.jmdoudoux.test.log4j.TestLog4j1 - msg d'information 2008-08-03 09:42:19.342 WARN [main]:com.jmdoudoux.test.log4j.TestLog4j1 - msg d'avertissement 2008-08-03 09:42:19.342 ERROR [main]:com.jmdoudoux.test.log4j.TestLog4j1 - msg d'erreur 2008-08-03 09:42:19.342 FATAL [main]:com.jmdoudoux.test.log4j.TestLog4j1 - msg d'erreur fatale |
Log4j peut être entièrement configuré directement dans le code de l'application.
Attention : dans ce cas, la configuration d'un appender nécessite généralement l'appel à la méthode activateOptions() de l'instance de l'Appender pour qu'elle soit prise en compte.
Cependant Log4j est généralement mis en oeuvre avec une externalisation de sa configuration pour que ses paramètres ne soient pas codés en dur. Ceci permet notamment de pouvoir modifier ces paramètres sans avoir à recompiler le code de l'application et donc d'offrir plus de souplesse dans l'utilisation de Log4j.
La configuration de Log4j commence par la définition du ou des appenders qui seront utilisés. Il faut ensuite définir le ou les logger en leur associant au besoin un ou plusieurs appenders. Pour que Log4j fonctionne, il faut à minima associer un appender au logger racine.
Log4j propose deux formats pour externaliser sa configuration :
Le format XML est plus verbeux mais il est mieux structuré. De plus, certaines fonctionnalités ne sont configurables que dans ce format. C'est donc le format dont l'utilisation est recommandée.
Dans les fichiers de configuration, la valeur d'une propriété peut être initialisée avec une variable d'environnement de la JVM en utilisant la syntaxe ${nom_propriété}
Dans une application, l'initialisation de Log4j n'a besoin d'être réalisée qu'une seule fois de préférence au lancement de l'application.
La configuration suit la même logique que celle des loggers : il est inutile de définir tous les loggers puisque le principe d'héritage permet automatiquement à un logger d'obtenir les caractéristiques de son ascendant le plus proche pour lequel une configuration particulière a été précisée.
Exemple :
|
Logger |
Niveau de gravité |
Affectation par |
|
rootLogger |
error |
assignation (debug par défaut) |
|
com |
error |
héritage |
|
com.jmdoudoux |
error |
héritage |
|
com.jmdoudoux.test |
info |
assignation |
|
com.jmdoudoux.test.log4j |
info |
héritage |
|
com.jmdoudoux.test.log4j.MaClasse |
debug |
assignation |
Ceci peut permettre de très finement régler le niveau de gravité des différents éléments qui composent une application que ce soit dans les classes de l'application ou d'une bibliothèque tierce.
La configuration au niveau des appenders utilisés suit aussi une logique hiérarchique mais ce n'est pas de l'héritage mais une additivité. Un appender définit dans un logger s'ajoute à ou aux appenders déjà définis dans les loggers de la hiérarchie mère.
Log4j propose par défaut un mécanisme de recherche de sa configuration. Log4j recherche un fichier de configuration dans le classpath car il utilise un classLoader pour cette tâche.
Ce mécanisme de recherche peut être désactivé en positionnant à true la propriété système log4j.defaultInitOverride. Ceci doit être utilisé si le chargement de la configuration est fait manuellement dans l'application.
La propriété système log4j.configuration peut être utilisée pour préciser le nom du fichier de configuration.
Par défaut, Log4j recherche dans le classpath un fichier nommé log4j.xml. Si ce fichier n'est pas trouvé, Log4j recherche un fichier nommé log4j.properties.
Log4j utilise un objet de type org.apache.log4j.spi.Configurator pour charger la configuration.
La propriété log4j.configuratorClass permet de préciser explicitement la classe à utiliser pour charger la configuration.
Par défaut, Log4j utilise un objet de type DomConfigurator pour charger un fichier au format XML sinon c'est un objet de type ProperyConfigurator qui est utilisé pour charger le fichier properties.
Vu le mécanisme par défaut proposé par Log4j, le plus simple est donc de nommer son fichier de configuration log4j.xml ou log4j.properties selon le format de configuration utilisé et de mettre le fichier dans le classpath.
Si le mode de fonctionnement par défaut ne répond pas aux besoins, il est possible de demander explicitement le chargement d'une configuration.
Pour effectuer ce chargement, l'API fournit plusieurs classes qui implémentent l'interface Configurator. La classe BasicConfigurator est la classe mère des classes PropertyConfigurator (pour la configuration via un fichier de propriétés) et DOMConfigurator (pour la configuration via un fichier XML).
La classe org.apache.log4j.BasicConfigurator permet de créer une configuration basique.
Avant la version 1.2 de Log4j, la classe BasicConfigurator permet de configurer la catégorie root avec des valeurs par défaut. L'appel à la méthode configure() ajoute à la catégorie root la priorité DEBUG et un ConsoleAppender vers la sortie standard (System.out) associé à un PatternLayout (TTCC_CONVERSION_PATTERN qui est une constante définie dans la classe PatternLayout).
| Exemple : |
import org.apache.log4j.*;
public class TestBasicConfigurator {
static Category cat = Category.getInstance(TestBasicConfigurator.class.getName()) ;
public static void main(String[] args) {
BasicConfigurator.configure();
cat.info("Mon message");
}
} |
| Résultat : |
0 [main] INFO TestBasicConfigurator - Mon message |
A partir de la version 1.2 de Log4j, la méthode configure instancie une configuration dont le rootLogger utilise un appender de type ConsoleAppender et un motif PatternLayout.TTCC_CONVERSION_PATTERN pour le PatternLayout utilisé par cet appender. Le niveau de gravité associé est DEBUG par défaut.
Exemple avec Log4j 1.2 :
| Exemple : |
package com.jmdoudoux.test.log4j;
import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;
public class TestLog4j14 {
static Logger logger = Logger.getLogger(TestLog4j14.class);
public static void main(
String[] args) {
BasicConfigurator.configure();
logger.info("debut");
System.out.println("traitement");
logger.debug("maValeur");
logger.info("fin");
}
} |
| Résultat : |
0 [main] INFO com.jmdoudoux.test.log4j.TestLog4j14 - debut traitement 0 [main] DEBUG com.jmdoudoux.test.log4j.TestLog4j14 - maValeur 0 [main] INFO com.jmdoudoux.test.log4j.TestLog4j14 - fin |
La classe org.apache.log4j.PorpertyConfigurator lit un fichier de configuration au format properties et instancie la configuration correspondante.
La classe PropertyConfigurator permet de configurer Log4j à partir d'un fichier de propriétés ce qui évite la recompilation de classes pour modifier la configuration. La méthode configure() qui attend en paramètre un nom de fichier permet de charger la configuration.
| Exemple : |
import org.apache.log4j.*;
public class TestLogging6 {
static Category cat = Category.getInstance(TestLogging6.class.getName());
public static void main(String[] args) {
PropertyConfigurator.configure("logging6.properties");
cat.info("Mon message");
}
} |
| Exemple : le fichier loggin6.properties de configuration de Log4j |
# Affecte a la catégorie root la priorité DEBUG et un appender nommé CONSOLE_APP log4j.rootCategory=DEBUG, CONSOLE_APP # le appender CONSOL_APP est associé à la console log4j.appender.CONSOLE_APP=org.apache.log4j.ConsoleAppender # CONSOLE_APP utilise un PatternLayout qui affiche : le nom du thread, la priorité, # le nom de la catégorie et le message log4j.appender.CONSOLE_APP.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE_APP.layout.ConversionPattern= [%t] %p %c - %m%n |
| Résultat : |
C:\>java TestLogging6 [main] INFO TestLogging6 - Mon message |
La classe DOMConfigurator permet de configurer Log4j à partir d'un fichier XML ce qui évite aussi la recompilation de classes pour modifier la configuration. La méthode configure() qui attend un nom de fichier permet de charger la configuration. Cette méthode nécessite un parser XML de type DOM compatible avec l'API JAXP.
Le fichier de configuration au format XML doit respecter la DTD log4j.dtd fourni dans la bibliothèque Log4j.
| Exemple : |
import org.apache.log4j.*;
import org.apache.log4j.xml.*;
public class TestLogging7 {
static Category cat = Category.getInstance(TestLogging7.class.getName());
public static void main(String[] args) {
try {
DOMConfigurator.configure("logging7.xml");
} catch (Exception e) {
e.printStackTrace();
}
cat.info("Mon message");
}
} |
| Exemple : le fichier loggin7.xml de configuration de log4j |
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="CONSOLE_APP" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%t] %p %c - %m%n"/>
</layout>
</appender>
<root>
<priority value ="DEBUG" />
<appender-ref ref="CONSOLE_APP" />
</root>
</log4j:configuration>
|
| Résultat : |
C:\j2sdk1.4.0-rc\bin>java TestLogging7 [main] INFO TestLogging7 - Mon message |
Le fichier de configuration permet :
Il est préférable d'utiliser un fichier de configuration plutôt que de configurer les entités de Log4j dans le code source car cette dernière solution implique une modification du code et une recompilation pour être pris en compte.
Le fichier de configuration permet basiquement de définir le niveau de gravité minimum à traiter, les flux de sorties (Appender) et le format des messages (Layout).
Deux formats de fichier de configuration sont proposés par Log4j :
L'ordre de déclaration des loggers dans le fichier de configuration n'est pas imposé mais il est préférable de conserver un ordre hiérarchique pour en faciliter la lecture et la compréhension.
Généralement le fichier de configuration est lu et utilisé au lancement de l'application.
Chaque appender possède ses propres propriétés de configuration.
Celles-ci sont définies de façons différentes selon le format du fichier de configuration :
La configuration utilisant un fichier properties est historiquement la plus ancienne : de nombreuses applications utilisant Log4j la mettent encore en oeuvre.
Comme pour tous fichiers properties, les lignes qui commencent par un caractère dièse sont des lignes de commentaires et sont donc ignorées.
Plusieurs options de configuration générale peuvent être définies dans le fichier de configuration :
|
Options |
Description |
|
log4j.debug |
Booléen qui précise si Log4j doit fournir des informations de debogage sur ses activités de chargement du fichier de configuration et de configuration. La valeur par défaut est false. |
|
log4j.disable |
Précise le niveau de gravité minimum des messages pour
être traités par tous les loggers/categorys. |
|
log4j.disableOverride |
Doit être positionnée à true pour utiliser l'option log4j.disable. La valeur par défaut est false. |
La clé log4j.threshold permet de préciser un niveau minimum de gravité pour tous les loggers ou categorys définis indépendamment du niveau spécifié pour chacun d'eux.
Remarque : l'ordre de déclaration des clés dans le fichier n'a pas d'importance pour la bonne mise en oeuvre de Log4j mais il est cependant recommandé d'utiliser un ordre logique pour faciliter la compréhension du paramétrage.
Une category est définie en utilisant une clé de la forme
log4j.category.nom_category
Un logger est défini en utilisant une clé de la forme
log4j.logger.nom_logger
La category racine est configurée en utilisant une clé de la forme
log4j.categoryLogger
Le logger racine est configuré en utilisant une clé de la forme
log4j.rootLogger
La valeur de ces clés est de la forme niveau_gravité, nom_appender1, nom_appender2, ...
| Exemple : |
# le niveau de gravité debug est associe à la catégorie racine avec deux # appenders nommés A1 et A2 log4j.rootCategory=DEBUG, A1, A2 |
| Exemple : |
# le niveau de gravité debug est associe au logger racine avec deux # appenders nommés A1 et A2 log4j.rootLogger=DEBUG, A1, A2 |
Remarque : chaque appender doit avoir un nom unique
Le niveau de gravité est optionnel mais dans le cas ou il n'est pas fourni, il est impératif de laisser la virgule entre le signe = et le nom du premier appender.
| Exemple : |
# le niveau de gravité debug (par défaut) est associe au logger racine # avec un appender nommé A1 log4j.rootLogger=, A1 |
Un appender est défini en utilisant une clé de la forme
log4j.appender.nom_appender
La valeur de cette clé est le nom pleinement qualifié de la classe qui encapsule l'appender
| Exemple : |
# l'appender nommé A1 est de type ConsoleAppender
log4j.appender.A1=org.apache.log4j.ConsoleAppender
# l'appender nommé A2 est de type RollingFileAppender
log4j.appender.A2=org.apache.log4j.RollingFileAppender |
Le layout d'un appender est précisé en utilisant une clé de la forme :
log4j.appender.nom_appender.layout
La valeur de cette clé est le nom pleinement qualifié de la classe qui encapsule le layout.
| Exemple : |
log4j.appender.A1.layout=org.apache.log4j.PatternLayout |
Une propriété d'un layout est précisée en utilisant une clé de la forme :
log4j.appender.nom_appender.layout.nom_propriété
La valeur sera fournie à la propriété correspondante par introspection.
| Exemple : |
log4j.appender.A1.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%-5p] %c- %m%n
log4j.appender.A2.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%-5p] %c- %m%n
log4j.appender.A2.File=monapp.log
log4j.appender.A2.MaxFileSize=1024KB
log4j.appender.A2.MaxBackupIndex=2 |
Pour modifier le niveau de gravité pris en compte par un logger il faut utiliser une clé de la forme log4j.logger.nom_logger. La valeur de cette clé doit être le niveau de gravité minimum qui sera traité par le logger.
| Exemple : |
log4j.logger.com.jmdoudoux.test=INFO |
Il est possible de supprimer l'additivité des appenders d'un logger en utilisant une clé de la forme log4j.additivity.nom_logger. La valeur est un booléen qui précise l'additivité des appenders (la valeur par défaut est true, il faut mettre false pour la supprimer).
| Exemple : |
log4j.additivity.com.jmdoudoux.test=false |
Il est possible de fournir comme valeur d'une clé la valeur d'une propriété système définie dans la JVM. Pour obtenir la valeur d'une de ces propriétés, il suffit d'utiliser la syntaxe ${nom_de_la_propriete}
La structure des données contenues dans le fichier XML est organisée en plusieurs parties définies dans la DTD log4j.dtd et comprend :
| Exemple : |
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="false"
xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-8p [%t]:%C - %m%n" />
</layout>
</appender>
<root>
<appender-ref ref="console" />
</root>
</log4j:configuration>
|
Si le fichier n'est pas valide alors une exception est levée durant sa lecture et son traitement.
| Exemple : |
log4j:WARN Fatal parsing error 12 and column 5 log4j:WARN The element type "param" must be terminated by the matching end-tag "</param>". log4j:ERROR Could not parse url [file:/C:/Documents%20and%20Settings/jmd/workspace/TestLog4j/bin/log4j.xml]. org.xml.sax.SAXParseException: The element type "param" must be terminated by the matching end-tag "</param>". |
Les différents éléments qui composent le fichier de configuration sont détaillés dans les sections suivantes.
Le fichier de configuration commence par un prologue et une déclaration de la DTD.
La structure du document xml qui va contenir la configuration de Log4j est définie dans la DTD log4j.dtd.
Cette DTD est fournie dans la bibliothèque log4j.jar dans le package org.apache.log4j.xml
| Exemple : |
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> |
La DTD défini l'élément racine comme suit :
| Exemple : |
<!ELEMENT log4j:configuration (renderer*, appender*,plugin*, (category|logger)*,root?,
(categoryFactory|loggerFactory)?)>
|
L'élément racine est le tag <configuration> associé à l'espace de nommage log4j.
| Exemple : |
<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'> </log4j:configuration> |
Le tag <configuration> peut avoir
La définition des éléments doit impérativement respecter cet ordre défini dans la DTD.
La DTD définit trois attributs pour le tag <configuration>.
| Exemple : |
<!ATTLIST log4j:configuration xmlns:log4j CDATA #FIXED "http://jakarta.apache.org/log4j/" threshold (all|trace|debug|info|warn|error|fatal|off|null) "null" debug (true|false|null) "null" reset (true|false) "false"> |
Le tag racine est le tag <configuration> qui possède trois attributs :
L'attribut debug est particulièrement utile pour comprendre l'utilisation du fichier de configuration et résoudre d'éventuel problème dans son contenu.
La configuration d'un appender se fait en utilisant un tag <appender>.
La DTD défini l'élément appender comme suit :
| Exemple : |
<!ELEMENT appender (errorHandler?, param*,
rollingPolicy?, triggeringPolicy?, connectionSource?,
layout?, filter*, appender-ref*)>
<!ATTLIST appender
name CDATA #REQUIRED
class CDATA #REQUIRED
>
|
Le tag <appender> possède deux attributs obligatoires :
Le tag fils facultatif <param> permet de fournir un paramètre à l'appender. Chaque appender possède ses propres paramètres. Le tag <param> permet de fournir des valeurs aux propriétés de l'appender dont le nom correspond à l'attribut name et la valeur à l'attribut value.
| Exemple : |
<appender name="console" class="org.apache.log4j.ConsoleAppender"> <param name="Target" value="System.out"/> </appender> |
Le tag facultatif layout permet de préciser le layout associé à l'appender. Le tag <layout> possède l'attribut obligatoire class qui précise le nom pleinement qualifié de la classe qui encapsule le layout.
| Exemple : |
<appender name="console" class="org.apache.log4j.ConsoleAppender"> <layout class="org.apache.log4j.SimpleLayout"/> </appender> |
Des paramètres peuvent aussi être fournis au layout en utilisant un ou plusieurs tags fils <param>. Chaque layout possède ses propres propriétés.
| Exemple : |
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="=%d{yyyy-MM-dd HH:mm:ss} [%-5p] %c- %m%n" />
</layout>
</appender>
|
La configuration d'un logger se fait en utilisant un tag <logger>.
La DTD défini l'élément logger comme suit :
| Exemple : |
<!ELEMENT logger (level?,appender-ref*)> <!ATTLIST logger name CDATA #REQUIRED additivity (true|false) "true" > |
Le tag <logger> possède un attribut obligatoire :
Le tag <logger> possède un attribut facultatif :
Le tag logger peut avoir deux types de tag fils : <level> et <appender-ref>
Le tag facultatif level permet de préciser le niveau de gravité associé au logger. L'attribut value permet de préciser ce niveau de gravité.
| Exemple : |
<logger name="com.jmdoudoux.test.monapp"> <level value="info"/> </logger> |
Le tag <appender-ref> permet d'associer un nouvel appender au logger en plus de ceux associés par additivité des loggers de hiérarchie supérieure. L'attribut ref permet de préciser le nom de l'appender concerné. La valeur de cet attribut doit correspondre à une valeur d'un attribut name d'un appender défini dans la configuration.
Un tag <logger> peut avoir aucun, un ou plusieurs tags <appender-ref> puisqu'un logger peut avoir plusieurs appenders.
| Exemple : |
<logger name="com.jmdoudoux.test.monapp"> <appender-ref ref="console" /> <appender-ref ref="journal" /> </logger> |
La configuration du logger racine se fait en utilisant un tag <root>.
La DTD défini l'élément logger comme suit :
| Exemple : |
<!ELEMENT root (param*, (priority|level)?, appender-ref*)> |
Sa configuration est similaire à celle des loggers sauf que le tag <root> ne possède pas d'attribut.
| Exemple : |
<root> <priority value ="info" /> <appender-ref ref="console"/> </root> |
La propriété threshold permet de définir un seuil minimum de niveau de gravité des messages traités par l'appender.
| Exemple : |
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="false"
xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="threshold" value="ERROR" />
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-8p [%t]:%C - %m%n" />
</layout>
</appender>
<root>
<appender-ref ref="console" />
</root>
</log4j:configuration>
|
| Résultat : |
2008-08-03 10:03:11.895 ERROR [main]:com.jmdoudoux.test.log4j.TestLog4j1 - msg d'erreur 2008-08-03 10:03:11.910 FATAL [main]:com.jmdoudoux.test.log4j.TestLog4j1 - msg d'erreur fatale |
Log4j propose un autre mécanisme plus puissant pour filtrer les messages traités par un appender : les filtres.
Log4j propose plusieurs filtres en standard :
Les filtres ne peuvent être utilisés que dans une configuration via un fichier xml.
Le filtre LevelMatchFilter possède plusieurs paramètres :
|
Paramètres |
Rôle |
|
levelToMatch |
Précise le niveau de gravité du message pour qu'il soit traité |
|
acceptOnMatch |
Booléen qui indique si le message est traité (true) ou rejeté (false) par le filtre |
Le filtre LevelMatchFilter traite les messages qui correspondent au filtre mais laisse passer ceux qui ne correspondent pas. Ainsi pour ignorer ces messages, il est nécessaire d'appliquer en plus un filtre de type DenyAllFilter.
| Exemple : |
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="false"
xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-8p [%t]:%C - %m%n" />
</layout>
<filter class="org.apache.log4j.varia.LevelMatchFilter">
<param name="levelToMatch" value="ERROR" />
</filter>
<filter class="org.apache.log4j.varia.DenyAllFilter"/>
</appender>
<root>
<appender-ref ref="console" />
</root>
</log4j:configuration>
|
| Résultat : |
2008-08-03 10:14:49.203 ERROR [main]:com.jmdoudoux.test.log4j.TestLog4j1 - msg d'erreur |
Si le filtre DenyAllFilter n'est pas utilisé alors tous les messages sont traités.
| Résultat : |
2008-08-03 10:19:28.612 DEBUG [main]:com.jmdoudoux.test.log4j.TestLog4j1 - msg de debogage 2008-08-03 10:19:28.612 INFO [main]:com.jmdoudoux.test.log4j.TestLog4j1 - msg d'information 2008-08-03 10:19:28.612 WARN [main]:com.jmdoudoux.test.log4j.TestLog4j1 - msg d'avertissement 2008-08-03 10:19:28.612 ERROR [main]:com.jmdoudoux.test.log4j.TestLog4j1 - msg d'erreur 2008-08-03 10:19:28.612 FATAL [main]:com.jmdoudoux.test.log4j.TestLog4j1 - msg d'erreur fatale |
Le filtre LevelRangeFilter possède plusieurs paramètres :
|
Paramètres |
Rôle |
|
levelMin |
Précise le niveau de gravité minimal du message pour qu'il soit traité |
|
levelMax |
Précise le niveau de gravité maximal du message pour qu'il soit traité |
|
acceptOnMatch |
true : le message est traité sans appliquer les autres filtres false : si le niveau de gravité est hors de la plage, alors le message est ignoré sinon le message est soumis aux autres filtres |
| Exemple : |
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="false"
xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-8p [%t]:%C - %m%n" />
</layout>
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="levelMin" value="INFO" />
<param name="levelMax" value="ERROR" />
</filter>
</appender>
<root>
<appender-ref ref="console" />
</root>
</log4j:configuration>
|
| Résultat : |
2008-08-03 10:44:46.636 INFO [main]:com.jmdoudoux.test.log4j.TestLog4j1 - msg d'information 2008-08-03 10:44:46.636 WARN [main]:com.jmdoudoux.test.log4j.TestLog4j1 - msg d'avertissement 2008-08-03 10:44:46.636 ERROR [main]:com.jmdoudoux.test.log4j.TestLog4j1 - msg d'erreur |
Avec les filtres, il est par exemple possible de définir un appender qui traite les messages de debogage et un appender qui traite les autres messages.
| Exemple : |
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd" >
<log4j:configuration>
<appender name="fichierLog"
class="org.apache.log4j.RollingFileAppender">
<param name="maxFileSize" value="1024KB" />
<param name="maxBackupIndex" value="2" />
<param name="File" value="c:/monapp.log" />
<param name="threshold" value="info" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-8p [%t]:%C - %m%n" />
</layout>
</appender>
<appender name="fichierDebug"
class="org.apache.log4j.RollingFileAppender">
<param name="maxFileSize" value="1024KB" />
<param name="maxBackupIndex" value="2" />
<param name="File" value="c:/monapp_debug.log" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-8p [%t]:%C - %m%n" />
</layout>
<filter class="org.apache.log4j.varia.LevelMatchFilter">
<param name="levelToMatch" value="DEBUG" />
</filter>
<filter class="org.apache.log4j.varia.DenyAllFilter"/>
</appender>
<root>
<priority value="debug"></priority>
<appender-ref ref="fichierLog" />
<appender-ref ref="fichierDebug" />
</root>
</log4j:configuration>
|
La configuration par fichier properties est moins verbeuse que par fichier XML.
Certaines fonctionnalités ne sont pas supportées par la configuration par properties comme l'utilisation des Filters ou des ErrorHandlers. Certains appenders ne sont configurables que par fichier XML.
La conversion d'un fichier de configuration au format properties en un fichier de configuration au format XML doit se faire manuellement.
Voici un premier exemple simple.
| Exemple : |
log4j.rootLogger=info, console log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.Target=System.out log4j.appender.console.layout=org.apache.log4j.SimpleLayout |
| Exemple : |
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd" >
<log4j:configuration>
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.SimpleLayout" />
</appender>
<root>
<priority value="info" />
<appender-ref ref="console" />
</root>
</log4j:configuration>
|
Le second exemple ci-dessous utilise deux appenders.
| Exemple : |
log4j.rootLogger=debug, console, fichier
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %-8p [%t]:%C - %m%n
log4j.appender.fichier=org.apache.log4j.RollingFileAppender
log4j.appender.fichier.File=c:/monapp.log
log4j.appender.fichier.MaxFileSize=1024KB
log4j.appender.fichier.MaxBackupIndex=2
log4j.appender.fichier.layout=org.apache.log4j.PatternLayout
log4j.appender.fichier.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %-8p [%t]:%C - %m%n |
| Exemple : |
<?xml version="1.0" encoding="UTF-8" ?>
!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-8p [%t]:%C - %m%n" />
</layout>
</appender>
<appender name="fichier"
class="org.apache.log4j.RollingFileAppender">
<param name="file" value="c:/monapp.log" />
<param name="MaxFileSize" value="1024KB" />
<param name="MaxBackupIndex" value="2" /></
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-8p [%t]:%C - %m%n" />
</layout>
</appender>
<root>
<priority value="debug" />
<appender-ref ref="console" />
<appender-ref ref="fichier" />
</root>
</log4j:configuration></pre> |
Cette section présente quelques fonctionnalités avancées de Log4j.
La consultation des logs peut ne pas être facile si elle doit être réalisée en temps réel ou si le volume de messages est très important.
Si l'application écrit régulièrement dans le fichier, un simple bloc note n'est pas suffisant pour lire les messages arrivés après l'ouverture du fichier.
Sous Unix, la commande tail est particulièrement utile car elle permet de visualiser les n dernières lignes d'un fichier alors que celui-ci est en train de grossir.
Log4j propose en standard une application graphique nommée chainsaw qui permet de visualiser des logs formatées avec un layout XMLLayout ou envoyées par un SocketAppender.
Pour exécuter Chainsaw, il faut exécuter la classe org.apache.log4j.chainsaw.Main
| Exemple : |
C:\>java -cp C:/java/apache-log4j-1.2.15/log4j-1.2.15.jar org.apache.log4j.chainsaw.Main |

Pour consulter un fichier XML qui contient des logs formatées avec un XML Layout, il faut utiliser l'option "Load File" du menu "File".
La partie "Controls" propose plusieurs filtres : il suffit de saisir les caractères recherchés et le filtre est appliqué au fur et à mesure de la saisie.
ChainSaw est aussi très pratique pour consulter les logs envoyées par un SocketAppender.
Il faut définir une variable d'environnement à la JVM nommée chainsaw.port pour préciser le port à écouter.
| Exemple : |
C:\>java -cp C:/java/apache-log4j-1.2.15/log4j-1.2.15.jar -Dchainsaw.port=10256 org.apache.log4j.chainsaw.Main |
Log4j utilise plusieurs variables d'environnement de la JVM pour éventuellement modifier certains comportement.
|
Variable |
Rôle |
|
log4j.debug |
Fournir des informations de débogage lors de la recherche de la configuration et de son chargement |
|
log4j.configuration |
Permet de préciser le nom du fichier properties qui contient la configuration. Ce fichier doit être dans le classpath |
|
log4j.defaultInitOverride |
Booléen qui permet de demander d'inhiber la recherche de la configuration. La valeur par défaut est false. |
La classe Category et par héritage la classe Logger proposent deux surcharges de la méthode l7dlog() qui permettent l'émission de messages internationalisés (l7d est le raccourci de localized).
Avant la première utilisation de la méthode l7dlog, il est nécessaire de préciser quel ResourceBundle doit être utilisé en invoquant la méthode setResourceBundle().
Les méthodes l7dlog() attendent en paramètres le niveau de gravité, la clé du message dans le resourceBundle et un objet de type Throwable. La seconde surcharge attend aussi un tableau d'objets qui seront insérés à leur emplacement définis dans la valeur de la clé.
| Exemple : |
package com.jmdoudoux.test.log4j;
import java.util.Locale;
import java.util.ResourceBundle;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;
public class TestLog4j19 {
static Logger logger = Logger.getLogger(TestLog4j19.class);
public static void main(
String args[]) {
Logger logRoot = Logger.getRootLogger();
ConsoleAppender ca = new ConsoleAppender();
ca.setName("console");
ca.setLayout(new SimpleLayout());
ca.activateOptions();
logRoot.addAppender(ca);
logRoot.setLevel(Level.DEBUG);
Locale locale = new Locale("fr", "FR");
ResourceBundle messages = ResourceBundle.getBundle("MessagesLog", locale);
logger.setResourceBundle(messages);
logger.l7dlog(Level.DEBUG, "MESSAGE", null);
locale = new Locale("en", "EN");
messages = ResourceBundle.getBundle("MessagesLog", locale);
logger.setResourceBundle(messages);
logger.l7dlog(Level.DEBUG, "MESSAGE", null);
}
} |
Il faut définir les fichiers properties qui seront utilisés par le ResourceBundle. Ces fichiers doivent être stockés dans le classpath.
| Exemple : le fichier MessagesLog.properties |
MESSAGE=mon message en français |
| Exemple : le fichier MessagesLog_en.properties |
MESSAGE=my message in english |
| Résultat : |
DEBUG - mon message en français DEBUG - my message in english |
![]() |
|
Cette section sera développée dans une version future de ce document
|
![]() |
|
Cette section sera développée dans une version future de ce document
|
![]() |
|
Cette section sera développée dans une version future de ce document
|
Cette section fournit quelques best practices lors de la mise oeuvre de Log4j.
Le choix du niveau de gravité de chaque message émis est très important.
Voici quelques exemples d'utilisation de chaque niveau de gravité :
Hors de l'environnement de développement, le niveau de gravité minimum des messages doit être info. Le niveau debug n'est à utiliser que dans l'environnement de développement ou à utiliser temporairement pour des besoins spécifiques dans les autres environnements.
Log4j a été développé dans le soucis de réduire au minimum le surcoût de son utilisation.
Cependant le logging a nécessairement un coût et ce coût peut devenir important si certaines précautions ne sont pas prises par le développeur.
Il est nécessaire de limiter le coût d'émission d'un message dont le coût de construction est important surtout si ce dernier sera ignoré par le logger.
| Exemple : |
logger.debug("valeur="+valeur+" , i="+i+" ,next="+next); |
Dans cet exemple, si le niveau de gravité du logger est supérieur à debug, le coût de l'émission du message contiendra aussi la création du message par concaténation des différentes valeurs.
Pour limiter ce coût, il est préférable de conditionner l'émission de message par un test préalable sur le niveau de gravité pris en charge par le logger lors de l'exécution du traitement.
Les classes Category et Logger proposent des méthodes pour effectuer ces tests.
| Exemple : |
if (logger.isDebugEnabled()) {
logger.debug("valeur="+valeur+" , i="+i+" ,next="+next);
} |
Avec ce test, le message n'est construit que s'il est pris en compte par le logger. L'inconvénient de ce test est qu'il est réalisé deux fois : une fois par la méthode isDebugEnabled() et une autre fois par la méthode debug(). Cependant ce surcoût est beaucoup moins important que la création du message.
Les temps de traitement de Log4j sont obligatoirement dépendants de l'utilisation qui en est faite dans l'application notamment :
Lors de l'utilisation d'un layout de type PatternLayout, l'utilisation de certains motifs sont connus pour être gourmand en temps de traitement. Même si les informations de ces motifs sont particulièrement utiles, il faut tenir compte de leur temps de traitement lors de leur utilisation.
Pour économiser de la mémoire, il est préférable de déclarer les loggers en tant que variables statiques.
| Exemple : |
private static Logger logger = Logger.getLogger(MaClasse.class); |
L'usage de fonctionnalités de logging dans les applications est tellement répandu que SUN a décidé de développer sa propre API et de l'intégrer au JDK à partir de la version 1.4.
Cette API a été proposée à la communauté sous la Java Specification Request numéro 047 (JSR-047).
Le but est de proposer un système qui puisse être exploité facilement par toutes les applications.
L'API repose sur plusieurs classes principales et une interface:
Un logger possède un ou plusieurs Handlers qui sont des entités qui vont recevoir les messages. Chaque handler peut avoir un filtre associé en plus du filtre associé au Logger.
Chaque message possède un niveau de sévérité représenté par la classe Level.
Cette classe est un singleton qui propose la méthode getLogManager() pour obtenir l'unique référence sur un objet de ce type.
Cette objet permet :
Pour réaliser ces actions, la classe LogManager possède plusieurs méthodes dont les principales sont :
| Méthode | Rôle |
| void addGlobalHandler(Handler) | Ajoute un handler à la liste des handler globaux |
| Logger getLogger(String) | Permet d'ajouter un nouveau Logger dont le nom fourni en paramètre lui sera attribué si il n'existe pas encore sinon la référence sur le Logger qui possède déjà ce nom est renvoyé. |
| void removeAllGlobalHandlers() | Supprime tous les Handler de la liste de handler globaux |
| void removeGlobalHandler(Handler) | Supprime le Handler de la liste de Handler globaux |
| void setLevel(String, Level) | Permet de préciser le niveau de tout les Logger dont le nom commence par la chaîne fournie en paramètre |
| LogManager getLogManager() | Renvoie l'instance unique de cette classe |
Par défaut, la liste des Loggers contient toujours un Logger nommé global qui peut être facilement utilisé.
La classe Logger est la classe qui se charge d'envoyer les messages dans la log. Un Logger est identifié par un nom qui est habituellement le nom qualifié de la classe dans laquelle le Logger est utilisé. Ce nom permet de gérer une hiérarchie de Logger. Cette gestion est assurée par le LogManager. Cette hiérarchie permet d'appliquer des modifications sur un Logger ainsi qu'à toute sa "descendance".
Il est aussi de possible de créer des Logger anonymes.
La méthode getLogger() de la classe LogManager permet d'instancier un nouvel objet Logger si aucun Logger possédant le nom passé en paramètre a déjà été défini sinon il renvoie l'instance existante.
La classe Logger se charge d'envoyer les messages aux Handlers enregistrés sous la forme d'un objet de type LogRecord. Par défaut, ces Handlers sont ceux enregistrés dans le LogManager. L'envoi des messages est conditionné par la comparaison du niveau de sévérité de message avec celui associé au Logger.
La classe Logger possède de nombreuses méthodes pour générer des messages : plusieurs méthodes sont définies pour chaque niveau de sécurité. Plutôt que d'utiliser la méthode log() en précisant le niveau de sévérité, il est possible d'utiliser la méthode correspondante au niveau de sécurité.
Ces méthodes sont surchargées pour accepter plusieurs paramètres :
Chaque message est associé à un niveau de sévérité représenté par un objet de type Level. Cette classe définit 7 niveaux de sévérité :
![]() |
|
Cette section sera développée dans une version future de ce document
|
Le framework propose plusieurs classes filles qui représentent différents moyens pour émettre les messages :
![]() |
|
Cette section sera développée dans une version future de ce document
|
Le framework propose deux implémentations :
XMLFormatter utilise un DTD particulière. Le tag racine est <log>. Chaque enregistrement est inclus dans un tag <record>.
Un fichier particulier au format Properties permet de préciser des paramètres de configuration pour le système de log tel que le niveau de sévérité géré par un Logger particulier et sa descendance, les paramètres de configuration des Handlers ...
Il est possible de préciser le niveau de sévérité pris en compte par tous les Logger :
.level = niveau
Il est possible de définir les handlers par défaut :
handlers = java.util.logging.FileHandler
Pour préciser d'autre handler, il faut les séparer par des virgules.
Pour préciser le niveau de sévérité d'un Handler, il suffit de le lui préciser :
java.util.logging.FileHandler.level = niveau
Un fichier par défaut est défini avec les autres fichiers de configuration dans le répertoire lib du JRE. Ce fichier ce nomme logging.properties.
Il est possible de préciser un fichier particulier précisant son nom dans la propriété système java.util.logging.config.file
exemple : java -Djava.util.logging.config.file=monLogging.properties
![]() |
|
Cette section sera développée dans une version future de ce document
|
Le projet Jakarta Commons propose un sous projet nommé Logging qui encapsule l'usage de plusieurs systèmes de logging et facilite ainsi leur utilisation dans les applications. Ce n'est pas un autre système de log mais il propose un niveau d'abstraction qui permet sans changer le code d'utiliser indifféremment n'importe lequel des systèmes de logging supportés. Son utilisation est d'autant plus pratique qu'il existe plusieurs système de log dont aucun des plus répandus, Log4j et l'API logging du JDK 1.4, ne sont dominants.
Le grand intérêt de cette bibliothèque est donc de rendre l'utilisation d'un système de log dans le code indépendant de l'implémentation de ce système. JCL encapsule l'utilisation de Log4j, l'API logging du JDK 1.4 et LogKit.
De nombreux projets du groupe Jakarta utilise cette bibliothèque tel que Tomcat ou Struts. La version de JCL utilisée dans cette section est le 1.0.3
Le package, contenu dans le fichier commons-logging-1.0.3.zip peut être téléchargé sur le site http://jakarta.apache.org/commons/logging.html. Il doit ensuite être décompressé dans un répertoire du système d'exploitation.
Pour utiliser la bibliothèque, il faut ajouter le fichier dans le classpath.
L'inconvénient d'utiliser cette bibliothèque est qu'elle n'utilise que le dénominateur commun des système de log qu'elle supporte : ainsi certaines caractéristiques d'un système de log particulier ne pourront être utilisées via cette API .
La bibliothèque propose une fabrique qui renvoie, en fonction du paramètre précisé, un objet qui implémente l'interface Log. La méthode statique getLog() de la classe LogFactory permet d'obtenir cet objet : elle attend en paramètre soit un nom sous la forme d'une chaîne de caractères soit un objet de type Class dont le nom sera utilisé. Si un objet de type log possédant ce nom existe déjà alors c'est cette instance qui est renvoyée par la méthode sinon c'est une nouvelle instance qui est retournée. Ce nom représente la catégorie pour le système log utilisé, si celui-ci supporte une telle fonctionnalité.
Par défaut, la méthode getLog() utilise les règles suivantes pour déterminer le système de log à utiliser :
Il est possible de forcer l'usage d'un système de log particulier en précisant la propriété org.apache.commons.logging.Log à la machine virtuelle.
Pour complètement désactiver le système de log, il suffit de fournir la valeur org.apache.commons.logging.impl.NoOpLog pour la propriété org.apache.commons.logging.Log à la JVM. Attention dans ce cas, plus aucune log ne sera émise.
Il existe plusieurs niveaux de gravité que la bibliothèque tentera de faire correspondre au mieux avec le système de log utilisé.
Il existe d'autres API de logging dont voici une liste non exhaustive :
| Produit | URL |
| Lumberjack | http://javalogging.sourceforge.net/ |
| Javalog | http://sourceforge.net/projects/javalog/ |
| Jlogger de Javelin Software | http://www.javelinsoft.com/jlogger/ |
|
|
|
|
|
|
Développons en Java v 1.60 | ||
| Copyright (C) 1999-2011 Jean-Michel DOUDOUX |