Aller au contenu principal

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>
Pourquoi deux dépendances ?

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 :

NiveauUtilisation
TRACEDétails très fins (rarement utilisé)
DEBUGInformations de débogage (désactivé en production)
INFOÉvénements normaux (démarrage, connexion réussie)
WARNSituation anormale mais non bloquante
ERRORErreur 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 {}

Préfère les paramètres au lieu de la concaténation

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é");
}
}
astuce

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


Qu'est-ce que SLF4J ?


Quel niveau de log utiliser pour une information normale (ex: 'Connexion réussie') ?


Quelle est la convention pour déclarer un Logger dans une classe Java ?


Pourquoi préférer logger.info('Valeur: {}', valeur) à logger.info('Valeur: ' + valeur) ?


Dans quel dossier Maven place-t-on le fichier logback.xml ?


Quel niveau de log permet de journaliser les détails de débogage sans impacter la production ?


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.


Bonne pratique - Une seule implémentation de logging

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.


Bonne pratique - Logger par classe

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.


Bonne pratique - Inclure les valeurs pertinentes dans les logs

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.

📌 Une solution