Advanced (Mis à jour: 02/06/2026)

Logging et monitoring avec Claude Code : guide d'observabilité

Concevoir logs structurés, OpenTelemetry, alertes et handoff d'incident avec Claude Code.

Logging et monitoring avec Claude Code : guide d'observabilité

Commencer par le contrat d’exploitation

Si vous demandez simplement à Claude Code “ajoute des logs”, vous obtiendrez souvent quelques console.log de plus. C’est utile en local, mais insuffisant pendant un incident de production. Une vraie base de logging et de monitoring doit expliquer quelle action utilisateur a lancé la requête, quel requestId la suit, quel service ralentit, quelle dépendance échoue et quelles données peuvent être partagées sans risque.

Le primer Observability d’OpenTelemetry présente l’observabilité comme la capacité à comprendre un système depuis l’extérieur et à répondre à des questions nouvelles. La page What is OpenTelemetry? rappelle aussi qu’OpenTelemetry est une couche d’instrumentation neutre, pas le backend d’observabilité lui-même. Cet article traduit cette idée en prompts Claude Code, extraits copiables, règles d’alerte, revue de dashboard et modèle de transmission d’incident.

Masa a testé ce flux sur une petite API de paiement. Le premier prompt disait seulement “rends les logs plus détaillés”. Claude Code a proposé de journaliser le corps complet de la requête en cas d’échec de paiement. Il n’y avait pas de numéro de carte, mais il pouvait y avoir email, coupon, adresse et note client. En commençant plutôt par les champs interdits, requestId, traceparent et les tests de redaction, la revue est devenue beaucoup plus concrète.

SignalQuestionContrainte pour Claude Code
LogsQue s’est-il passéJSON, champs fixes, masquage PII
MétriquesQuelle est la gravitérate, p95, taux d’erreur
TracesOù le temps est partipropager traceparent, nommer les spans
Health checksUne dépendance est-elle utilisablestatut et latency par dépendance

Prompt sûr et règles CLAUDE.md

La documentation de mémoire de Claude Code indique que CLAUDE.md fournit des instructions de projet, d’utilisateur ou d’organisation au début des sessions. Pour l’observabilité, placez-y les niveaux de log, noms de champs, données interdites, commandes de test, dashboards et format de handoff. Côté site, relisez les bonnes pratiques CLAUDE.md. Côté officiel, gardez les pages Claude Code permissions et hooks ouvertes.

Claude Code task:
- Add observability to the checkout API only.
- Keep all changes inside src/checkout and tests/checkout.
- Use structured JSON logs with requestId and traceparent.
- Never log passwords, tokens, cookies, email, phone, address,
  raw prompt text, or full request/response bodies.
- Add tests proving redaction and requestId propagation.
- Add a /healthz report with database and cache latency.
- Add alert rules for 5xx rate, p95 latency, and redaction failure.
- Show a diff summary and remaining manual checks at the end.

Ce prompt limite volontairement le périmètre. Claude Code ne choisit pas un nouvel outil de monitoring, ne modifie pas des routes sans rapport et ne reçoit pas de logs de production bruts. Il produit un diff opérationnel, vérifiable et réversible.

Logs structurés et identifiants de corrélation

L’OWASP Logging Cheat Sheet traite le logging comme une fonction de sécurité : revue, tests, contrôle d’accès, injection de logs, saturation disque et panne du mécanisme de logs doivent être pris en compte. Demandez donc à Claude Code des tests de redaction et de propagation, pas seulement des messages.

Voici un logger JSON sans dépendance. Enregistrez-le sous structured-logger.mjs et lancez-le avec Node.js 18 ou plus récent.

import { randomUUID } from "node:crypto";

const rank = { debug: 10, info: 20, warn: 30, error: 40 };
const current = process.env.LOG_LEVEL || "info";
const threshold = rank[current] ?? rank.info;

const secretKeys = [
  "password",
  "token",
  "authorization",
  "cookie",
  "set-cookie",
  "apikey",
];

function cleanText(value) {
  return String(value).replace(/[\r\n\t]/g, " ").slice(0, 500);
}

function redact(value) {
  if (Array.isArray(value)) return value.map(redact);
  if (!value || typeof value !== "object") return value;

  return Object.fromEntries(
    Object.entries(value).map(([key, item]) => {
      if (secretKeys.includes(key.toLowerCase())) {
        return [key, "[REDACTED]"];
      }
      return [key, redact(item)];
    }),
  );
}

export function log(level, message, fields = {}) {
  if ((rank[level] ?? 99) < threshold) return;

  const entry = {
    ts: new Date().toISOString(),
    level,
    service: process.env.SERVICE_NAME || "checkout-api",
    env: process.env.NODE_ENV || "development",
    requestId: fields.requestId || randomUUID(),
    msg: cleanText(message),
    ...redact(fields),
  };

  process.stdout.write(`${JSON.stringify(entry)}\n`);
}

log("info", "payment accepted", {
  requestId: "req_demo_001",
  userId: "user_123",
  amount: 4980,
  token: "sk_live_should_not_leak",
});

Dans une application web, renvoyez l’ID de requête dans l’en-tête de réponse et gardez-le dans le contexte asynchrone. La spécification W3C Trace Context décrit comment créer ou propager traceparent. Utilisez x-request-id pour la corrélation applicative et traceparent pour le tracing distribué.

import { AsyncLocalStorage } from "node:async_hooks";
import { randomUUID } from "node:crypto";
import type { Request, Response, NextFunction } from "express";
import { log } from "./structured-logger";

type RequestContext = {
  requestId: string;
  traceparent?: string;
  userId?: string;
};

const storage = new AsyncLocalStorage<RequestContext>();

export function getRequestContext() {
  return storage.getStore();
}

export function requestContext(
  req: Request,
  res: Response,
  next: NextFunction,
) {
  const started = performance.now();
  const user = (req as Request & { user?: { id?: string } }).user;
  const requestId =
    req.get("x-request-id") ||
    req.get("cf-ray") ||
    randomUUID();

  const context = {
    requestId,
    traceparent: req.get("traceparent"),
    userId: user?.id,
  };

  res.setHeader("x-request-id", requestId);

  storage.run(context, () => {
    res.on("finish", () => {
      const durationMs = Math.round(performance.now() - started);
      const level = res.statusCode >= 500
        ? "error"
        : res.statusCode >= 400
          ? "warn"
          : "info";

      log(level, "http request completed", {
        requestId,
        method: req.method,
        path: req.path,
        statusCode: res.statusCode,
        durationMs,
      });
    });

    next();
  });
}

Gardez la sémantique des niveaux simple : debug pour le local, info pour les événements normaux importants, warn pour les risques récupérables et error pour ce qui exige une investigation humaine.

Bases OpenTelemetry

OpenTelemetry sert à standardiser les signaux avant de les envoyer vers votre backend. Pour Node.js, vérifiez les guides officiels JavaScript Node.js et JavaScript exporters.

npm install @opentelemetry/sdk-node \
  @opentelemetry/auto-instrumentations-node \
  @opentelemetry/exporter-trace-otlp-proto \
  @opentelemetry/exporter-metrics-otlp-proto \
  @opentelemetry/sdk-metrics
const opentelemetry = require("@opentelemetry/sdk-node");
const {
  getNodeAutoInstrumentations,
} = require("@opentelemetry/auto-instrumentations-node");
const {
  OTLPTraceExporter,
} = require("@opentelemetry/exporter-trace-otlp-proto");
const {
  OTLPMetricExporter,
} = require("@opentelemetry/exporter-metrics-otlp-proto");
const {
  PeriodicExportingMetricReader,
} = require("@opentelemetry/sdk-metrics");

process.env.OTEL_SERVICE_NAME ||= "checkout-api";

const endpoint =
  process.env.OTEL_EXPORTER_OTLP_ENDPOINT ||
  "http://localhost:4318";

const sdk = new opentelemetry.NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: `${endpoint}/v1/traces`,
  }),
  metricReader: new PeriodicExportingMetricReader({
    exporter: new OTLPMetricExporter({
      url: `${endpoint}/v1/metrics`,
    }),
    exportIntervalMillis: 30000,
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();

process.on("SIGTERM", () => {
  sdk.shutdown().finally(() => process.exit(0));
});
flowchart LR
  A["Action utilisateur"] --> B["Application"]
  B --> C["Log structure"]
  B --> D["Metrique"]
  B --> E["Trace span"]
  C --> F["Stockage logs"]
  D --> G["Regle alerte"]
  E --> H["Backend traces"]
  F --> I["Handoff incident"]
  G --> I
  H --> I

Health checks et alertes

Un health check utile ne se limite pas à 200 OK. Il vérifie la base de données, le cache, la file et les API externes séparément, retourne la latency et ne divulgue aucun secret.

function timeout(ms) {
  return new Promise((_, reject) => {
    setTimeout(() => reject(new Error("timeout")), ms);
  });
}

export async function buildHealthReport(checks) {
  const started = Date.now();
  const results = {};

  for (const [name, check] of Object.entries(checks)) {
    const before = Date.now();
    try {
      await Promise.race([check(), timeout(800)]);
      results[name] = {
        status: "ok",
        latencyMs: Date.now() - before,
      };
    } catch (error) {
      const message =
        error instanceof Error ? error.message : String(error);
      results[name] = {
        status: "fail",
        latencyMs: Date.now() - before,
        reason: message.slice(0, 120),
      };
    }
  }

  const failed = Object.values(results)
    .filter((item) => item.status === "fail")
    .length;

  return {
    status: failed ? "degraded" : "ok",
    uptimeSec: Math.round(process.uptime()),
    totalLatencyMs: Date.now() - started,
    checks: results,
  };
}

Construisez les alertes sur des taux et percentiles, pas sur une seule ligne de log.

groups:
  - name: checkout-api
    rules:
      - alert: CheckoutHigh5xxRate
        expr: |
          sum(rate(http_requests_total{
            service="checkout-api",
            status_code=~"5.."
          }[5m]))
          /
          sum(rate(http_requests_total{
            service="checkout-api"
          }[5m])) > 0.02
        for: 10m
        labels:
          severity: page
        annotations:
          summary: "Checkout 5xx rate is above 2%"

      - alert: CheckoutP95LatencyHigh
        expr: |
          histogram_quantile(
            0.95,
            sum by (le) (
              rate(http_request_duration_seconds_bucket{
                service="checkout-api"
              }[5m])
            )
          ) > 1.5
        for: 15m
        labels:
          severity: ticket
        annotations:
          summary: "Checkout p95 latency is above 1.5s"

Trois cas d’usage concrets

Premier cas : une API de paiement ecommerce. Conservez orderId, requestId, paymentProvider et amount, mais jamais carte, email, adresse ou token. Les alertes doivent séparer taux 5xx, taux d’échec paiement et p95 du prestataire.

Deuxième cas : un dashboard admin SaaS. Connexion, changement de permission, invitation et changement de plan relèvent des audit logs. Le corps des emails et les notes privées n’ont pas à y être. Demandez à Claude Code de séparer audit logs et logs applicatifs, puis d’ajouter des tests RBAC.

Troisième cas : un site média ou CMS de blog. Suivez publication, clic CTA, réussite du formulaire, échec de génération d’image et traduction manquante. Les pages vues seules ne pilotent pas le revenu. Séparez cta_click et generate_lead, puis relisez le guide analytics avec Claude Code.

Avec des microservices, combinez cela avec le guide microservices. Des valeurs incohérentes de service.name rendent OpenTelemetry difficile à exploiter.

Échecs fréquents et handoff

Les échecs typiques sont connus : journaliser tout le body, utiliser du texte libre impossible à requêter, multiplier les IDs de corrélation, avoir un /healthz qui ne teste aucune dépendance, créer des alertes sans propriétaire. Avec Claude Code, ajoutez le risque de coller des logs bruts dans le prompt. Masquez d’abord les logs et partagez seulement métriques agrégées, requestId et trace IDs.

{
  "incident_id": "INC-2026-06-02-001",
  "severity": "SEV2",
  "owner": "oncall-api",
  "customer_impact": "Checkout errors for some card payments",
  "first_seen": "2026-06-02T09:15:00+09:00",
  "request_ids": ["req_7f3a", "req_8b21"],
  "trace_ids": ["7bba9f33312b3dbb8b2c2c62bb7abe2d"],
  "dashboards": ["Checkout API overview"],
  "current_hypothesis": "Payment provider latency spike",
  "actions_taken": ["Disabled checkout_v2 feature flag"],
  "next_checks": ["Compare p95 latency by region"],
  "do_not_do": ["Do not paste raw customer data into prompts"]
}

Pour une enquête en lecture seule, consultez aussi le guide des permissions.

Revue, CTA et vérification

Revoyez les dashboards chaque semaine : cinq erreurs principales, p95, volume de logs, fausses alertes, échecs de redaction et incidents ouverts. Chaque mois, prenez un incident réel et vérifiez si logs, métriques et traces auraient aidé une nouvelle personne d’astreinte à trouver la cause.

Pour démarrer seul, utilisez le cheatsheet gratuit. Pour des prompts et supports réutilisables, voyez la page produits. Si votre équipe veut concevoir standards de logs, CLAUDE.md, permissions, CI et incidents dans un vrai dépôt, la formation et consultation Claude Code est la bonne étape suivante.

En testant ce flux, le plus utile a été d’écrire d’abord les champs interdits. structured-logger.mjs masque le token en [REDACTED] et normalise les retours ligne. En simulant une panne cache, le health check devient degraded; avec seulement requestId et traceId dans le handoff, la revue est plus courte.

#Claude Code #logging #monitoring #OpenTelemetry #observabilité
Gratuit

PDF gratuit: cheatsheet Claude Code

Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.

Nous protégeons vos données et n'envoyons pas de spam.

Masa

À propos de l'auteur

Masa

Ingénieur spécialisé dans les workflows pratiques avec Claude Code.