Les logs : structure, niveaux et coût du signal le plus riche
Un log est un événement horodaté. Structuré en JSON avec les bons champs et le bon niveau, il devient interrogeable. Ce qu'il ne faut jamais logger et son coût.
L’issue s’ouvre déjà remplie avec le titre et l’adresse de cette page.
Votre application renvoie des erreurs 500 à 3 h du matin, et l'alerte ne dit
pas pourquoi. Vous ouvrez les journaux et tombez sur des milliers de lignes en
texte libre, mélangées entre douze services, dont le format change d'une
application à l'autre. Les logs sont le signal d'observabilité le plus
riche, puisque chaque événement peut porter tout son contexte, mais aussi le
plus coûteux à stocker et le plus pénible à exploiter quand ils ne sont pas
structurés.
Cette leçon s'adresse à qui a déjà centralisé des journaux, par exemple dans le
lab Loki, et veut savoir ce qui rend un
log utile quel que soit l'outil qui le stocke : le format, le niveau de
sévérité, les champs indispensables, ce qu'il ne faut jamais écrire et ce qui
fait grimper la facture.
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 traitée,
chaque erreur rencontrée, chaque connexion à une base de données peut produire
une ligne. Là où une métrique agrège des valeurs dans le temps, le log
conserve le détail individuel de chaque événement.
Les deux signaux répondent à des questions différentes. Une métrique indique
qu'un taux d'erreur est passé de 0,1 % à 4 % entre 14 h 30 et 14 h 35 ; un
log indique quelle requête a échoué, pour quel utilisateur et avec quel
message. Un diagnostic part presque toujours de la métrique pour situer la
fenêtre de temps, puis descend dans les logs de cette fenêtre uniquement.
Caractéristique
Log
Métrique
Nature
Événement discret
Valeur numérique agrégée
Granularité
Chaque occurrence
Agrégat sur une fenêtre de temps
Volume
Élevé : une ligne par événement
Faible : un point par intervalle
Coût de stockage
Élevé
Faible
Requêtage
Filtrage par champs, recherche textuelle
Calculs (taux, somme, percentile)
Usage principal
Diagnostic, audit, débogage
Alerting, tendances, tableaux de bord
Glissez pour voir
La colonne du volume explique une pratique courante : on garde les
métriques plusieurs mois et les logs quelques semaines. Elle explique aussi
une erreur fréquente, compter des événements en fouillant les logs, alors
qu'un compteur exposé en métrique donnerait le même chiffre pour quelques
octets. La leçon Les métriques
détaille ce signal complémentaire.
La différence entre un log utile et un log inutilisable tient souvent à un seul
choix : structuré ou non. Un log non structuré est une phrase écrite pour un
humain ; un log structuré est un ensemble de champs nommés qu'une machine
filtre sans deviner. Les deux exemples qui suivent décrivent le même
événement.
C'est une ligne de texte libre, lisible par un humain mais difficile à
exploiter par une machine. Pour en extraire l'identifiant de l'utilisateur, le
montant ou le type d'erreur, il faut écrire une expression régulière,
différente pour chaque format, et qui casse dès que le développeur reformule
son message.
2026-02-07 14:32:15 ERROR PaymentService - Payment failed for user 12345: bank provider timeout after 3000ms (amount=99.99)
Le même événement en JSON sépare chaque information dans un champ typé.
Un outil de requêtage filtre alors directement sur level, service ou
user_id, sans expression régulière et sans ambiguïté, et un champ ajouté
demain ne casse aucune requête déjà écrite.
{
"timestamp": "2026-02-07T14:32:15.042Z",
"level": "ERROR",
"service": "payment-api",
"message": "Payment failed: bank provider timeout",
Ce tableau sert d'argumentaire quand une équipe hésite à reprendre son logging
existant. La ligne décisive est l'évolution du format : un parseur à base
d'expressions régulières casse dès qu'une information s'insère au milieu d'un
message, alors qu'un champ JSON supplémentaire est simplement ignoré. La
lisibilité brute est le seul critère où le texte libre garde l'avantage, et
il disparaît dès qu'on passe la sortie dans un formateur comme jq.
Le niveau de sévérité dit l'importance d'un événement, et il sert de premier
filtre à tout le monde : à l'équipe d'astreinte qui cherche ce qui exige une
action, au pipeline qui décide quoi garder, au stockage qui applique des
durées de rétention différentes. La RFC 5424 en définit huit, mais la plupart des
applications utilisent une échelle à cinq niveaux.
Lisez la colonne « Quand l'utiliser » avant les exemples : c'est elle qui porte
le critère de décision. Le piège se situe presque toujours à la frontière
entre WARN et ERROR : beaucoup d'équipes classent en ERROR tout ce qui
lève une exception, y compris un refus métier normal comme un mot de passe
invalide. Le tableau de bord d'astreinte affiche alors des centaines d'ERROR
par heure qui n'appellent aucune action, et la vraie panne passe inaperçue.
Niveau
Quand l'utiliser
Exemple
DEBUG
Détails fins, utiles en développement ou en diagnostic ponctuel
« Requête SQL exécutée en 3 ms »
INFO
Événement normal qui marque le bon fonctionnement
« Serveur démarré sur le port 8080 »
WARN
Situation anormale mais non bloquante, le service continue
« Pool de connexions à 85 % », « Nouvelle tentative n°2 »
ERROR
Une opération a échoué et mérite une investigation
« Délai dépassé sur la connexion à la base »
FATAL
Erreur irrécupérable, le processus va s'arrêter
« Fichier de configuration illisible au démarrage »
Le niveau est un engagement envers l'équipe d'astreinte : un WARN mal
calibré produit du bruit, un ERROR manquant masque un problème réel. Deux
questions suffisent à trancher la plupart des cas, à condition de les poser
dans l'ordre indiqué.
Le service fonctionne-t-il encore pour cette requête ? Si oui, et que la
situation reste anormale (nouvelle tentative réussie, ressource presque
pleine), c'est un WARN. Si l'opération a échoué, c'est au moins un
ERROR. Un refus métier attendu, comme un mot de passe invalide, n'est
pas un échec du service : c'est un INFO. Côté sécurité, des échecs
d'authentification répétés signalent peut-être une attaque par force
brute : on les compte et on alerte sur le taux, plutôt que de les promouvoir
en ERROR un par un.
Le processus peut-il continuer ? Si l'erreur empêche le service de
tourner, au démarrage par exemple, c'est un FATAL. Sinon l'ERROR suffit :
la requête suivante peut réussir, et c'est le taux d'erreurs, mesuré par
une métrique, qui décidera d'une alerte.
En production, la grande majorité des lignes devrait être de niveau INFO,
les WARN devraient rester minoritaires, et chaque ERROR devrait mériter
qu'on le regarde. Un volume de WARN ou d'ERROR qui ne déclenche jamais aucune
action signale un calibrage à revoir, pas un service malade.
Pour les systèmes qui utilisent syslog, le tableau fait correspondre les
huit niveaux de la RFC 5424 aux cinq niveaux applicatifs. Retenez surtout le
sens des codes : ils croissent vers le moins grave, 0 étant le plus
critique et 7 le plus bavard.
Code
Niveau RFC 5424
Niveau applicatif
0
Emergency
FATAL (système inutilisable)
1
Alert
FATAL (action immédiate requise)
2
Critical
FATAL ou ERROR critique
3
Error
ERROR
4
Warning
WARN
5
Notice
INFO (événement significatif)
6
Informational
INFO
7
Debug
DEBUG
Glissez pour voir
Ce sens inversé est une source classique d'erreurs de filtrage. Ainsi,
journalctl -p 3 ne montre pas seulement les messages de niveau 3 : il affiche
tous ceux dont la priorité est inférieure ou égale à 3, donc Error,
Critical, Alert et Emergency.
Un log structuré utile contient au minimum six champs, qui répondent aux
questions du diagnostic : quand, avec quelle gravité, où, quoi, et comment le
relier aux autres signaux. Sans eux, un log est un fragment ; avec eux, il se
filtre, se trie et se raccorde à une trace.
Le tableau donne, pour chacun des six champs, la question à laquelle il
répond et un exemple de valeur. Lisez-le comme une liste de contrôle : un champ absent laisse une question du diagnostic sans réponse.
Champ
Pourquoi il est indispensable
Exemple
timestamp
Quand : ordonne les événements
"2026-02-07T14:32:15.042Z" (ISO 8601, UTC)
level
Gravité : filtrage par sévérité
"ERROR"
service
Où : identifie le composant émetteur
"payment-api"
message
Quoi : décrit l'événement
"Payment failed: bank provider timeout"
trace_id
Corrélation entre services : relie le log à une trace distribuée
"4bf92f35…"
request_id
Corrélation locale : regroupe les logs d'une requête, même sans tracing
"req-789ghi"
Glissez pour voir
Le trace_id est propagé entre les services par le système de tracing
distribué, comme le décrit la leçon Les traces.
Le request_id est généré à l'entrée, par un reverse proxy ou une
passerelle d'API, et transmis dans un en-tête comme X-Request-ID : il relie
les logs d'une requête même quand le tracing n'est pas encore en place.
Une heure locale change selon le fuseau de la machine et l'heure d'été, ce
qui rend l'ordre des événements invalide dès que deux services ne partagent
pas le même réglage. L'encadré donne la règle à appliquer.
Le champ message doit rester lisible et stable dans le temps : les
détails variables, comme le montant, l'identifiant ou la durée, vont dans des
champs dédiés. Un message stable permet de grouper les logs par type
d'événement ; si chaque message contient ses valeurs, il devient unique et le
regroupement est impossible.
Correct : "Payment failed: bank provider timeout" avec les champs amount et duration_ms
À éviter : "Payment of 99.99 EUR failed for user 12345 after 3000ms: bank provider timeout"
Aucun de ces champs n'est obligatoire, mais chacun répond à une question qui
revient pendant un incident. version et hostname permettent de trancher
entre « tout le parc est touché » et « une seule instance dérive », ce qui
oriente vers un retour arrière ou le remplacement d'un pod. duration_ms
rend la latence analysable sans métrique dédiée. user_id est le plus
délicat : il rapproche le log d'une donnée personnelle et impose une
pseudonymisation.
Champ
Usage
span_id
Situe le log dans un span précis de la trace
environment
Distingue production, préproduction et développement
Ce log rassemble les champs indispensables et les complémentaires. Il répond
d'un coup d'œil à quand (timestamp), quoi (message et type d'erreur),
où (service et instance) et pour qui (identifiant pseudonymisé), et il
permet d'ouvrir la trace complète grâce à son trace_id.
{
"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: bank provider timeout",
Les logs traversent des pipelines de collecte, des stockages partagés et des
tableaux de bord ouverts à plusieurs équipes : tout ce qui y est écrit devient
lisible par beaucoup de monde, et durable.
Une ligne est répliquée, indexée et sauvegardée dans la minute qui suit son
émission : la retirer après
coup impose de purger l'index, les sauvegardes et les exports vers un
SIEM, sans certitude d'avoir tout couvert. Un secret parti dans les logs est
un secret à révoquer, pas à effacer.
Le tableau classe les données à bannir par catégorie et indique le risque
associé. Il sert de grille en revue de code : une valeur qui entre dans l'une de ces lignes ne doit pas être journalisée en clair.
Catégorie
Exemples
Risque
Secrets
Mots de passe, jetons d'API, clés privées
Compromission du système
En-têtes d'authentification
Authorization, Cookie, Set-Cookie
Sessions et jetons exposés en clair
Données personnelles
E-mail, adresse postale, téléphone, adresse IP (arbitrage ci-dessous)
Le masquage s'applique à deux endroits complémentaires. À l'émission, dans
le code, c'est le plus sûr : la valeur ne quitte jamais le processus, mais
chaque service doit être modifié. Dans le collecteur, on rattrape ce que les
applications ont laissé passer, en une seule configuration, au prix d'un
traitement appliqué à tout le flux. Une équipe qui ne peut pas reprendre trente
services d'un coup commence par le collecteur, puis remonte le masquage dans
le code service par service.
Masquer à l'émission : "card": "****1234" plutôt que le numéro complet
Ne pas journaliser les corps de requêtes HTTP en production : ils
contiennent souvent des formulaires remplis par les utilisateurs
Filtrer dans le collecteur les champs identifiés comme sensibles
Dans le doute, ne pas écrire le champ : on peut toujours l'ajouter
ensuite, jamais le retirer des sauvegardes
L'adresse IP demande un arbitrage. C'est une donnée personnelle, mais aussi
la première pièce d'une réponse à incident : les journaux de sécurité et
d'accès (pare-feu, authentification, serveur web frontal) en ont besoin pour
retrouver l'origine d'une attaque. On la masque donc dans les logs applicatifs
ordinaires, et on la conserve dans ces journaux dédiés, avec un accès restreint
et une durée de conservation encadrée et documentée.
Les logs sont le signal le plus volumineux, parce qu'ils sont les seuls à
conserver chaque occurrence sans agrégation. Leur coût se décompose en trois
postes qui s'additionnent, et c'est sur chacun d'eux qu'on agit pour le
maîtriser : l'ingestion, proportionnelle au volume émis, le stockage,
proportionnel au volume multiplié par la durée de conservation, et le
requêtage, qui se facture ou se dimensionne à part.
DEBUG en production, corps de requêtes, messages verbeux
INFO au minimum en production, filtrage à la source
Rétention
Conservation illimitée « au cas où »
Durées par niveau, par exemple courtes pour DEBUG, plus longues pour ERROR
Requêtage
Index plein texte sur tous les champs
Indexer seulement les champs utiles au filtrage
Glissez pour voir
Le facteur le plus simple à régler est la rétention : ramener les logs de 90
à 30 jours divise le stockage par trois sans toucher une ligne de code. Réduire
le volume émis demande au contraire de reprendre l'instrumentation service
par service, mais c'est le seul levier qui réduit aussi le coût de
collecte et d'indexation.
Comment un log voyage-t-il de l'application au requêtage ?
Un log parcourt quatre étapes entre son émission et le moment où quelqu'un
le lit. Savoir où se fait chaque traitement indique où agir : le format et le
niveau se décident à l'émission, le filtrage et l'enrichissement à la
collecte, le coût au stockage.
Émission. L'application produit le log par une bibliothèque de
journalisation, comme structlog en Python, slog en Go ou pino en
Node.js. C'est là que se décident le format et le niveau. En conteneur, les
logs sont écrits sur la sortie standard, et le moteur d'exécution les
capture.
Collecte. Un agent récupère les logs depuis un fichier, la sortie
standard ou une socket syslog, puis les transmet. C'est l'étape où l'on
filtre, où l'on ajoute des métadonnées comme le nom du pod, et où l'on
limite le débit des messages trop fréquents.
Stockage et indexation. Deux familles de backends coexistent. Les uns
indexent le contenu des logs, avec une recherche plein texte puissante
mais coûteuse ; les autres n'indexent que quelques labels et parcourent le
contenu au moment de la requête, plus économe mais plus lent sur de gros
volumes.
Requêtage. L'opérateur interroge les logs par un langage de requête,
depuis une interface d'exploration. C'est là que la structure paie : un log
JSON se filtre par champ en quelques secondes, un texte libre impose des
expressions régulières lentes et fragiles.
Le chemin varie selon l'environnement, et ce qui distingue vraiment les
variantes, c'est l'endroit où les lignes attendent, donc où elles se perdent
quand tout sature. En conteneur, le moteur d'exécution retient la sortie
standard et peut perdre des lignes à la rotation de fichier. Sur une machine
virtuelle, le fichier absorbe les à-coups mais expose au disque plein si la
rotation n'est pas configurée. En syslog sur UDP, il n'y a aucun accusé de
réception : un collecteur saturé perd les messages en silence, d'où le choix de
TCP dès que les logs servent de piste d'audit, c'est-à-dire de trace
exploitable d'une action sensible : qui l'a faite (identité), quoi (action et
ressource), quand (horodatage fiable) et avec quel résultat, dans un journal
protégé contre la modification.
Dans la formation, le lab Loki fait
parcourir ces quatre étapes sur un clusterKubernetes, et la leçon
Loki hors Kubernetes, avec Alloy les
reprend sur une machine seule, avec l'agent Alloy qui remplace l'ancien
collecteur.
Cette liste se relit en revue de code, pas après la mise en production :
chaque point manquant coûte plus cher à rattraper une fois les logs émis en
volume. Les deux premiers bloquent l'ingestion elle-même, puisqu'un log sur
plusieurs lignes ou un horodatage sans fuseau produit des entrées mal découpées
ou mal ordonnées dans le backend.
Un événement, un log JSON : pas de log sur plusieurs lignes, la pile
d'appels va dans un champ dédié
Horodatage en UTC et en ISO 8601, jamais en heure locale
Niveau calibré : pas de WARN partout, pas d'ERROR pour un cas normal
service, version et environment renseignés
trace_id ou request_id présent pour la corrélation
Aucun secret ni donnée personnelle en clair, ni dans le message, ni dans
les champs
Pas de charge utile volumineuse : ni corps HTTP complet, ni objet sérialisé
en entier
Message court et stable, les valeurs dans des champs dédiés
Ces six pièges se corrigent dans l'ordre du tableau, parce qu'ils dépendent les
uns des autres. Le passage au JSON structuré conditionne tout le reste : sans
champs nommés, ni le trace_id, ni la convention de nommage, ni le
filtrage à la collecte n'ont de prise. La dernière ligne coûte le plus cher
quand on la corrige tard : compter des occurrences par recherche textuelle
revient à stocker des millions de lignes pour obtenir un seul nombre.
Piège
Pourquoi c'est un problème
Que faire
Logger en texte libre
Filtrage par expressions régulières fragiles
Adopter le JSON avec des champs nommés
Oublier le trace_id
Les logs restent isolés des traces
Injecter le trace_id depuis le contexte de tracing
Les références ci-dessous sont les documents d'origine des faits avancés dans cette page : à consulter pour vérifier un chiffre, une définition ou une syntaxe auprès de ses auteurs.
Un log est un événement discret et horodaté : il garde le détail
individuel, là où la métrique agrège
Un log structuré en JSON se filtre par champ ; un texte libre ne se fouille
qu'avec des expressions régulières
Les six champs indispensables : timestamp (UTC, ISO 8601), level,
service, message, trace_id, request_id
Le niveau est un engagement envers l'astreinte : WARN si le service
continue, ERROR si l'opération échoue, FATAL si le processus s'arrête
On ne journalise jamais de secret, d'en-tête d'authentification ni de
donnée personnelle en clair
Le coût additionne ingestion, stockage (volume × rétention) et
requêtage ; le DEBUG permanent en production fait grimper les deux
premiers
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
6 questions
6 min.
70% requis
Informations
Le chronomètre démarre au clic sur Démarrer
Questions à choix multiples, vrai/faux et réponses courtes
Vous pouvez naviguer entre les questions
Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
⏱️--:--
0 / 6
Vérification
(0/0)
--:--
0%
0Correctes
0À revoir
0:00Temps
-Niveau
Score par domaine
Un domaine regroupe plusieurs guides et reçoit plusieurs questions : son score est plus fiable qu'un score par thème, qui porte souvent sur une seule question.
Bilan
Points forts
Aucun sujet réussi à 80 % sur au moins deux questions pour l’instant.