Fichiers LOG
Logging avec SLF4J et Logback
Notions théoriques
Pourquoi ne pas utiliser System.out.println en production ?
System.out.println() convient pour apprendre et déboguer rapidement. En revanche, dans une application professionnelle :
- Il n'est pas possible de filtrer les messages (tout s'affiche ou rien)
- Il n'y a pas de niveau de gravité (information, avertissement, erreur)
- Il n'y a pas d'horodatage automatique
- Il est difficile de rediriger la sortie vers un fichier de log
- Il ne peut pas être désactivé facilement en production
Le logging résout tous ces problèmes.
SLF4J et Logback
SLF4J (Simple Logging Facade for Java) est une façade : une interface standard qui permet d'utiliser n'importe quelle bibliothèque de logging derrière (Logback, Log4j, etc.) sans changer votre code.
Logback est l'implémentation de logging la plus utilisée avec SLF4J. C'est le successeur de Log4j, conçu par le même auteur.
Dépendances Maven à ajouter :
<dependencies>
<!-- SLF4J API (façade) -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
<!-- Logback (implémentation) -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.14</version>
</dependency>
</dependencies>
slf4j-api fournit les interfaces (Logger, LoggerFactory). logback-classic est l'implémentation concrète qui écrit les logs. Si vous voulez changer d'implémentation plus tard, vous changez seulement logback-classic — votre code reste identique.
Créer et utiliser un Logger
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MaClasse {
// Convention : une constante statique private final par classe
private static final Logger logger = LoggerFactory.getLogger(MaClasse.class);
public static void main(String[] args) {
logger.debug("Message de débogage (détails internes)");
logger.info("Application démarrée");
logger.warn("Attention, mémoire basse : 15%");
logger.error("Erreur critique : connexion perdue");
}
}
Les niveaux de log
Du moins grave au plus grave :
| Niveau | Utilisation |
|---|---|
TRACE | Détails très fins (rarement utilisé) |
DEBUG | Informations de débogage (désactivé en production) |
INFO | Événements normaux (démarrage, connexion réussie) |
WARN | Situation anormale mais non bloquante |
ERROR | Erreur qui empêche une opération |
En production, on configure souvent le niveau minimum à INFO : seuls INFO, WARN et ERROR apparaissent. DEBUG et TRACE sont silencieux.
Logging avec paramètres {}
Avec SLF4J, utilise {} comme paramètre au lieu de concaténer. La String n'est construite que si le message est effectivement loggé (économie de ressources) :
// A eviter (concaténation toujours évaluée, même si DEBUG est désactivé)
logger.debug("Traitement de l'élève : " + eleve.getNom() + " (note: " + note + ")");
// Correct (paramètres évalués seulement si nécessaire)
logger.debug("Traitement de l'élève : {} (note: {})", eleve.getNom(), note);
Configurer Logback avec logback.xml
Crée le fichier src/main/resources/logback.xml pour personnaliser le comportement :
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- Affichage dans la console -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss} [%-5level] %logger{20} - %msg%n</pattern>
</encoder>
</appender>
<!-- Écriture dans un fichier -->
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>logs/application.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%-5level] %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- Niveau minimum : DEBUG -->
<root level="DEBUG">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
Explication du pattern : %d = date/heure, %-5level = niveau sur 5 chars, %logger{20} = nom de la classe (20 chars max), %msg = le message, %n = retour à la ligne.
MDC — Contexte de logging
MDC (Mapped Diagnostic Context) permet d'ajouter des informations de contexte à tous les logs d'un traitement :
import org.slf4j.MDC;
MDC.put("utilisateur", "alice");
MDC.put("requestId", "REQ-001");
logger.info("Début du traitement"); // inclura utilisateur=alice et requestId=REQ-001
logger.info("Fin du traitement");
MDC.clear(); // nettoyer après
Exemple pratique
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ServiceEleve {
private static final Logger logger = LoggerFactory.getLogger(ServiceEleve.class);
public static void inscrire(String nom, double note) {
logger.info("Tentative d'inscription : nom={}, note={}", nom, note);
if (nom == null || nom.isBlank()) {
logger.warn("Inscription refusée : nom vide");
return;
}
if (note < 0 || note > 20) {
logger.error("Note invalide reçue : {} (doit être entre 0 et 20)", note);
return;
}
// Inscription réussie
logger.info("Inscription réussie : {} avec la note {}", nom, note);
}
public static void main(String[] args) {
logger.info("Démarrage du service d'inscription");
inscrire("Alice", 15.5);
inscrire("", 12.0);
inscrire("Bob", 25.0);
inscrire("Charlie", 10.0);
logger.info("Service arrêté");
}
}
Observe la différence entre System.out.println et les logs SLF4J dans la console : les logs affichent automatiquement l'heure, le niveau et la classe source. Ces informations sont précieuses pour déboguer une application en production.
Test de mémorisation/compréhension
TP pour réfléchir et résoudre des problèmes
Vous allez ajouter un système de logging à une application de gestion de stock. L'application ajoute et retire des articles d'un stock.
Étape 1 — Configurer les dépendances Maven
Ajoute les dépendances SLF4J et Logback dans le pom.xml.
N'ajoutez qu'une seule implémentation de logging (Logback ou Log4j) dans votre projet. Si deux implémentations sont présentes dans le classpath, SLF4J affiche un avertissement et en choisit une arbitrairement. Vérifiez les dépendances transitives avec mvn dependency:tree.
Étape 2 — Déclarer le Logger dans la classe
Dans la classe GestionStock, déclare un Logger SLF4J en constante statique.
Chaque classe a son propre Logger créé avec sa propre classe en paramètre. Cela permet dans la configuration Logback de filtrer les logs par classe ou par package : utile pour activer le mode DEBUG uniquement sur une partie du code.
Étape 3 — Logger les opérations de stock
Dans la méthode retirer(), log en INFO quand le retrait réussit, et en WARN si le stock est insuffisant.
Un log utile contient les valeurs qui permettent de comprendre le problème sans accès au code source. "Stock insuffisant" seul ne suffit pas. "Stock insuffisant : demandé=50, disponible=20" permet de diagnostiquer immédiatement.