Du monitoring à l'observabilité
Ton serveur est en feu. Tu regardes les dashboards, tout est vert. C'est le problème.
Pendant longtemps, "surveiller son infrastructure" voulait dire : définir des seuils, configurer des alertes, regarder des courbes de CPU. C'est le monitoring. Ça répond à une question : est-ce que ça marche ?
Le problème, c'est que cette question est trop simple pour les systèmes modernes. Une application distribuée peut présenter des latences anormales sans qu'aucun seuil ne soit dépassé. Un service peut fonctionner parfaitement pendant que ses clients reçoivent des erreurs. Tu peux avoir 99 % de disponibilité et une expérience utilisateur catastrophique.
Capacité à inférer l'état interne d'un système uniquement à partir de ses sorties externes. Un système observable répond non seulement à "ça marche ?" mais aussi à "pourquoi ça ne marche pas ?" — même pour des problèmes que tu n'avais pas anticipés.
Le terme vient de la théorie du contrôle : un système est observable si son état interne peut être entièrement déterminé depuis ses sorties. En informatique, Charity Majors (Honeycomb) l'a popularisé en 2016 pour décrire ce dont les systèmes distribués ont besoin.
Les trois signaux
L'observabilité repose sur trois types de données que ton système produit en permanence. OpenTelemetry — le standard ouvert de la CNCF — les appelle les signals :
Chaque signal répond à une question différente. Ensemble, corrélés via un trace_id commun,
ils permettent de comprendre n'importe quel comportement — y compris ceux que tu n'avais pas prévus.
Ce que ce cours couvre
Dans ce cours, tu vas comprendre le rôle de chaque signal, leurs forces et leurs limites, comment les corréler efficacement, les méthodes de mesure (RED, USE, Golden Signals), et une vue d'ensemble du stack open source qui s'est imposé : LGTM (Loki, Grafana, Tempo, Mimir).
Les logs : le journal du système
La forme la plus ancienne d'observabilité — et la plus mal utilisée.
Un log est un événement horodaté émis par ton application. C'est le premier réflexe de tout développeur :
console.log(), error_log(), logger->info(). Simple, direct, immédiat.
Le problème des logs texte
La plupart des applications produisent encore des logs comme ça :
# Format texte traditionnel
[2024-01-15 14:32:01] app.INFO: User 42 logged in from 192.168.1.5
[2024-01-15 14:32:02] app.ERROR: Payment failed for order #1337 amount=59.99
Ces logs sont lisibles pour un humain mais difficiles à requêter. Pour extraire "tous les paiements échoués supérieurs à 50€ en janvier", tu dois parser du texte libre avec des regex fragiles.
Les logs structurés
La solution : émettre des logs en JSON avec des champs typés et cohérents. Chaque champ devient une colonne que tu peux filtrer, agréger, corréler.
{
"timestamp": "2024-01-15T14:32:02Z",
"level": "error",
"service": "payment-service",
"message": "Payment failed",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"span_id": "00f067aa0ba902b7",
"order_id": 1337,
"amount": 59.99,
"error": "card_declined",
"user_id": 42
}
trace_id et span_id sont la clé de la corrélation.
Ils permettent de relier ce log à la trace OpenTelemetry correspondante — et donc de voir
exactement où dans le chemin d'exécution l'erreur s'est produite.
Les niveaux de log
| Niveau | Usage | Exemple |
|---|---|---|
DEBUG | Diagnostic détaillé — production désactivée | Valeur de variable intermédiaire |
INFO | Événement normal attendu | Utilisateur connecté, commande créée |
WARNING | Situation anormale mais récupérable | Retry réussi après échec, config manquante avec défaut |
ERROR | Échec d'une opération — intervention possible | Paiement rejeté, service tiers indisponible |
CRITICAL | Échec système — intervention immédiate | Base de données inaccessible, OOM |
Ce que les logs font bien — et ce qu'ils ne font pas
✅ Points forts
Contexte riche et arbitraire. Utiles pour déboguer un cas précis. Faciles à émettre. Conservés longtemps. Idéaux pour les audits.
❌ Limites
Volume énorme en production. Difficiles à agréger (pas de tendances). Coûteux à stocker. Pas adaptés aux questions "combien ?" ou "depuis quand ?".
Les métriques : la température du système
Des chiffres agrégés dans le temps — parfaits pour détecter, impuissants pour expliquer.
Une métrique est une mesure numérique agrégée dans le temps. Contrairement aux logs qui capturent des événements individuels, les métriques répondent à des questions statistiques : combien ?, à quelle vitesse ?, quel est le 99e percentile ?
Imagine un thermomètre dans ta maison. Il ne te dit pas pourquoi il fait chaud, mais il te dit qu'il fait chaud, depuis combien de temps, et à quelle vitesse la température monte. Les métriques sont ce thermomètre.
Les outils de collecte de métriques
Plusieurs outils existent pour collecter et stocker des métriques. Chacun a son modèle de données, son protocole d'ingestion et ses forces :
| Outil | Modèle | Particularité |
|---|---|---|
| Prometheus | OpenMetrics (pull) | Standard de facto cloud-native. Kubernetes natif. Écosystème d'exporters massif. |
| Datadog | Propriétaire (push) | SaaS tout-en-un. Corrélation logs/traces/métriques intégrée. Payant. |
| InfluxDB | Time-series (push) | Très haute fréquence d'écriture. Langage Flux. Stockage optimisé. |
| StatsD | UDP (push) | Ultra-léger, protocole texte. Agrège côté serveur avant envoi. |
Les quatre types du standard OpenMetrics
OpenMetrics définit quatre types fondamentaux, implémentés par Prometheus et alignés avec le modèle OpenTelemetry. D'autres outils ont des équivalents proches, parfois sous des noms différents.
| Type | Description | Exemple |
|---|---|---|
| Counter | Valeur qui ne fait qu'augmenter. Se remet à zéro au redémarrage. | http_requests_total, errors_total |
| Gauge | Valeur qui peut monter ou descendre à n'importe quel moment. | memory_usage_bytes, queue_size |
| Histogram | Distribution en buckets préconfigurés. Permet les percentiles. | http_request_duration_seconds{le="0.5"} |
| Summary | Percentiles calculés côté client. Moins flexible, éviter en général. | rpc_duration_seconds{quantile="0.99"} |
Labels et cardinalité
Les métriques ont des labels (ou dimensions) qui permettent de filtrer et d'agréger. C'est là que réside le piège principal.
# Bonne cardinalité : 3 méthodes × 5 codes = 15 séries
http_requests_total{method="GET", status="200"} 1423
http_requests_total{method="POST", status="201"} 89
http_requests_total{method="POST", status="422"} 12
# Cardinalité explosive : 1 série par user_id → des millions de séries
http_requests_total{user_id="42"} 7 ← NE JAMAIS FAIRE
Le nombre de combinaisons uniques de labels pour une métrique donnée. Une cardinalité élevée crée des millions de séries temporelles — Prometheus explose en mémoire, mais le problème existe dans tous les outils : Datadog facture par série, InfluxDB ralentit. La règle universelle : les labels doivent avoir un ensemble fini et petit de valeurs (statut HTTP, méthode, service, environnement — pas user_id, pas URL complète).
Ce que les métriques font bien — et ce qu'elles ne font pas
✅ Points forts
Légères et compressibles. Idéales pour alerter. Permettent les tendances et corrélations temporelles. Peu coûteuses à stocker longtemps. Parfaites pour les dashboards.
❌ Limites
Aucun contexte sur un événement précis. "Le p99 a monté" ne dit pas quelle requête est concernée. Nécessitent de l'instrumentation préalable — tu ne peux pas poser une question à laquelle tu n'avais pas pensé.
Les traces : le fil d'Ariane
Quand une requête traverse dix services, une trace te montre exactement où le temps est passé.
Imagine que tu commandes un colis en ligne. Le suivi te dit : "reçu entrepôt Marseille → tri Lyon → livraison Paris → livré". C'est une trace : le chemin complet d'un événement à travers plusieurs étapes, avec le temps passé à chaque étape. Les traces distribuées, c'est exactement ça — mais pour une requête HTTP.
Anatomie d'une trace
Représentation du chemin complet d'une requête à travers un système distribué, de son entrée jusqu'à sa réponse finale. Identifiée par un
trace_id unique (128 bits).
Unité de travail au sein d'une trace. Chaque appel de service, chaque requête SQL, chaque appel HTTP crée un span. Un span a un
span_id, un parent, un début, une durée,
un statut, et des attributs arbitraires.
// En-tête W3C TraceContext propagé entre services
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
// version-{trace_id 32hex}-{parent_span_id 16hex}-{flags}
// flags: 01 = sampled, 00 = not sampled
Visualisation d'une trace : le diagramme de Gantt
Une trace est visualisée comme un diagramme de Gantt imbriqué, où chaque span est une barre dont la longueur représente la durée. L'imbrication représente la hiérarchie parent/enfant.
Trace ID: 4bf92f35... Total: 142ms
├─ api-gateway GET /orders/42 [0ms ──────────────────── 142ms]
│ ├─ auth-service ValidateToken [5ms ───── 18ms]
│ └─ orders-service GetOrder [20ms ──────────────── 142ms]
│ ├─ postgres SELECT * FROM orders WHERE [22ms ──── 35ms]
│ └─ postgres SELECT * FROM order_items [38ms ──── 98ms] ← le goulot
Sampling : on ne trace pas tout
En production, tracer 100 % des requêtes est trop coûteux. Le sampling réduit le volume. Deux approches existent :
| Stratégie | Décision | Avantage | Inconvénient |
|---|---|---|---|
| Head-based | À l'entrée de la trace (premier service) | Simple, pas de buffering | Peut rater les erreurs rares |
| Tail-based | En fin de trace (après collecte complète) | Peut garder 100 % des erreurs | Nécessite un collecteur avec buffer |
Ce que les traces font bien — et ce qu'elles ne font pas
✅ Points forts
Montrent exactement où le temps est passé. Révèlent les dépendances cachées entre services. Indispensables pour déboguer des latences dans les architectures distribuées.
❌ Limites
Nécessitent une instrumentation dans chaque service. Volumineuses — le sampling est obligatoire. Ne montrent pas les tendances dans le temps (pour ça, il faut les métriques).
Corrélation : les 3 piliers ensemble
Un trace_id dans tes logs, c'est la différence entre une heure de debug et dix minutes.
Chaque pilier pris seul est limité. Une alerte de métrique te dit "quelque chose va mal", mais pas quoi. Un log d'erreur te dit quoi, mais pas dans quel contexte global. Une trace te dit le chemin, mais tu ne vois pas les tendances dans le temps.
La puissance de l'observabilité vient de la corrélation entre les trois signaux.
Le lien, c'est le trace_id : un identifiant unique généré à l'entrée du système,
propagé à travers tous les services, et injecté dans les logs, les traces, et les métriques exemplaires.
Le workflow d'investigation
- La métrique alerte : le p99 de latence dépasse 500ms sur
orders-service. - La trace identifie : tu trouves les traces lentes, tu vois que 80% du temps est sur un span
postgres. - Les logs expliquent : tu ouvres les logs de ce span précis et tu vois
slow query: missing index on orders.user_id.
trace_id. C'est ce qu'on appelle le context switching —
tu ne changes pas d'outil, tu changes de vue sur le même événement.
Simulateur : observe une requête distribuée
Clique sur "Simuler une requête" pour voir comment les trois signaux sont générés et corrélés.
Lance la simulation pour voir les logs structurés.
Lance la simulation pour voir la trace.
Observe le trace_id commun dans les logs et la trace — c'est lui qui permet de naviguer entre les signaux.
RED, USE & Golden Signals : quoi mesurer
Observer tout est impossible. Ces trois frameworks te disent ce qui compte vraiment.
L'instrumentation naïve produit des centaines de métriques inutiles et rate les signaux importants. Trois frameworks ont émergé pour répondre à la question : qu'est-ce qu'on mesure vraiment ?
La méthode RED — pour les services
La méthode RED a été proposée par Tom Wilkie chez Weave Works (aujourd'hui Grafana Labs) pour les microservices. Elle dit : pour chaque service, instrumente trois métriques.
| Lettre | Signification | Métrique Prometheus |
|---|---|---|
| R — Rate | Nombre de requêtes par seconde traitées | rate(http_requests_total[5m]) |
| E — Errors | Taux de requêtes en erreur (4xx/5xx) | rate(http_requests_total{status=~"5.."}[5m]) |
| D — Duration | Distribution des latences (p50, p95, p99) | histogram_quantile(0.99, ...) |
La méthode USE — pour les ressources
La méthode USE a été définie par Brendan Gregg (Netflix) pour les ressources physiques et logiques : CPU, mémoire, disque, réseau, connexions de pool.
| Lettre | Signification | Exemple |
|---|---|---|
| U — Utilization | Pourcentage de temps où la ressource est occupée | CPU à 75 %, pool de connexions à 60/100 |
| S — Saturation | Quantité de travail en attente (queue) | Load average, longueur de la queue DB |
| E — Errors | Erreurs matérielles ou logiques de la ressource | Erreurs réseau, pages mémoire incorrectes |
Les Golden Signals de Google SRE
Le livre "Site Reliability Engineering" de Google définit quatre signaux dorés que tout service exposé aux utilisateurs devrait surveiller :
Latency : temps pour servir une requête — distinguer succès et erreurs (une erreur rapide ne compte pas comme "bonne latence"). Traffic : charge — requêtes/sec, transactions/sec. Errors : taux d'échecs explicites (5xx) et implicites (200 avec contenu incorrect). Saturation : à quel point la ressource la plus contrainte est proche de la limite.
RED = Rate + Errors + Duration ≈ Traffic + Errors + Latency des Golden Signals. Les Golden Signals ajoutent la Saturation, ce qui les rend plus complets. En pratique, les deux sont utilisés et se complètent.
Le stack LGTM : l'outillage open source
Loki, Grafana, Tempo, Mimir — comment les quatre composants s'articulent.
L'écosystème de l'observabilité open source a convergé autour d'un stack cohérent porté par Grafana Labs. Chaque composant correspond à un signal.
| Composant | Rôle | Alternative |
|---|---|---|
| Loki | Stockage et requêtage de logs. Indexe uniquement les labels (pas le contenu) — compact et rapide. | Elasticsearch, OpenSearch |
| Grafana | Visualisation unifiée. Datasources multiples (Prometheus, Loki, Tempo, Mimir). Alerting. OnCall. | Kibana (ELK), Datadog |
| Tempo | Stockage de traces compatible OpenTelemetry, Jaeger, Zipkin. Requêtage par trace_id ou TraceQL. | Jaeger, Zipkin |
| Mimir | Prometheus à l'échelle — stockage long terme, multi-tenant, compatible API Prometheus. | Prometheus seul, Thanos, VictoriaMetrics |
Le collecteur universel : OpenTelemetry Collector
En pratique, tes applications n'envoient pas directement à Loki/Tempo/Mimir. Elles envoient au OpenTelemetry Collector (otelcol), qui reçoit tous les signaux, les transforme si nécessaire, et les route vers les backends appropriés.
# Pipeline OTel Collector (otelcol-config.yaml)
receivers:
otlp:
protocols: {grpc: {endpoint: "0.0.0.0:4317"}, http: {endpoint: "0.0.0.0:4318"}}
exporters:
loki: {endpoint: "http://loki:3100/loki/api/v1/push"}
prometheusremotewrite: {endpoint: "http://mimir:9090/api/v1/push"}
otlp/tempo: {endpoint: "http://tempo:4317"}
service:
pipelines:
logs: {receivers: [otlp], exporters: [loki]}
metrics: {receivers: [otlp], exporters: [prometheusremotewrite]}
traces: {receivers: [otlp], exporters: [otlp/tempo]}
Alternatives SaaS
Si tu ne veux pas opérer l'infrastructure toi-même, les alternatives managées couvrent le même besoin : Datadog (leader commercial, très complet), New Relic (APM historique, OpenTelemetry natif), Grafana Cloud (stack LGTM hébergé, free tier généreux), Elastic APM (si tu as déjà un cluster Elasticsearch).
Synthèse : construire un système observable
Ce qu'il faut instrumenter en priorité, les pièges à éviter, la feuille de route.
Les trois signaux en un coup d'œil
| Signal | Question à laquelle il répond | Forces | Limites |
|---|---|---|---|
| Logs | "Que s'est-il passé exactement ?" | Contexte riche, arbitraire, facile à émettre | Volume massif, difficile à agréger |
| Métriques | "Quel est l'état du système dans le temps ?" | Légères, alerting, tendances, dashboards | Aucun contexte événementiel, cardinalité à maîtriser |
| Traces | "Où le temps a-t-il été passé dans ce chemin ?" | Révèle les goulots, cartographie les dépendances | Instrumentation nécessaire, sampling obligatoire |
Guide de priorités
Si tu pars de zéro, voici l'ordre d'instrumentation recommandé :
- Métriques RED sur chaque service HTTP — rate, errors, duration. Minimum vital pour alerter.
- Logs structurés avec trace_id — même sans traces actives, le champ te servira plus tard.
- Traces sur les chemins critiques — les requêtes qui touchent plusieurs services ou la BD.
- Métriques USE sur les ressources — une fois RED en place, ajoute CPU, mémoire, pools.
- Corrélation dans Grafana — configure les derived fields Loki → Tempo, et les exemplars Prometheus → Tempo.