Esc
 Naviguer  Ouvrir Esc Fermer
Aller au contenu

14.5 — Observabilité

🎯 Objectif : ne plus apprendre une panne par un client mécontent. Mettre en place les 3 piliers (logs, métriques, traces), définir des SLO réalistes, et construire des alertes qui réveillent uniquement quand il faut.

À l'issue de cet axe, tu sauras :

  • Différencier monitoring et observabilité
  • Implémenter les 3 piliers : logs structurés, métriques, traces distribuées
  • Choisir entre OSS (Prometheus/Loki/Tempo) et SaaS (Datadog, New Relic, Honeycomb)
  • Définir des SLI / SLO mesurables avec un error budget
  • Construire des alertes qui n'usent pas l'astreinte

Confirmé 11 min prérequis : axes 4 et 8 lus

ConceptDéfinition
MonitoringSurveiller des métriques prédéfinies (CPU, RAM, latence, erreurs)
ObservabilitéPouvoir poser une question nouvelle sur le système sans redéployer

Monitoring répond aux questions que tu savais poser. Observabilité te permet de répondre à celles que tu ne savais pas poser.

Exemple concret. À 3h du matin, p95 latence explose sur /checkout. Avec du monitoring : tu vois la métrique. Avec de l’observabilité : tu peux filtrer par tenant, par version, par région, voir la stack trace, et trouver que c’est seulement le tenant ACME en région eu-west-3 sur la version v1.2.3 qui souffre.


flowchart LR
    A[Une requête<br/>en prod] --> Logs[Logs<br/>Que s'est-il passé exactement ?]
    A --> Metrics[Métriques<br/>Combien et à quel rythme ?]
    A --> Traces[Traces<br/>Où va le temps ?]
Logs, métriques, traces — chacun répond à une question différente

Texte (ou JSON) émis par l’app à chaque événement.

Bon log :

{
"ts": "2026-04-30T14:23:11.123Z",
"level": "error",
"service": "checkout-api",
"trace_id": "abc-123",
"user_id": 4567,
"tenant": "acme",
"event": "payment.failed",
"amount": 49.90,
"currency": "EUR",
"error": "stripe.card_declined",
"stripe_request_id": "req_xyz"
}

Mauvais log :

Erreur paiement

Règles :

RèglePourquoi
JSON structuréGreppable, agrégeable, filtre par champ
Inclure trace_idLier au tracing distribué
Niveau : debug / info / warn / errorFiltrer en prod
Pas de PII en clairRGPD — masquer email, IBAN, etc.
Pas de passwordÉvident, mais arrive
Volume mesuré100 logs/req × 10k req/s × 30 j = facture explosive

Stacks courantes :

StackPour qui
Loki + GrafanaOSS, pay-per-volume modeste
OpenSearch / Elasticsearch + KibanaPlus puissant, plus lourd
Datadog LogsCher, intégration totale
Vector ou Fluent BitAgents qui collectent et routent
Better Stack / LogtailSaaS très simple, prix correct

Séries temporelles numériques agrégées : http_requests_total{status="500"}.

Time Value
14:00 1234
14:01 1289
14:02 1345
Type PrometheusUsage
CounterCumulatif (jamais décroît). requests_total
GaugeValeur instantanée. memory_used_bytes
HistogramDistribution avec buckets. request_duration_seconds
SummaryQuantiles côté client. Préférer Histogram en pratique
// Exposition d'un compteur depuis Hono / Express
import { register, Counter, Histogram } from 'prom-client';
const httpRequests = new Counter({
name: 'http_requests_total',
help: 'Nombre total de requêtes HTTP',
labelNames: ['method', 'route', 'status'],
});
const httpDuration = new Histogram({
name: 'http_request_duration_seconds',
help: 'Durée des requêtes HTTP',
labelNames: ['method', 'route', 'status'],
buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5],
});
app.use('*', async (c, next) => {
const end = httpDuration.startTimer();
await next();
const labels = { method: c.req.method, route: c.req.routePath, status: c.res.status };
httpRequests.inc(labels);
end(labels);
});
app.get('/metrics', async (c) => {
c.header('Content-Type', register.contentType);
return c.body(await register.metrics());
});

Stack standard :

App → /metrics endpoint → Prometheus scrape (15s) → Grafana dashboards

Une trace suit une requête à travers plusieurs services. Chaque opération est un span avec parent / enfant.

GET /checkout (842 ms) ─┐
├─ db.query (find_user) (12 ms) │
├─ http.fetch stripe.com /charges (210 ms) │
├─ db.transaction (480 ms) │
│ ├─ db.query (insert_order) (15 ms) │
│ └─ db.query (decrement_stock × 4) (435 ms) │ ← 4 N+1
└─ redis.set (8 ms) │

OpenTelemetry est le standard 2026 — neutre, supporté partout (Datadog, Honeycomb, Grafana Tempo, Jaeger, Sentry).

// Instrumentation auto-magique avec OTel SDK Node
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
new NodeSDK({
serviceName: 'checkout-api',
traceExporter: new OTLPTraceExporter({ url: 'http://otel-collector:4318/v1/traces' }),
instrumentations: [getNodeAutoInstrumentations()],
}).start();

Tu obtiens gratuitement : HTTP, fetch, fs, pg, mysql, redis, prisma instrumentés.


┌─────────┐ OTLP ┌─────────────────┐ push ┌────────────┐
│ App │ ─────────> │ OTel Collector │ ─────────> │ Backend │
│ (SDK) │ │ (relai) │ │ (Tempo, │
└─────────┘ └─────────────────┘ │ Honeycomb)│
└────────────┘

Le Collector est un proxy qui :

  • reçoit les signaux des apps (OTLP),
  • enrichit / filtre / sample,
  • exporte vers un ou plusieurs backends.

Avantage : changer de backend sans toucher l’app. Tu pointes le Collector ailleurs.


Sentry n’est pas redondant avec les logs : il agrège, déduplique, lie au commit, alerte. Plug & play pour 90 % des stacks.

import * as Sentry from '@sentry/node';
Sentry.init({
dsn: process.env.SENTRY_DSN!,
environment: process.env.NODE_ENV,
release: process.env.GIT_SHA,
tracesSampleRate: 0.1,
profilesSampleRate: 0.1,
});

Bonus : Source maps uploadés en CI → la stack trace pointe ton TypeScript original, pas un bundle minifié.


SigleDéfinitionExemple
SLI (Indicator)Une mesure technique réelle« Latence p95 de /api/checkout »
SLO (Objective)Cible interne sur un SLI« p95 < 300 ms sur 30 jours »
SLA (Agreement)Engagement contractuel client + sanctions« Disponibilité ≥ 99,9 % ou avoir 5 % du mois »

Toujours : SLA > SLO > SLI réel. Sinon tu paies des pénalités.

Disponibilité = (req sans erreur 5xx) / (req totales)
Latence p95 = 95% des req plus rapides que X
Taux d'erreur = (req 4xx + 5xx) / (req totales)
Saturation = ressource en %, > 80 % = saturé

Si ton SLO est 99,9 % de dispo sur 30 jours, ton « budget d’erreur » est :

30 jours × 24h × 60min × 0,1 % = 43,2 minutes/mois autorisées

Une fois consommé : on gèle les déploiements et on stabilise. Une fois préservé : on peut prendre plus de risques (déployer plus souvent, faire du chaos engineering).

C’est l’idée centrale du Site Reliability Engineering : la fiabilité n’est pas absolue — c’est un compromis vélocité/stabilité piloté par un budget.


Un humain réveillé pour une fausse alerte à 3h du matin :

  • Râle.
  • Met l’alerte en mute « pour cette nuit ».
  • Oublie le mute.
  • L’alerte vraiment critique passe inaperçue 6 mois plus tard.
  1. Alerter sur des symptômes, pas des causes. « Latence p95 > 500 ms » > « CPU > 80 % ». L’utilisateur s’en fout du CPU.
  2. Toute alerte doit avoir un runbook : quoi vérifier, qui appeler, comment mitiger.
  3. Si tu ne ferais rien à 3h du matin, ce n’est pas une alerte critique. Met-la en warning / Slack.
NiveauQuandNotification
CritiqueSLO en danger, impact client massifPager (PagerDuty, OnCall, Better Stack) → réveil
HauteRisque de devenir critique sous 1-2 hSlack haut prio + email
InfoDrift, anomalie sans impactSlack low prio, ou ticket

Pour éviter de paniquer sur un blip court tout en réagissant aux dégradations soutenues :

FenêtreBurn rateAction
5 min14× le rythme acceptablePage critique
1 h6× le rythme acceptablePage critique
6 h3× le rythme acceptableNotif équipe
3 j1× le rythme acceptableTicket dette

Si plusieurs fenêtres dépassent en même temps, on est sûr que ce n’est pas un faux positif.


LettreSens
R RateRequêtes/s
E ErrorsTaux d’erreur
D DurationLatence (p50, p95, p99)
LettreSens
U Utilization% d’usage
S SaturationFile d’attente, contention
E ErrorsErreurs hardware / driver

Un bon dashboard tient sur un écran, montre l’essentiel, et ne prétend pas tout dire.


SolutionCoût directCoût humain
Prometheus + Grafana + Loki + Tempo (OSS)Très basTu maintiens, tu scales
Datadog / New RelicCher (€/host ou €/log GB)Très bas
HoneycombCher mais excellent pour tracesBas
Better StackModéré, all-in-oneBas
Cloud-native (CloudWatch, GCP Ops)ModéréLock-in cloud
Grafana CloudFree tier généreux + payantBas, OSS-friendly

La plupart des startups partent en Grafana Cloud free tier ou Better Stack : on a tout en quelques heures pour un coût modeste, on migrera vers du self-host quand le coût SaaS dépasse le coût humain.


PratiquePourquoi
Rotation hebdo entre N personnes (N ≥ 4)Personne ne brûle
On-call comp (prime ou jours de repos compensateurs)Reconnaissance + légalité
Runbooks par alerteLe réveillé sait quoi faire sans réfléchir
Post-mortem blameless systématiqueApprentissage > culpabilisation
Post-mortem → actions concrètesSinon c’est de la littérature
Métriques : MTTR, MTBF, fréquence d’incidentsPour piloter l’amélioration
Escalation path clairÀ qui appeler après 30 min de blocage

« An on-call shift you wouldn’t take yourself is one you shouldn’t ask anyone else to take. » — Honeycomb


Tu mets en place une alerte qui pingue PagerDuty à chaque fois que CPU > 80 %. Au bout d'un mois, ton équipe l'a mute. Pourquoi ?
Ton SLO est 99,9 % de dispo. À quoi sert l'error budget ?
Tu choisis OpenTelemetry plutôt que un agent propriétaire (Datadog, New Relic). Quelle est l'avantage principal ?
Sur 1 mois, tu cumules 200 alertes critiques sur PagerDuty dont 192 fausses (« flap » d'un service qui rebascule). Conséquence ?

  • Site Reliability Engineering — Google (gratuit en ligne)
  • The SRE Workbook — Google (suite pratique)
  • Observability Engineering — Charity Majors et al. (Honeycomb)
  • OpenTelemetry docsopentelemetry.io
  • Prometheus / Grafana / Loki / Tempografana.com/oss
  • Sentry docsdocs.sentry.io
  • PagerDuty Incident Response — guide gratuit

Si ton backend appelle des LLM (Claude, OpenAI, Mistral), les métriques classiques ne suffisent pas. Tu dois en plus tracer :

  • Tokens consommés par requête (prompt + completion)
  • Latence par modèle (Claude Sonnet 4.6 vs GPT-5 vs Llama local)
  • Coût €/requête
  • Qualité de réponse (golden set evaluation)
  • Hallucinations (citations inventées, refus calibrés)

→ Voir 16.4 — IA appliquée pour les outils dédiés (Langfuse, promptfoo, OpenLLMetry) et les patterns d’instrumentation.


Suite : 14.6 — Fiabilité pour les sauvegardes, runbooks et plans de reprise.