Votre application plante en production à 3 h du matin. L’alerte dit “erreur
500”, mais pas pourquoi. Vous ouvrez les logs, et vous tombez sur des
milliers de lignes en texte libre, sans structure, mélangées entre 12 services.
Trouver la cause revient à chercher une aiguille dans une botte de foin — avec
un grep. Les logs sont le signal d’observabilité le plus riche (chaque
événement peut contenir un contexte complet) mais aussi le plus coûteux en
stockage et le plus difficile à exploiter s’ils ne sont pas structurés.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Qu’est-ce qu’un log : événement discret vs métrique agrégée
- Structuré vs non structuré : pourquoi le JSON change tout
- Niveaux de sévérité : DEBUG, INFO, WARN, ERROR, FATAL — quand utiliser chacun
- Les champs essentiels : les 6 champs qu’un log structuré doit contenir
- Ce qu’il ne faut pas logger : PII, secrets, données sensibles
- Volume et coûts : pourquoi les logs sont le signal le plus cher
- Le pipeline de logs : de l’émission au requêtage
Qu’est-ce qu’un log
Section intitulée « Qu’est-ce qu’un log »Un log (ou journal) est un événement discret et horodaté : il décrit quelque chose qui s’est passé à un instant précis. Chaque requête HTTP traitée, chaque erreur rencontrée, chaque connexion à la base de données peut générer une ligne de log.
Contrairement à une métrique qui agrège des valeurs dans le temps (ex. : “142 requêtes/seconde”), un log conserve le détail individuel de chaque événement. Cela le rend extrêmement riche pour le diagnostic, mais aussi beaucoup plus volumineux à stocker.
L’analogie du carnet de bord
Section intitulée « L’analogie du carnet de bord »Un log fonctionne comme le carnet de bord d’un navire : chaque événement est noté avec l’heure, les circonstances et les détails pertinents. Le capitaine n’écrit pas “en moyenne, la mer était calme aujourd’hui” (c’est une métrique) — il note “14 h 32 : vent force 7, changement de cap vers le sud-est” (c’est un log).
Événement discret vs signal continu
Section intitulée « Événement discret vs signal continu »| Caractéristique | Log | Métrique |
|---|---|---|
| Nature | Événement discret | Valeur numérique agrégée |
| Granularité | Chaque occurrence individuelle | Agrégation sur une fenêtre de temps |
| Volume | Élevé (1 ligne par événement) | Faible (1 point par intervalle) |
| Coût de stockage | Élevé | Faible |
| Requêtage | Recherche textuelle / filtrage par champs | Calculs mathématiques (rate, sum, avg) |
| Usage principal | Diagnostic, audit, debug | Alerting, tendances, dashboards |
Logs structurés vs non structurés
Section intitulée « Logs structurés vs non structurés »La différence entre un log utile et un log inutilisable tient souvent à un seul choix : structuré ou non.
Le log non structuré
Section intitulée « Le log non structuré »C’est une ligne de texte libre, lisible par un humain mais difficile à exploiter par une machine :
2026-02-07 14:32:15 ERROR PaymentService - Payment failed for user 12345: insufficient funds (amount=99.99, balance=42.50)Pour extraire l’identifiant utilisateur, le montant ou le type d’erreur, il faut écrire une expression régulière — fragile, différente pour chaque format, et qui casse dès que le message change.
Le log structuré (JSON)
Section intitulée « Le log structuré (JSON) »Le même événement en JSON :
{ "timestamp": "2026-02-07T14:32:15.042Z", "level": "ERROR", "service": "payment-api", "message": "Payment failed: insufficient funds", "user_id": "12345", "amount": 99.99, "balance": 42.50, "trace_id": "abc123def456", "span_id": "789ghi"}Chaque champ est un clé/valeur typé. Un outil de requêtage peut
filtrer instantanément par level="ERROR", service="payment-api" ou
user_id="12345" — sans regex, sans ambiguïté.
Comparaison
Section intitulée « Comparaison »| Critère | Non structuré | Structuré (JSON) |
|---|---|---|
| Lisibilité humaine brute | Bonne | Correcte (avec jq) |
| Filtrage par champ | Regex fragile | Natif (clé/valeur) |
| Corrélation avec les traces | Manuelle | Automatique via trace_id |
| Indexation | Plein texte uniquement | Par champ — rapide et ciblé |
| Évolution du format | Casse les parseurs | Ajout de champs sans rupture |
Les niveaux de sévérité
Section intitulée « Les niveaux de sévérité »Les niveaux de log (ou severity levels) indiquent l’importance d’un événement. La RFC 5424 (Syslog) en définit 8, mais la plupart des applications utilisent une échelle simplifiée à 5 niveaux.
Les 5 niveaux courants
Section intitulée « Les 5 niveaux courants »| Niveau | Quand l’utiliser | Exemple |
|---|---|---|
| DEBUG | Détails fins utiles uniquement en développement ou diagnostic ponctuel | ”Requête SQL exécutée : SELECT * FROM users WHERE id=12345 — durée 3 ms” |
| INFO | Événements normaux qui marquent le bon fonctionnement | ”Serveur démarré sur le port 8080”, “Paiement de 99,99 € traité avec succès” |
| WARN | Situation anormale mais non bloquante — le service continue | ”Pool de connexions à 85 % de capacité”, “Retry n°2 vers le service externe” |
| ERROR | Échec d’une opération — une requête ou une action a échoué | ”Paiement refusé : fonds insuffisants”, “Timeout de connexion à la base” |
| FATAL | Erreur irrécupérable — le processus va s’arrêter | ”Impossible de lire le fichier de configuration”, “Port 8080 déjà occupé” |
Choisir le bon niveau
Section intitulée « Choisir le bon niveau »Le choix du niveau est un acte d’engagement envers l’équipe d’astreinte. Un WARN mal calibré génère du bruit ; un ERROR manquant masque un problème réel.
Deux questions pour choisir :
- Quelqu’un doit-il agir immédiatement ? → ERROR ou FATAL
- Est-ce que ça fonctionne encore ? → Si oui, WARN. Si non, ERROR.
La correspondance syslog (RFC 5424)
Section intitulée « La correspondance syslog (RFC 5424) »Pour les systèmes qui utilisent syslog, voici la correspondance avec les 8 niveaux de la RFC 5424 :
| Code | Niveau RFC 5424 | Correspondance application |
|---|---|---|
| 0 | Emergency | FATAL (système inutilisable) |
| 1 | Alert | FATAL (action immédiate requise) |
| 2 | Critical | FATAL / ERROR critique |
| 3 | Error | ERROR |
| 4 | Warning | WARN |
| 5 | Notice | INFO (événement significatif) |
| 6 | Informational | INFO |
| 7 | Debug | DEBUG |
Les champs essentiels d’un log structuré
Section intitulée « Les champs essentiels d’un log structuré »Un log structuré utile contient au minimum 6 champs qui répondent aux questions fondamentales du diagnostic : quand, quoi, où, qui, et comment le relier aux autres signaux.
| Champ | Pourquoi il est indispensable | Exemple |
|---|---|---|
timestamp | Quand — ordonne les événements chronologiquement | "2026-02-07T14:32:15.042Z" (ISO 8601, UTC) |
level | Gravité — permet le filtrage par sévérité | "ERROR" |
service | Où — identifie le composant émetteur | "payment-api" |
message | Quoi — décrit l’événement en langage humain | "Payment failed: insufficient funds" |
trace_id | Corrélation inter-services — relie le log à une trace distribuée (tracing) | "abc123def456" |
request_id | Corrélation locale — regroupe les logs d’une même requête, utile même sans tracing | "req-789ghi" |
trace_id est propagé entre les services par le système de tracing
distribué (OpenTelemetry, Jaeger…). request_id est généré à l’entrée
(reverse-proxy ou API gateway) et transmis via un header (X-Request-ID) :
il permet de corréler les logs d’une requête même si le tracing distribué
n’est pas encore en place.
Le message : court et stable
Section intitulée « Le message : court et stable »Le champ message doit rester lisible par un humain et stable dans
le temps. Les détails variables (montant, identifiant, durée) vont
dans des champs dédiés (error_type, duration_ms, amount…), pas
dans le message lui-même.
- ✅
"Payment failed: insufficient funds"+ champamount: 99.99 - ❌
"Payment of 99.99 EUR failed for user 12345 after 127ms: insufficient funds"
Un message stable permet de grouper les logs par type d’événement. Si chaque message est unique (parce qu’il contient des valeurs), le groupement devient impossible.
Champs complémentaires recommandés
Section intitulée « Champs complémentaires recommandés »| Champ | Usage |
|---|---|
span_id | Situe le log dans un span précis de la trace |
environment | Distingue production / staging / dev |
version | Identifie la version de l’application déployée |
hostname / pod_name | Localise l’instance émettrice |
user_id | Identifie l’utilisateur concerné (attention PII) |
duration_ms | Mesure la durée de l’opération loguée |
error_type | Catégorise l’erreur (TimeoutError, ValidationError) |
Exemple complet
Section intitulée « Exemple complet »{ "timestamp": "2026-02-07T14:32:15.042Z", "level": "ERROR", "service": "payment-api", "version": "2.3.1", "environment": "production", "hostname": "payment-api-7b4d9-xk2p", "message": "Payment failed: insufficient funds", "trace_id": "abc123def456", "span_id": "789ghi", "request_id": "req-a1b2c3", "user_id": "usr_12345", "amount": 99.99, "currency": "EUR", "error_type": "InsufficientFundsError", "duration_ms": 127}Ce log permet en un coup d’oeil de répondre à : quand (timestamp), quoi (message + error_type), où (service + hostname), pour qui (user_id), et de naviguer vers la trace complète (trace_id).
Ce qu’il ne faut pas logger
Section intitulée « Ce qu’il ne faut pas logger »Les logs traversent souvent des pipelines de collecte, des stockages partagés et des dashboards accessibles à plusieurs équipes. Certaines données ne doivent jamais s’y trouver.
Données interdites dans les logs
Section intitulée « Données interdites dans les logs »| Catégorie | Exemples | Risque |
|---|---|---|
| Secrets | Mots de passe, tokens API, clés privées | Compromission du système |
| Headers d’authentification | Authorization, Cookie, Set-Cookie | Tokens/sessions exposés en clair |
| PII (données personnelles) | E-mail, adresse postale, numéro de téléphone, IP | Non-conformité RGPD/CNIL |
| Données financières | Numéro de carte bancaire, IBAN complet | Violation PCI-DSS |
| Données médicales | Diagnostics, prescriptions | Violation réglementaire |
Bonnes pratiques
Section intitulée « Bonnes pratiques »- Masquer les données sensibles avant l’émission (ex. :
"card": "****1234") - Ne pas logger les corps de requêtes HTTP complets en production (ils contiennent souvent des formulaires avec des données utilisateur)
- Identifier les champs PII et les exclure du pipeline de collecte
ou les anonymiser dans le collecteur (ex. : processeur OTel
transform) - En cas de doute, ne pas logger le champ — on peut toujours l’ajouter plus tard
Volume et coûts : le signal le plus cher
Section intitulée « Volume et coûts : le signal le plus cher »Les logs sont, de loin, le signal d’observabilité le plus volumineux et le plus coûteux. En ordre de grandeur, une application web génère entre 1 et 10 Go de logs par jour (davantage sous fort trafic ou avec des logs verbeux). En comparaison, ses métriques Prometheus occupent quelques centaines de Mo, et ses traces (échantillonnées) quelques Go par semaine. Ces chiffres varient fortement selon le trafic, le nombre de services et le niveau de verbosité.
Pourquoi les logs coûtent cher
Section intitulée « Pourquoi les logs coûtent cher »Le coût des logs est le produit de trois facteurs :
Volume × Rétention × Requêtage = Coût
| Facteur | Ce qui l’augmente | Levier d’optimisation |
|---|---|---|
| Volume | Logs DEBUG en production, corps de requêtes, logs verbeux | Réduire le niveau en prod (INFO minimum), filtrer à la source |
| Rétention | Conservation illimitée (“on ne sait jamais”) | Définir des politiques par importance : 7 j DEBUG, 30 j INFO/WARN, 90 j ERROR |
| Requêtage | Index plein texte sur tous les champs | Indexer uniquement les champs utiles (labels dans Loki) |
Comparaison du coût par signal
Section intitulée « Comparaison du coût par signal »| Signal | Volume typique/jour | Coût relatif de stockage | Rétention courante |
|---|---|---|---|
| Métriques | ~100 Mo – 1 Go | Faible | 15 – 90 jours |
| Traces (échantillonnées) | ~500 Mo – 5 Go | Moyen | 7 – 30 jours |
| Logs | ~1 – 50 Go | Élevé | 7 – 90 jours |
Le pipeline de logs
Section intitulée « Le pipeline de logs »Les logs parcourent un chemin en 4 étapes entre leur émission et le moment où un humain les consulte. Comprendre ce pipeline est essentiel pour savoir où agir sur le volume, le format et l’enrichissement.
-
Émission
L’application génère le log via une bibliothèque de logging (Python
structlog, JavaLogback, Goslog, Node.jspino). C’est à cette étape que le format (structuré ou non) et le niveau de sévérité sont décidés. En environnement conteneurisé, les logs sont émis sur stdout/stderr — le runtime (Docker, Kubernetes) les capture automatiquement. -
Collecte
Un agent ou un collecteur récupère les logs depuis la source (fichier, stdout, socket syslog) et les transmet au stockage. Exemples : Promtail (pour Loki), Fluentd, Fluent Bit, OpenTelemetry Collector. C’est à cette étape qu’on peut filtrer, enrichir (ajouter des labels Kubernetes), transformer le format, et échantillonner ou limiter le débit (rate limit) des logs très fréquents pour maîtriser le volume.
-
Stockage et indexation
Le backend stocke les logs et les indexe pour permettre le requêtage. Deux approches existent : Elasticsearch indexe le contenu riche des logs (recherche plein texte puissante, mais coûteuse en stockage et en ressources). Loki optimise l’index sur des labels et s’appuie sur le scan du contenu au moment du requêtage — plus économique, mais les recherches dans le corps du message sont plus lentes sur de gros volumes.
-
Requêtage et visualisation
L’opérateur recherche dans les logs via un langage de requête (LogQL pour Loki, KQL pour Elasticsearch/Kibana) depuis une interface comme Grafana Explore. C’est à cette étape que la structure du log paie : un log JSON avec des champs nommés se filtre en secondes ; un log texte libre nécessite des regex lentes.
Schéma du pipeline
Section intitulée « Schéma du pipeline »Le chemin que prend un log dépend de l’environnement d’exécution :
- En conteneurs (Docker, Kubernetes) : l’application écrit sur stdout/stderr, le runtime capture les logs et un agent (Promtail, Fluent Bit, OTel Collector) les collecte
- Sur VM / bare-metal : l’application écrit dans un fichier (.log), un agent le lit (tail) et le transmet
- Syslog : l’application envoie sur une socket (UDP/TCP), rsyslog ou syslog-ng reçoit et route
Checklist : log prêt pour la production
Section intitulée « Checklist : log prêt pour la production »Avant de déployer un service, vérifiez que vos logs cochent ces cases :
- 1 événement = 1 log JSON — pas de logs multi-lignes (stack traces
incluses dans un champ
error.stack, pas étalées sur 30 lignes) - Timestamp UTC ISO 8601 —
2026-02-07T14:32:15.042Z, jamais l’heure locale levelcalibré — pas de WARN partout, pas d’ERROR pour un cas nominalservice,version,environmentrenseignés — identifient précisément l’émetteurtrace_idet/ourequest_idprésents — permettent la corrélation- Aucun secret ni PII en clair — ni dans le message, ni dans les champs
- Pas de payloads énormes — pas de body HTTP complets, pas de dumps d’objets
- Message court et stable — les variables dans des champs dédiés
Pièges courants
Section intitulée « Pièges courants »| Piège | Pourquoi c’est un problème | Que faire |
|---|---|---|
| Logger en texte libre | Impossible à filtrer proprement, regex fragiles | Adopter le JSON structuré avec des champs nommés |
Oublier le trace_id | Les logs sont isolés — pas de corrélation avec les traces | Injecter le trace_id via le contexte OpenTelemetry ou un middleware |
| Logger les corps de requêtes | Volume énorme + risque PII | Logger uniquement les métadonnées (méthode, path, status, durée) |
| Pas de convention de nommage | userId / user_id / UserID dans 3 services différents | Définir un schéma de nommage d’équipe (snake_case recommandé) |
| Ignorer la rétention | Les logs s’accumulent indéfiniment → coût explosif | Définir une politique de rétention par niveau |
| Confondre log et métrique | ”Compter les erreurs via grep” est lent et imprécis | Les compteurs appartiennent aux métriques, pas aux logs |
À retenir
Section intitulée « À retenir »-
Un log est un événement discret et horodaté — il capture le détail individuel, contrairement à la métrique qui agrège.
-
Un log structuré (JSON avec des champs nommés) est interrogeable par une machine ; un log texte libre ne l’est que par
grep. -
Les 6 champs essentiels sont :
timestamp(UTC, ISO 8601),level,service,message,trace_id,request_id. -
Les niveaux de sévérité (DEBUG → INFO → WARN → ERROR → FATAL) sont un engagement envers l’équipe d’astreinte : un ERROR doit toujours mériter une investigation.
-
Ne jamais logger de secrets, de PII en clair ni de données financières complètes.
-
Les logs sont le signal le plus cher : le volume en production doit être maîtrisé (pas de DEBUG permanent, rétention adaptée).
-
Le pipeline émission → collecte → stockage → requêtage est la chaîne à optimiser. La qualité du log se décide à l’émission, pas au requêtage.
-
IETF — RFC 5424 : The Syslog Protocol : datatracker.ietf.org/doc/html/rfc5424 — la spécification des niveaux de sévérité syslog (0–7) et du format structuré syslog.
-
OpenTelemetry — Logs Data Model : opentelemetry.io/docs/specs/otel/logs/data-model — le modèle de données OTel pour les logs, incluant severity, body, attributes et les liens avec traces/métriques.
-
Uptrace — Structured Logging: Best Practices & JSON Examples : uptrace.dev/glossary/structured-logging — guide pratique des champs JSON, des bibliothèques par langage et de la corrélation avec les traces.