Observabilité et Instrumentation

Instrumente le code pour rendre le comportement en production visible et diagnostiquable. À utiliser lors de l'ajout de logs, métriques, tracing ou alerting, ou lors de la mise en production d'une fonctionnalité nécessitant la preuve de son bon fonctionnement. Utile lorsque des incidents en production sont signalés mais que les données disponibles ne permettent pas d'expliquer ce qui s'est passé.

Qu'est-ce que Observabilité et Instrumentation ?

Cette compétence oriente le développeur ou l'ingénieur de production vers une instrumentation systématique, conditionnant la visibilité opérationnelle à la traçabilité du code en production. Elle explicite comment définir les questions que l'astreinte se posera avant d'instrumenter, puis choisir le signal adapté (log structuré, métrique, trace) à chaque question. Le processus couvre la journalisation structurée (événements stables, IDs de corrélation, champs de point d'entrée), les métriques RED (taux, erreurs, durée) et USE (utilisation, saturation, erreurs) avec mise en garde sur la cardinalité, le tracing distribué via OpenTelemetry, et l'alerting sur symptômes utilisateur plutôt que causes. Elle inclut la rédaction de runbooks minimaux viables et la vérification explicite de la télémétrie elle-même avant la mise en production. Les plateformes cibles sont tous les environnements où le code s'exécute après déploiement (serveurs, lambdas, conteneurs). La compétence s'applique à toute feature qui tourne en production, indépendamment du langage ou du framework, et son exécution est documentée dans le SKILL.md via des listes de vérification et des contre-exemples.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/addyosmani/agent-skills/tree/main/skills/observability-and-instrumentation

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Que fait Observabilité et Instrumentation ?

Vue d'ensemble

Un code que vous ne pouvez pas observer est un code que vous ne pouvez pas exploiter. L'observabilité est la capacité de répondre à « que fait le système et pourquoi ? » de l'extérieur, en utilisant la télémétrie émise par le code. L'instrumentation n'est pas un ajout après le lancement — elle s'écrit en même temps que la fonctionnalité, comme les tests. Si une fonctionnalité est livrée sans télémétrie, le premier bug signalé par un utilisateur devient une fouille archéologique au lieu d'une simple requête.

Quand l'utiliser

  • Construire toute fonctionnalité qui tournera en production
  • Ajouter un nouveau service, endpoint, tâche planifiée ou intégration externe
  • Un incident de production a mis trop de temps à être diagnostiqué (« nous n'avons pas pu déterminer ce qui s'est passé »)
  • Mettre en place ou revoir des règles d'alerting
  • Revoir une PR qui ajoute des E/S, des tentatives, des files d'attente ou des appels inter-services

À NE PAS utiliser pour :

  • Diagnostiquer une défaillance en cours — utilisez la compétence debugging-and-error-recovery (l'observabilité est ce qui rendra cette compétence rapide la prochaine fois)
  • Profiler et optimiser une lenteur mesurée — utilisez la compétence performance-optimization
  • Les listes de contrôle de surveillance le jour du lancement et les déclencheurs de rollback — voir la compétence shipping-and-launch ; cette compétence couvre l'instrumentation qui les alimente

Processus

1. Définir « fonctionne » avant d'instrumenter

De la télémétrie sans question, c'est du bruit. Avant d'ajouter une instrumentation, écrivez 2 à 4 questions qu'un ingénieur d'astreinte se posera à propos de cette fonctionnalité :

FONCTIONNALITÉ : reprise du paiement en caisse
QUESTIONS POUR L'ASTREINTE :
1. Quelle fraction des paiements réussit du premier coup par rapport à après une tentative ?
2. Lorsqu'un paiement échoue définitivement, pourquoi ? (erreur du fournisseur ? délai d'attente ? validation ?)
3. Le fournisseur de paiement est-il plus lent que d'habitude ?
→ Chaque signal ci-dessous doit aider à répondre à l'une de ces questions.

Si vous ne pouvez pas nommer les questions, vous n'êtes pas prêt à instrumenter — vous enregistrerez tout et n'apprendrez rien.

2. Choisir le bon signal pour chaque question

SignalRépond àProfil de coûtExemple
Log structuré« Que s'est-il passé dans ce cas spécifique ? »Par événement ; augmente avec le traficpayment_failed avec le code d'erreur du fournisseur
Métrique« À quelle fréquence / vitesse, en agrégé ? »Fixe par série ; peu coûteux à interrogerlatence p99 des appels au fournisseur
Trace« Où le temps a-t-il été passé entre les services ? »Par requête ; généralement échantillonnéUn paiement lent, décomposé par étape

Règle empirique : les métriques vous disent que quelque chose ne va pas, les traces vous disent où, les logs vous disent pourquoi.

3. Journalisation structurée

Enregistrez des événements, pas des phrases. Chaque ligne de log est un objet JSON avec un nom d'événement stable et des champs lisibles par machine :

// MAUVAIS : interpolation de chaîne — non interrogeable, incohérent
logger.info(`Paiement ${id} échoué pour l'utilisateur ${userId} après ${n} tentatives`);

// BON : nom d'événement stable + champs structurés
logger.warn({
  event: 'payment_failed',
  paymentId: id,
  provider: 'stripe',
  errorCode: err.code,
  attempt: n,
}, 'paiement échoué');

Niveaux de log — utilisez-les de manière cohérente :

NiveauSignificationAction d'astreinte
errorInvariant rompu ; quelqu'un doit peut-être agirEnquêter
warnDégradé mais géré (tentative réussie, solution de repli utilisée)Surveiller les tendances
infoÉvénement métier significatif (commande passée, tâche terminée)Aucune
debugDétail de diagnosticDésactivé en production par défaut

Les IDs de corrélation sont obligatoires. Générez (ou acceptez) un ID de requête à la frontière du système et attachez-le à chaque ligne de log, span et appel sortant. Sans lui, vous ne pouvez pas reconstruire une seule requête à partir de logs entrelacés :

// Express : logger enfant par requête, ID propagé en aval
app.use((req, res, next) => {
  req.id = req.headers['x-request-id'] ?? crypto.randomUUID();
  req.log = logger.child({ requestId: req.id });
  res.setHeader('x-request-id', req.id);
  next();
});

Lorsque plusieurs points d'entrée écrivent dans un même log, nommez le point d'entrée. Un ID de corrélation identifie une exécution ; il ne dit pas quel chemin de code l'a lancée. La même tâche atteinte par un planificateur, un endpoint de rejeu et une CLI manuelle produit des lignes interchangeables dans un même réceptacle : attribuer une ligne revient à éliminer — en recoupant l'historique du planificateur, la table des processus, un journal de déploiement — et cet argument ne tient que tant que ces enregistrements externes existent encore. Estampez le point d'entrée là où l'exécution commence, à côté de l'ID de corrélation, et propagez les deux de la même manière :

// Une seule aide pour chaque point d'entrée : le propre logger de l'exécution porte les deux champs.
// `entryPoint`, pas `source` — ECS réserve `source.*` pour les champs réseau.
export const runLog = (entryPoint: 'scheduler' | 'replay_endpoint' | 'cli', runId: string) =>
  logger.child({ entryPoint, requestId: runId });

// tick du planificateur -> runLog('scheduler', crypto.randomUUID())
// POST /jobs/:id/replay -> runLog('replay_endpoint', req.id)
// invocation CLI        -> runLog('cli', process.env.RUN_ID ?? crypto.randomUUID())

Les deux champs doivent traverser les mêmes frontières que l'ID de corrélation — métadonnées de file d'attente, en-têtes HTTP — sinon un travailleur déduit le point d'entrée et devine. Un champ qui se contente de corréler avec un point d'entrée est un indice, pas une attribution : tout ce qui peut invoquer la tâche peut la reproduire.

N'enregistrez jamais de secrets, jetons, mots de passe ou PII complète. C'est une règle stricte de la compétence security-and-hardening — les pipelines de télémétrie sont une voie classique de fuite de données. Listez les champs autorisés ; n'enregistrez pas les corps de requête entiers.

4. Métriques

Pour les services pilotés par requêtes, instrumentez RED sur chaque endpoint et chaque dépendance externe : Rate (requêtes/s), Erreurs (taux d'échec), Duration (histogramme de latence, pas de moyenne). Pour les ressources (files d'attente, pools, hôtes), utilisez USE : Utilisation, Saturation, Erreurs.

Comme pour le tracing, la voie neutre vis-à-vis du fournisseur est l'API métriques OpenTelemetry (même SDK et contexte que l'étape 5). L'exemple ci-dessous utilise prom-client de Prometheus — un choix de backend courant, pas le seul ; les règles RED/USE et de cardinalité sont identiques dans les deux cas.

import { Histogram } from 'prom-client';

const httpDuration = new Histogram({
  name: 'http_request_duration_seconds',
  help: 'Durée des requêtes HTTP',
  labelNames: ['method', 'route', 'status_class'],  // '2xx', pas '200'
  buckets: [0.05, 0.1, 0.25, 0.5, 1, 2.5, 5],
});

La cardinalité est le mode de défaillance. Chaque combinaison unique d'étiquettes est une série temporelle distincte. Les étiquettes doivent provenir d'ensembles petits et fixes (modèle de route, classe de statut, nom du fournisseur). N'utilisez jamais d'IDs utilisateur, d'URL brutes, de messages d'erreur ou d'autres valeurs non bornées comme étiquettes — cela appartient aux logs et aux traces.

OK comme étiquette :    route="/api/tasks/:id"   status_class="5xx"   provider="stripe"
JAMAIS une étiquette :  user_id, email, request_id, URL complète, texte du message d'erreur

Ne suivez jamais les moyennes, toujours les percentiles : une moyenne cache le 1 % d'utilisateurs qui vivent une mauvaise expérience. Utilisez des histogrammes et lisez p50/p95/p99.

5. Tracing distribué

Utilisez OpenTelemetry — c'est le standard neutre vis-à-vis du fournisseur, et l'auto-instrumentation couvre HTTP, gRPC et les clients DB courants avec quasiment zéro code :

// tracing.ts — doit être importé avant tout le reste
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';

const sdk = new NodeSDK({
  serviceName: 'checkout-service',
  instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();

Ajoutez des spans manuels uniquement autour d'unités de travail internes significatives (par exemple, applyDiscounts, chargeProvider) et attachez les attributs que l'astreinte pourra filtrer. Propagez le contexte à travers chaque frontière asynchrone — en-têtes HTTP, métadonnées de message de file d'attente — sinon la trace meurt à l'écart. Échantillonnez head-based à un faible taux par défaut ; conservez 100 % des erreurs si votre backend supporte l'échantillonnage de queue.

6. Alerting

Déclenchez une alerte sur les symptômes ressentis par les utilisateurs, pas sur les causes :

SYMPTÔME (page justifiée) :              CAUSE (tableau de bord, pas de page) :
taux d'erreur > 1 % pendant 5 min        CPU à 85 %
latence p99 > 2 s                        un pod redémarré
âge de la file d'attente > 10 min        disque à 70 %

Les alertes basées sur les causes se déclenchent quand rien ne va mal et manquent les défaillances que vous n'avez pas prévues. Les alertes basées sur les symptômes se déclenchent exactement quand les utilisateurs sont affectés, quelle que soit la cause.

Règles pour chaque alerte que vous créez :

  1. Elle doit être actionnable. Si la réponse est « ignorez-la, elle se guérit toute seule », supprimez l'alerte.
  2. Elle renvoie à un runbook — même trois lignes : ce que cela signifie, première requête à exécuter, chemin d'escalade.
  3. Elle a un seuil et une durée justifiés par le SLO ou par des données historiques, pas par une estimation.
  4. Utilisez deux niveaux de gravité seulement : page (visible pour l'utilisateur, agissez maintenant) et ticket (dégradation, agissez cette semaine). Un troisième niveau devient du bruit qui habitue les gens à tout ignorer.

Rédaction de runbooks

La règle 2 ci-dessus exige que chaque alerte renvoie à un runbook. Le travail d'un runbook est de répondre à trois questions sans obliger le lecteur à réfléchir : ce qui se passe, quoi vérifier en premier, et qui appeler si cela ne résout pas le problème. Stockez dans docs/runbooks/ nommé d'après l'alerte.

Runbook minimum viable (trois lignes) :

# Runbook : Taux d'erreur élevé sur /api/tasks
**Signification :** Le pool de connexions DB est probablement épuisé, ou un déploiement défectueux.
**Première vérification :** `SELECT count(*) FROM pg_stat_activity WHERE backend_type = 'client backend';`
  — si le nombre > limite du pool, voir l'Étape 2. (Remplacez par l'équivalent pour votre base de données.)
**Escalade à :** #db-oncall ou rotation d'astreinte engineering.

Quand dépasser trois lignes : ajoutez des étapes uniquement lorsque la première vérification ne suffit pas à décider. Un runbook en cinq étapes qui couvre les trois causes les plus courantes est meilleur qu'un document de vingt étapes qui couvre tous les cas extrêmes et qui est parcouru en diagonale.

Maintenez les runbooks à jour. Mettez à jour le runbook en clôturant chaque incident où il a été utilisé — un runbook obsolète crée une fausse confiance. Si une étape était erronée ou manquante, corrigez-la avant de marquer l'incident comme résolu.

7. Vérifier la télémétrie elle-même

L'instrumentation est du code ; elle peut être erronée. Avant de considérer le travail comme terminé, déclenchez les chemins et examinez la sortie réelle :

  • Provoquez une erreur en staging → trouvez-la dans les logs par requestId, confirmez que les champs sont structurés (pas [object Object])
  • Envoyez du trafic de test → confirmez que les séries de métriques apparaissent avec les étiquettes attendues et des valeurs cohérentes
  • Suivez une requête à travers les services dans l'interface de tracing → pas de spans cassés
  • Déclenchez chaque nouvelle alerte une fois (baissez temporairement le seuil) → confirmez qu'elle atteint le bon canal et que le lien vers le runbook fonctionne

Rationalisations courantes

RationalisationRéalité
« J'ajouterai les logs après que ça fonctionne »« Après » devient « après le premier incident », qui est le moment le plus coûteux pour découvrir que vous êtes aveugle. Instrumentez en construisant.
« Plus de logs = plus d'observabilité »Le bruit non structuré rend les incidents plus lents, pas plus rapides. Trois événements interrogeables battent trois cents lignes de prose.
« console.log suffit pour l'instant »La sortie non structurée ne peut être filtrée, corrélée ni alertée. Le logger structuré coûte cinq minutes supplémentaires une fois.
« Nous pouvons simplement regarder les tableaux de bord quand quelque chose se casse »Les tableaux de bord construits sans questions définies vous montrent tout sauf la réponse. Commencez par les questions d'astreinte.
« Alertez sur tout ce qui est important, nous ajusterons plus tard »Un pager bruyant habitue les gens à l'ignorer. L'ajustement n'arrive jamais ; la vraie page manquée, oui.
« L'ID utilisateur comme étiquette de métrique facilite le débogage »Cela fait aussi s'effondrer votre backend de métriques. Les recherches à haute cardinalité appartiennent aux logs et aux traces.
« Le tracing est excessif pour nos deux services »Deux services signifient déjà des questions de latence inter-services que les logs ne peuvent pas répondre. L'auto-instrumentation rend le coût négligeable.

Drapeaux rouges

  • Une PR de fonctionnalité avec des tentatives, des files d'attente ou des appels externes et zéro nouvelle télémétrie
  • Des lignes de log construites par interpolation de chaîne au lieu de champs structurés
  • Absence d'ID de corrélation/requête — chaque ligne de log est un orphelin
  • Un flux de log alimenté par un planificateur, un webhook et des exécutions manuelles, sans champ indiquant lequel a produit la ligne
  • Des métriques étiquetées avec des IDs utilisateur, des URL brutes ou du texte de message d'erreur (bombe de cardinalité)
  • Latence suivie comme une moyenne sans percentiles
  • Alertes qui se déclenchent quotidiennement et sont acquittées sans action
  • Alertes sur les causes (CPU, mémoire) qui pagent des humains alors que le taux d'erreur visible par l'utilisateur n'est pas surveillé
  • Secrets, jetons ou corps de requête complets apparaissant dans les logs
  • « Ça marche sur ma machine » comme seule preuve qu'une fonctionnalité de production est saine

Vérification

Après avoir instrumenté une fonctionnalité, confirmez :

  • Les questions d'astreinte pour cette fonctionnalité sont écrites, et chaque signal en correspond à une
  • Toute la sortie des logs est structurée (JSON), avec des noms d'événement stables et un ID de corrélation sur chaque ligne
  • Chaque réceptacle de log écrit par plus d'un point d'entrée porte un champ de point d'entrée, défini là où l'exécution commence et propagé avec l'ID de corrélation plutôt que déduit en aval
  • Aucun secret, jeton ou PII non expurgé dans aucune ligne de log (vérifiez ponctuellement la sortie réelle)
  • Des métriques RED existent pour chaque nouvel endpoint et chaque dépendance externe, avec des ensembles d'étiquettes bornés
  • La latence est un histogramme ; p95/p99 sont interrogeables
  • Une seule requête peut être suivie de bout en bout dans l'interface de tracing sans spans cassés
  • Chaque nouvelle alerte est basée sur un symptôme, a un lien vers un runbook, et a été testée une fois
  • Une défaillance provoquée en staging a été localisée via la télémétrie seule, sans lire le source

Pour la version condensée de cette liste, incluant la porte d'instrumentation pré-lancement, voir ../../references/observability-checklist.md.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

addyosmani/agent-skills

Builds production-quality, accessible, responsive user-facing UIs. Use when building or modifying interfaces and pages, creating components, implementing layouts, meeting WCAG accessibility requirements, managing state, or when the output needs to look and feel production-quality rather than AI-generated.

addyosmani/agent-skills

Prepares production launches. Use when preparing to deploy to production, or when asking what needs to be in place before shipping. Use when you need a pre-launch checklist, when setting up monitoring, when planning a staged rollout, or when you need a rollback strategy.

addyosmani/agent-skills

Guides stable API and interface design. Use when designing APIs, module boundaries, or any public interface. Use when creating REST or GraphQL endpoints, defining type contracts between modules, or establishing boundaries between frontend and backend.

addyosmani/browser-testing-with-devtools

Tests in real browsers via Chrome DevTools MCP. Use when building or debugging anything that runs in a browser. Use when you need to inspect the DOM, capture console errors, analyze network requests, profile performance, or verify visual output with real runtime data. Requires the chrome-devtools MCP server to be configured.

addyosmani/ci-cd-and-automation

Automates CI/CD pipeline setup. Use when setting up or modifying build and deployment pipelines. Use when you need to automate quality gates, configure test runners in CI, or establish deployment strategies.

addyosmani/code-review-and-quality

Conducts multi-axis code review. Use before merging any change. Use when reviewing code written by yourself, another agent, or a human. Use when you need to assess code quality across multiple dimensions before it enters the main branch.

addyosmani/code-simplification

Simplifies code for clarity. Use when refactoring code for clarity without changing behavior. Use when code works but is harder to read, maintain, or extend than it should be. Use when reviewing code that has accumulated unnecessary complexity.

addyosmani/constraint-driven-development

Establishes a project's quality bar as a written contract and stops agents quietly lowering it. Interviews the user on which dimensions matter, supplies sane default thresholds when they have no number in mind, records everything in CONSTRAINTS.md, and watches the diff for a weakened bar — new @ts-ignore or eslint-disable suppressions, skipped or deleted tests, assertions stripped out, unimplemented stubs, thresholds edited down. Use when no quality bar is written down, when the user says "set up constraints" or "define our standards", when the user wants dimensions they care about — accessibility, web performance, coverage — set up as enforced constraints, when an agent keeps silencing checks or skipping tests to get to green, when you need a coverage or performance threshold and don't know what number to pick, or when an agent writes more code than anyone will read.

addyosmani/debugging-and-error-recovery

Guides systematic root-cause debugging. Use when tests fail, builds break, behavior doesn't match expectations, or you encounter any unexpected error. Use when you need a systematic approach to finding and fixing the root cause rather than guessing.

addyosmani/deprecation-and-migration

Manages deprecation and migration. Use when removing old systems, APIs, or features. Use when migrating users from one implementation to another. Use when deciding whether to maintain or sunset existing code.

addyosmani/documentation-and-adrs

Records decisions and documentation. Use when making architectural decisions, changing public APIs, shipping features, or when you need to record context that future engineers and agents will need to understand the codebase.

addyosmani/doubt-driven-development

Subjects every non-trivial decision to a fresh-context adversarial review before it stands. Use when correctness matters more than speed, when working in unfamiliar code, when stakes are high (production, security-sensitive logic, irreversible operations), or any time a confident output would be cheaper to verify now than to debug later.

addyosmani/git-workflow-and-versioning

Structures git workflow practices. Use when making any code change. Use when committing, branching, resolving conflicts, or when you need to organize work across multiple parallel streams.

addyosmani/idea-refine

Refines raw ideas into sharp, actionable concepts through structured divergent and convergent thinking. Use when an idea is still vague, when you need to stress-test assumptions before committing to a plan, or when you want to expand options before converging on one. Triggers on "ideate", "refine this idea", or "stress-test my plan".

addyosmani/incremental-implementation

Delivers changes incrementally. Use when implementing any feature or change that touches more than one file. Use when you're about to write a large amount of code at once, or when a task feels too big to land in one step.

addyosmani/interview-me

Extracts what the user actually wants instead of what they think they should want. Achieves this through one-question-at-a-time interview until ~95% confidence about the underlying intent. Use when an ask is underspecified ("build me X" without "for whom" or "why now"), when the user explicitly invokes ("interview me", "grill me", "are we sure?", "stress-test my thinking"), or when you catch yourself silently filling in ambiguous requirements before any plan, spec, or code exists.

addyosmani/performance-optimization

Optimizes application performance. Use when performance requirements exist, when you suspect performance regressions, or when Core Web Vitals or load times need improvement. Use when profiling reveals bottlenecks that need fixing.

addyosmani/planning-and-task-breakdown

Breaks work into ordered tasks. Use when you have a spec or clear requirements and need to break work into implementable tasks. Use when a task feels too large to start, when you need to estimate scope, or when parallel work is possible.

addyosmani/security-and-hardening

Hardens code against vulnerabilities. Use when handling user input, authentication, data storage, or external integrations. Use when building any feature that accepts untrusted data, manages user sessions, or interacts with third-party services.

addyosmani/source-driven-development

Grounds every implementation decision in official documentation. Use when you want authoritative, source-cited code free from outdated patterns. Use when building with any framework or library where correctness matters.

Skills associés