La journalisation - Principes et implementations Java
Jakarta Commons Logging
Jakarta Commons Logging est une API fournie par le projet Apache Jakarta afin d'utiliser la journalisation de façon transparente, sans se préoccuper du système qui la réalisera effectivement. Ainsi, on utilise Commons Logging pour toutes les instructions de journalisation et c'est au moment de l'exécution du programme que l'API déterminera quel est le système à utiliser. Cela permet de ne pas dépendre d'un système, la seule dépendance est vis-à-vis du package Commons Logging qui ne pèse que quelques dizaines de kilo-octets et peut donc être embarqué dans n'importe quelle application. L'API Commons Logging se doit donc d'être compatible avec une grande variété de systèmes de journalisation (Log4J, l'API de journalisation du JDK 1.4, Avalon, etc.). Il ne reprend donc que les caractéristiques communes à tous ces systèmes et n'est donc pas très compliqué à prendre en main.
Utilisation
Jakarta Commons Logging fournit au développeur un outil middleware
qui est une abstration simple et indépendante des autres outils de journalisation.
Cette API founit un système de Factory qui utilise les autres systèmes de journalisation, tels que Log4J, Avalon LogKit, le JDK 1.4, ...
Le développeur n'a à se servir que de deux entités, définies dans le package org.apache.commons.logging : Log et LogFactory.
Comme son nom l'indique, la LogFactory permet de créer des entités Log.
Installation:
Avant tout, il est nécessaire d'ajouter l'API Commons Logging de Jakarta au chemin de compilation.
La librairie est récupérable sur le site de Jakarta
Il existe deux façons d'obtenir un Log de la part de la LogFactory :
Log log = LogFactory.getFactory().getInstance(MaClasse.class);
ou
Log log = LogFactory.getLog(MaClasse.class);
Les deux sont strictement équivalents, la deuxième forme est un raccourci pour la première. Voici un exemple d'utilisation des Jakarta Commons Logging :
package example.commons.logging;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Enregistre des messages à différents niveaux de gravité.
*/
public class CommonsLoggingExample {
public static void main(String[] args) {
Log log = LogFactory.getLog(CommonsLoggingExample.class);
log.trace("Msg de trace");
log.info("Msg d'information");
log.warn("Msg d'avertissement");
log.error("Msg d'erreur");
log.fatal("Msg d'erreur fatale");
}
}
On se contente d'appeler getLog en lui passant la classe en cours, le Log obtenu sera identifié par ce nom. Un Log n'est créé que s'il n'en n'existe pas avec le même identifiant. Il est également d'identifier le Log par une chaîne quelconque mais l'usage veut que l'on utilise le nom de la classe qui effectue l'appel (ou le nom de son package), qui plus est, cela facilite généralement la configuration du système concret.
Au niveau du code, l'utilisation de commons logging ne sera généralement pas plus compliquée que cela. Il faut noter que dans des cas complexes, une autre famille de méthodes peut être utilisée en conjonction avec celles de journalisation : la famille des isxxxEnabled(), où xxx est le nom d'un niveau (isTraceEnabled(), isDebugEnabled, etc.). Cette famille de méthodes renvoie true si le niveau considéré est activé, false sinon. Les instructions de journalisation ressemblent alors à cela :
if(log.isTraceEnabled()) {
log.trace("Entrée dans la méthode xxxx, paramètres : " +
uneInstanceCompliquéeAAfficher + ', ' + UneMethodeTresLongue());
}
Dans le cas où le niveau est journalisable, cela ne fait que dupliquer un test qui est déjà effectué pour savoir si le message est enregistré (le temps perdu est négligeable), mais dans le cas où le niveau ne l'est pas, cela évite tout le processus d'évaluation et de passage des paramètres ce qui permet d'éviter au maximum la perte de temps.
Le LogFactory
Il faut préciser que la configuration doit être faite par l'utilisateur de l'application et non pas par le programmeur. En effet, l'API commons logging sert de couche d'abstraction, si le programmeur la configure, c'est qu'il émet des suppositions sur l'environnement dans lequel va s'exécuter l'application et est donc en contradiction avec ce principe d'abstraction.
La LogFactory utilisée pour obtenir une instance de Log est en fait une classe abstraite, elle va rechercher quelle est l'implémentation à utiliser. LogFactory utilise le processus de recherche suivant :
- Recherche d'un attribut système nommé org.apache.commons.logging.LogFactory (cet attribut est défini en passant l'option -Dorg.apache.commons.logging.LogFactory=classe d'implémentation à la JVM)
- Utilisation du système de recherche de service du JDK 1.3 pour trouver un fichier appelé META-INF/services/org.apache.commons.logging.LogFactory dont la première ligne doit contenir le nom de la LogFactory
- Recherche du fichier
commons-logging.properties
qui doit contenir l'attribut org.apache.commons.logging.LogFactory indiquant la Factory à utiliser - Utilisation de la Factory par défaut (org.apache.commons.logging.impl.LogFactoryImpl)
La recherche s'effectue dans l'ordre présenté ci-dessus. Dès qu'une des conditions est remplie, la recherche s'arrête et c'est la Factory trouvée qui est utilisée.
Dans la plupart des cas, l'implémentation par défaut est suffisante. L'utilisation d'une factory différente peut être justifiée dans des cas particuliers comme l'exécution dans un environnement conteneur (serveur) ou des contraintes de taille (embarqué), il faut alors créer sa propre Factory et renseigner l'une des propriétés citées ci-dessus (utiliser la propriété système accélère le processus).
Le Log
Est présenté ici la configuration de la Factory par défaut (org.apache.commons.logging.impl.LogFactoryImpl). C'est elle qui va déterminer quelle est l'implémentation de Log à utiliser.
Si un fichier de configuration commons-logging.properties
existe, les propriétés qu'il définit sont passées à la Factory.
La Factory par défaut utilise le mécanisme de recherche suivant pour le Log, il est assez similaire à celui de la recherche de Factory :
- Recherche d'une propriété org.apache.commons.logging.Log (et org.apache.commons.logging.log pour compatibilité) indiquant l'implémentation à utiliser
- Recherche au niveau système d'une propriété org.apache.commons.logging.Log (et org.apache.commons.logging.log pour compatibilité)
- Recherche du système de journalisation Log4J
- Recherche du système de journalisation du JDK 1.4
- Utilisation de l'implémentation org.apache.commons.logging.impl.SimpleLog par défaut
On voit donc que là encore le mécanisme de recherche a été conçu de façon à utiliser le meilleur système de journalisation disponible. La configuration de l'emploi d'un Log particulier n'est justifiée que si l'on utilise un système de journalisation qui n'est pas pris en compte dans le processus de recherche. Attention toutefois, si le système de journalisation n'est pas disponible à l'exécution le mécanisme de recherche ne reprend pas et une exception est levée.
Exemple de fichier commons-logging.properties
:
#indique quel LogFactory utiliser
org.apache.commons.logging.LogFactory=org.apache.commons.logging.impl.LogFactoryImpl
#indique quel Log utiliser
#org.apache.commons.logging.log = org.apache.commons.logging.impl.SimpleLog
Le SimpleLog
Il est donc préférable de s'appuyer sur le mécanisme de détection proposé et de configurer Log4J et le SimpleLog, puisque la configuration de l'API du JDK se fait au niveau système.
Ainsi, on est sûr d'avoir un système de journalisation fiable. SimpleLog est configurable via un fichier nommé simplelog.properties
, les propriétés que l'on peut y renseigner sont
les suivantes :
- org.apache.commons.logging.simplelog.defaultlog : indique le niveau de détail par défaut ("trace", "debug", "info", "warn", "error", ou "fatal", prend la valeur "info" par défaut)
- org.apache.commons.logging.simplelog.log.nomlogger : niveau de détail du logger indiqué
- org.apache.commons.logging.simplelog.showlogname : booléen indiquant si le nom du logger doit être affiché dans les messages (désactivé par défaut)
- org.apache.commons.logging.simplelog.showShortLogname : indique si un nom simple doit être affiché pour le logger (actif par défaut)
- org.apache.commons.logging.simplelog.showdatetime : indique si la date et l'heure doivent être affichées (masquées par défaut)
- org.apache.commons.logging.simplelog.dateTimeFormat : donne le format d'affichage de la date et de l'heure (au format SimpleDateFormat, format par défaut "yyyy/MM/dd HH:mm:ss:SSS zzz")
Exemple de fichier de configuration du SimpleLog:
#indique le niveau de détail par défaut ("trace", "debug", "info", "warn", "error", ou "fatal", prend la valeur "info" par défaut)
org.apache.commons.logging.simplelog.defaultlog = trace
#niveau de détail du logger indiqué
org.apache.commons.logging.simplelog.log.Test = warn
#booléen indiquant si le nom du logger doit être affiché dans les messages (désactivé par défaut)
org.apache.commons.logging.simplelog.showlogname = false
#indique si un nom simple doit être affiché pour le logger (actif par défaut)
org.apache.commons.logging.simplelog.showShortLogname = false
#indique si la date et l'heure doivent être affichées (masquées par défaut)
org.apache.commons.logging.simplelog.showdatetime = false
#donne le format d'affichage de la date et de l'heure (au format SimpleDateFormat, format par défaut "yyyy/MM/dd HH:mm:ss:SSS zzz")
org.apache.commons.logging.simplelog.dateTimeFormat = yyyy/MM/dd HH:mm:ss:SSS