CodeGym /Cours /JAVA 25 SELF /Mise en forme et niveaux de logs : bonnes pratiques

Mise en forme et niveaux de logs : bonnes pratiques

JAVA 25 SELF
Niveau 63 , Leçon 1
Disponible

1. Structure d’un message de log

Imaginons que les logs ne sont pas simplement « flux de conscience » de votre programme, mais un journal précieux dans lequel, dans un mois ou un an, vous ou votre collègue pourrez trouver la réponse à la question : « Qu’est-ce qui s’est donc passé ici, bon sang ? ». Pour que cela soit possible, chaque message de log doit être structuré. En général (et c’est la norme dans la plupart des bibliothèques), chaque message contient :

  • Horodatage — quand cela s’est produit.
  • Niveau — à quel point c’est important (INFO, ERROR, etc.).
  • Nom du logger — généralement le nom de la classe ou du composant.
  • Texte du message — ce qui s’est exactement passé.
  • Stack trace (en cas d’erreur) — pour comprendre où et pourquoi.

Voici un exemple de ligne de log bien formatée (Log4j/SLF4J) :

2024-06-16 18:42:07,123 INFO  com.example.MainApp - L’utilisateur s’est connecté au système : username=john_doe

Et si une erreur s’est produite :

2024-06-16 18:42:10,456 ERROR com.example.LoginService - Erreur d’authentification de l’utilisateur : john_doe
java.lang.IllegalArgumentException: Mot de passe invalide
    at com.example.LoginService.checkPassword(LoginService.java:42)
    ...

Pourquoi est-ce important ?
Lorsque l’application fonctionne longtemps, les logs peuvent peser des gigaoctets. Si les messages ne sont pas structurés, trouver un problème devient une tâche du genre « deviner une mélodie au bruit du ventilateur ».

2. Mise en forme des messages

Pourquoi éviter ceci :

logger.info("L'utilisateur " + username + " s'est connecté au système");

Cela paraît simple, mais il y a un piège : même si le niveau de journalisation est actuellement ERROR, la chaîne à l’intérieur des parenthèses sera quand même construite (la concaténation s’exécutera), ce qui entraîne une consommation inutile de ressources. Dans les grands systèmes où l’on écrit des milliers de lignes de log par seconde, cela peut provoquer de réels retards.

La bonne approche : modèles et paramètres

Les bibliothèques modernes (par exemple SLF4J et Log4j 2) prennent en charge les modèles avec paramètres :

logger.info("L'utilisateur {} s'est connecté au système", username);

Ici, la chaîne ne sera construite que si le niveau de journalisation autorise l’affichage de ce message. Si le niveau est, par exemple, WARN, alors même le calcul de la chaîne n’aura pas lieu — économie de ressources et de nerfs.

Bonus : si vous passez plusieurs paramètres, ils seront substitués dans l’ordre :

logger.info("L'utilisateur {} a effectué l'action {} sur l'objet {}", username, action, objectId);

Journalisation des exceptions (stack trace)

Si vous interceptez une exception, n’ajoutez pas manuellement la stack trace au message :

// À NE PAS FAIRE :
logger.error("Erreur : " + ex.getMessage() + "\n" + Arrays.toString(ex.getStackTrace()));

Correct :

logger.error("Erreur lors du traitement de la requête", ex);

SLF4J et Log4j ajouteront eux-mêmes proprement la stack trace au log.

Exemple : comparaison des approches

// Mauvais (la concaténation s'exécute toujours)
logger.debug("Objet : " + expensiveToString(obj));

// Bon (construction paresseuse)
logger.debug("Objet : {}", obj);

3. Choix des niveaux de journalisation

Si tout votre log est au niveau ERROR, alors ce ne sont plus des logs mais « un voyant rouge ». Si tout est en DEBUG, vous vous noierez dans les détails. Voyons quand utiliser chaque niveau.

Niveau À quoi il sert Exemple de message
ERROR
Pannes critiques qui rendent le système défaillant ou inutilisable « Erreur de connexion à la base de données »
WARN
Avertissements importants, non critiques mais nécessitant une attention « Impossible de trouver l’utilisateur, utilisation de guest »
INFO
Événements normaux reflétant le bon fonctionnement de l’application « Utilisateur enregistré : john_doe »
DEBUG
Informations détaillées pour le débogage, inutiles en production « Méthode checkPassword appelée avec des paramètres ... »
TRACE
Informations les plus détaillées, généralement pour un diagnostic poussé « Début de la boucle de traitement : i=0 »

Exemples de messages typiques

  • ERROR — échec d’écriture d’un fichier, exception non gérée interceptée, service indisponible.
  • WARN — API obsolète, comportement utilisateur suspect, limite de tentatives dépassée.
  • INFO — l’utilisateur est entré/sorti, traitement de commande terminé, démarrage de l’application.
  • DEBUG — paramètres de requête, valeurs de variables, résultats intermédiaires des calculs.
  • TRACE — entrée/sortie des méthodes, boucles internes, détails du fonctionnement des algorithmes.

Conseil :
En production, on active généralement uniquement INFO et au-dessus, parfois WARN et ERROR. DEBUG et TRACE — seulement lors de la recherche de bugs complexes.

4. Bonnes pratiques de journalisation

Ne journalisez pas de données sensibles

Les mots de passe, tokens, numéros de cartes bancaires — tout cela n’a pas sa place dans les logs. Même si vous pensez que « le fichier de log — c’est seulement pour moi », souvenez-vous du RGPD et du collègue qui enverra par inadvertance un log dans un canal commun.

// Mauvais :
logger.info("L'utilisateur {} s'est connecté avec le mot de passe {}", username, password);

// Bon :
logger.info("L'utilisateur {} s'est connecté au système", username);

N’abusez pas du niveau ERROR

Si vous écrivez tout via logger.error, le jour où une vraie catastrophe arrivera, personne ne la remarquera — tout le monde sera habitué aux « voyants rouges ». Utilisez ERROR uniquement pour les situations où l’application ne peut réellement pas continuer à fonctionner ou lorsque la logique métier est violée.

Journalisez les exceptions avec la stack complète

N’écrivez pas uniquement ex.getMessage(), sinon vous ne saurez jamais où l’erreur s’est produite. Passez l’exception en deuxième paramètre au logger.

logger.error("Erreur lors du traitement de la requête", ex);

Utilisez des identifiants uniques (corrélation des événements)

Dans les grands systèmes, il est utile d’attribuer un identifiant unique à chaque requête, utilisateur ou opération. Cela aide à « recoudre » les événements provenant de différentes parties du système.

logger.info("Début du traitement de la commande : orderId={}", orderId);
logger.info("Commande traitée avec succès : orderId={}", orderId);

Ne mettez pas tout et n’importe quoi dans les logs

Si les logs sont trop verbeux, ils deviennent inutiles. Ne journalisez pas chaque ligne de code, sinon il sera impossible de trouver l’information pertinente.

Formatez des messages compréhensibles

Rédigez des messages de sorte qu’ils soient compris non seulement par l’auteur du code, mais aussi par la personne qui lira les logs dans six mois. Évitez les abréviations, les raccourcis obscurs et les « blagues d’initiés ».

5. Pratique : configuration du format de log et des niveaux

Exemple de configuration du format dans Log4j2 (log4j2.xml)

<Configuration>
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="info">
      <AppenderRef ref="Console"/>
    </Root>
  </Loggers>
</Configuration>

Qu’est-ce que cela signifie ?

  • %d{...} — horodatage.
  • %-5level — niveau (ERROR, INFO, etc.).
  • %logger{36} — nom du logger (généralement la classe).
  • %msg — le message lui-même.

Exemple de code avec différents niveaux de journalisation (SLF4J)

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LogDemo {
    private static final Logger logger = LoggerFactory.getLogger(LogDemo.class);

    public static void main(String[] args) {
        logger.info("Application démarrée");
        logger.debug("Valeur de la variable x : {}", 42);

        try {
            throw new IllegalArgumentException("Aïe aïe aïe !");
            // ...
        } catch (Exception ex) {
            logger.error("Une erreur s'est produite au démarrage", ex);
        }
    }
}

Démonstration de la différence entre les niveaux

Si la configuration du logger est au niveau INFO, les messages de niveau DEBUG et inférieur ne seront pas affichés. Essayez de passer le niveau à debug dans la configuration — vous verrez plus de détails.

6. Erreurs typiques

Erreur n° 1 : Concaténation de chaînes dans les logs. Très souvent, les débutants écrivent ceci :

logger.debug("Utilisateur : " + user.getName() + ", rôle : " + user.getRole());

En conséquence, même avec le niveau DEBUG désactivé, ces chaînes seront construites, ce qui entraîne une charge superflue. Utilisez les paramètres !

Erreur n° 2 : Journalisation sans la stack trace de l’exception. Ils n’écrivent que le message :

logger.error("Erreur : " + ex.getMessage());

Au final, il n’y a aucune information dans le log sur l’endroit précis où l’erreur s’est produite. Passez l’exception en second paramètre !

Erreur n° 3 : Tout journaliser au niveau ERROR. Si tout est en rouge — rien n’est vraiment rouge. Utilisez les niveaux comme il se doit, sinon les erreurs importantes se perdront parmi les « broutilles ».

Erreur n° 4 : Journalisation de données sensibles. N’écrivez jamais de mots de passe, tokens, numéros de carte dans les logs. Même si vous pensez que personne ne les verra, la vie réserve des surprises.

Erreur n° 5 : Messages incompréhensibles. Si un message de log ressemble à « ERR42: fail », dans un mois vous ne vous souviendrez pas vous-même de ce que cela signifie. Écrivez de manière claire et détaillée.

Erreur n° 6 : Absence d’identifiants uniques. Dans les systèmes complexes, sans orderId, userId et d’autres identifiants, vous ne pourrez pas « recoudre » les événements ni comprendre ce qui est arrivé à un utilisateur ou une commande précis.

1
Mission
JAVA 25 SELF, niveau 63, leçon 1
Bloqué
Architecte
Architecte
1
Mission
JAVA 25 SELF, niveau 63, leçon 1
Bloqué
Réseau social
Réseau social
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION