E-NO
Surveillance Node.js 10 min de lecture

Surveillance et alertes Node.js avec exemples pratiques : guide d'implémentation

calendar_today Publié : 2026-07-23
update Dernière mise à jour : 2026-07-23
analytics Efficacité SEO : 100%
Illustration du guide technique pour « Surveillance et alertes Node.js avec exemples pratiques : guide d'implémentation ».

Les applications Node.js sont rapides à créer, mais elles ne semblent rapides que lorsque la latence, les erreurs et les dépendances sont maîtrisées. Ce guide montre exactement quoi surveiller, comment instrumenter des métriques et des logs utiles, comment écrire des règles d'alerte qui réduisent le bruit, comment construire un tableau de bord ciblé et comment exécuter un flux d'incident simple. Vous pouvez tout tester localement d'abord, puis déployer en toute sécurité.

Vue d'ensemble du workflow

Un flux pragmatique pour passer des signaux à l'action :

  1. Identifier les signaux qui reflètent l'expérience utilisateur et la santé du service.
  2. Instrumenter les métriques et les logs structurés dans votre service Node.js.
  3. Collecter et stocker les données dans votre pile de surveillance.
  4. Visualiser avec un tableau de bord ciblé pour un triage rapide.
  5. Définir des alertes actionnables avec des seuils et des durées clairs.
  6. Répondre avec un flux d'incident léger et vérifier la récupération.
  7. Réviser et affiner les seuils, les panneaux et les runbooks après chaque événement.

Quoi surveiller dans Node.js

Suivez d'abord un petit ensemble de signaux à forte valeur.

Signaux dorés

  • Latence : p50, p95, p99 pour les routes clés et l'ensemble du service.
  • Débit : requêtes par seconde.
  • Erreurs : taux de 5xx et types d'erreurs.
  • Saturation : CPU, mémoire (RSS et heap), décalage de la boucle d'événements.

Internes de Node.js

  • Décalage de la boucle d'événements (event loop lag) : un décalage soutenu indique une saturation ou une pression du ramasse-miettes (GC). Un décalage supérieur à 50 ms sous charge stable signifie souvent qu'une opération synchrone bloque le thread principal — les coupables fréquents sont les gros JSON.parse, les opérations crypto ou les lectures de fichiers synchrones.
  • Garbage collection : temps et fréquence des GC majeurs.
  • Mémoire : heap utilisé vs max, croissance du RSS (suspicion de fuite si elle augmente monotone sous charge stable).
  • Redémarrages du processus : arrêts inattendus ou boucles de crash.

Dépendances

  • MongoDB : latence des opérations, nombre d'erreurs, pool de connexions utilisé vs max.
  • Redis : latence des commandes, timeouts, erreurs de connexion, profondeur de file/backlog.
  • API externes : latence d'appel, taux de non-2xx, ouvertures de circuit breaker.

Santé et prétt (readiness)

  • Statut du healthcheck et temps de réponse.
  • Profondeur de file et taux de succès/échec des workers.

Exemples d'instrumentation

Voici une application Express minimale avec des métriques de style Prometheus via prom-client, mesure du décalage de la boucle d'événements et logs JSON structurés avec un ID de requête.

// app.js
'use strict';

const express = require('express');
const { performance, monitorEventLoopDelay } = require('perf_hooks');
const client = require('prom-client');
const crypto = require('crypto');

const app = express();
const register = new client.Registry();

// Étiqueter toutes les métriques avec un nom de service pour un filtrage facile
const serviceName = process.env.SERVICE_NAME || 'api';
register.setDefaultLabels({ service: serviceName });

// Métriques de processus Node.js par défaut (CPU, mémoire, GC, etc.)
client.collectDefaultMetrics({ register });

// Jauge de décalage de la boucle d'événements (secondes)
const ell = monitorEventLoopDelay({ resolution: 20 });
ell.enable();
const eventLoopLag = new client.Gauge({
  name: 'event_loop_lag_seconds',
  help: 'Event loop lag in seconds (mean over scrape interval)',
});
register.registerMetric(eventLoopLag);
setInterval(() => {
  eventLoopLag.set(ell.mean / 1e9); // mean is in nanoseconds
  ell.reset();
}, 1000);

// Histogramme de durée des requêtes HTTP
const httpDuration = new client.Histogram({
  name: 'http_request_duration_seconds',
  help: 'HTTP request duration in seconds',
  labelNames: ['method', 'route', 'status_code'],
  buckets: [0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2, 5]
});
register.registerMetric(httpDuration);

// Compteur total d'erreurs
const httpErrors = new client.Counter({
  name: 'http_request_errors_total',
  help: 'Total number of failed HTTP requests',
  labelNames: ['route', 'status_code', 'cause']
});
register.registerMetric(httpErrors);

// Middleware simple d'ID de requête pour la corrélation des logs
app.use((req, res, next) => {
  req.id = req.headers['x-request-id'] || crypto.randomUUID();
  res.setHeader('x-request-id', req.id);
  next();
});

// Middleware de temporisation + logs
app.use(async (req, res, next) => {
  const start = performance.now();
  res.on('finish', () => {
    const ms = performance.now() - start;
    const route = req.route ? req.route.path : req.path;
    httpDuration.labels(req.method, route, String(res.statusCode)).observe(ms / 1000);
    const log = {
      ts: new Date().toISOString(),
      level: res.statusCode >= 500 ? 'error' : 'info',
      msg: 'request',
      req_id: req.id,
      method: req.method,
      route,
      status: res.statusCode,
      latency_ms: Math.round(ms),
      service: serviceName
    };
    // Émettre sur stdout ; votre collecteur de logs le transférera
    process.stdout.write(JSON.stringify(log) + '\n');
  });
  next();
});

// Exemples de routes
app.get('/api/todos', (req, res) => {
  // Simule un travail variable
  setTimeout(() => res.json([{ id: 1, title: 'learn monitoring' }]), Math.random() * 80);
});

app.get('/api/error', (req, res) => {
  const err = new Error('forced');
  httpErrors.labels('/api/error', '500', 'forced').inc();
  res.status(500).json({ error: err.message });
});

// Endpoint de métriques
app.get('/metrics', async (req, res) => {
  res.set('Content-Type', register.contentType);
  res.end(await register.metrics());
});

const port = process.env.PORT || 3000;
app.listen(port, () => {
  // Log de démarrage simple
  const log = { ts: new Date().toISOString(), level: 'info', msg: 'listening', port, service: serviceName };
  process.stdout.write(JSON.stringify(log) + '\n');
});

Exemples de logs structurés (stdout) pour vérification visuelle rapide :

{"ts":"2025-01-01T12:00:00.000Z","level":"info","msg":"request","req_id":"c1b2","method":"GET","route":
/api/todos","status":200,"latency_ms":42,"service":"api"}
{"ts":"2025-01-01T12:00:01.000Z","level":"error","msg":"request","req_id":"c1b3","method":"GET","route":
/api/error","status":500,"latency_ms":3,"service":"api"}

Règles d'alerte et signaux de logs

Commencez par des alertes qui reflètent la douleur utilisateur et la saturation soutenue. Voici des exemples d'alertes au style Prometheus qui correspondent aux noms de métriques ci-dessus.

Violation de SLO de latence (p95 > 300 ms pendant 10 minutes)

groups:
  - name: node-api-latency
    rules:
      - alert: NodeServiceHighLatencyP95
        expr: |
          histogram_quantile(
            0.95,
            sum(rate(http_request_duration_seconds_bucket{service="api"}[5m])) by (le)
          ) > 0.3
        for: 10m
        labels:
          severity: page
          service: api
        annotations:
          summary: "High latency p95 on api"
          description: "p95 latency > 300ms for 10m"

Taux d'erreur supérieur à 2 % pendant 10 minutes (utilise le _count de l'histogramme comme total des requêtes)

groups:
  - name: node-api-errors
    rules:
      - alert: NodeServiceHighErrorRate
        expr: |
          sum(rate(http_request_errors_total{service="api"}[5m]))
          /
          sum(rate(http_request_duration_seconds_count{service="api"}[5m]))
          > 0.02
        for: 10m
        labels:
          severity: page
          service: api
        annotations:
          summary: "High error rate on api"
          description: >2% errors for 10m

Décalage de la boucle d'événements soutenu au-dessus de 200 ms pendant 5 minutes

groups:
  - name: node-api-ell
    rules:
      - alert: NodeServiceEventLoopLagHigh
        expr: event_loop_lag_seconds{service="api"} > 0.2
        for: 5m
        labels:
          severity: ticket
          service: api
        annotations:
          summary: "Event loop lag high"
          description: >200ms lag for 5m suggests saturation or GC pressure

Alertes de dépendances optionnelles (adaptez les noms à vos jauges)

  • Saturation du pool MongoDB : db_pool_in_use / db_pool_max > 0.8 pendant 10 min.
  • Backlog Redis : worker_queue_backlog > 1000 pendant 10 min.

Signaux de logs à surveiller

  • Pics de 5xx par route : filtrez level=error ET status>=500, regroupez par route.
  • Erreurs de dépendances : erreurs réseau MongoDB (message contient "MongoNetworkError" ou code "ETIMEDOUT\)) ; timeouts Redis (message contient "RedisTimeoutError" ou "ECONNRESET\)).
  • Répondeurs lents : latency_ms>500 avec status<500 indique une lenteur sans échec pur.

Garder les alertes actionnables

  • Liez les alertes aux symptômes visibles par l'utilisateur (latence et taux d'erreur) avant les métriques internes.
  • Utilisez une fenêtre temporelle (for:) pour éviter les pages sur des transitoires.
  • Redirigez les internes bruyants (comme de brèves pauses GC) vers un ticket ou une notification au lieu d'une page.

Tableaux de bord qui pilotent l'action

Organisez autour des décisions, pas des décharges de données.

Première rangée de panneaux (signaux dorés)

  • Latence p50/p95/p99 (global et top 3 routes) : requête histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{service="api"}[5m])) by (le, route)).
  • Débit (rps) : requête sum(rate(http_request_duration_seconds_count{service="api"}[1m])).
  • Taux d'erreur (%) : requête 100 * sum(rate(http_request_errors_total{service="api"}[5m])) / sum(rate(http_request_duration_seconds_count{service="api"}[5m])).

Santé des ressources

  • Décalage de la boucle d'événements (secondes) : event_loop_lag_seconds{service="api"}.
  • Mémoire RSS et heap utilisé : process_resident_memory_bytes, nodejs_heap_size_used_bytes.
  • CPU user/system : process_cpu_user_seconds_total, process_cpu_system_seconds_total (en taux).

Forages (drilldowns)

  • Routes les plus lentes : trier le p95 par route.
  • Latence et erreurs des dépendances : panneaux par métrique MongoDB/Redis.
  • Marqueur de déploiement récent : incluez une étiquette build/version sur les métriques (ex. labels par défaut) et annotez les changements.

Lien vers les logs

Ajoutez des liens de tableau de bord qui passent la route et le x-request-id à votre visualiseur de logs pour passer d'un panneau lent aux requêtes exactes.

Workflows de réponse aux incidents

Visez un triage rapide et une atténuation sûre.

  1. Acquitter et dimensionner l'impact
  • Vérifiez les signaux dorés : est-ce visible par l'utilisateur (latence ou taux d'erreur) ?
  • Identifiez le rayon d'action : une seule route, tout le service ou une dépendance.
  1. Former une hypothèse en 2-3 minutes
  • Latence élevée avec CPU normal : probable lenteur de dépendance.
  • Décalage de boucle d'événements élevé et RSS en hausse : pression GC ou travail synchrone sur le thread principal.
  • Pic d'erreurs après un changement : régression ou dérive de configuration.
  1. Atténuer
  • Revenir en arrière ou désactiver le changement risqué.
  • Réduire la charge prudemment (limites de taux) sur les routes spécifiques si nécessaire.
  • Redémarrer les répliques défaillantes seulement si vous en comprenez l'impact.
  1. Vérifier la récupération
  • Latence et taux d'erreur reviennent dans les bandes normales.
  • Décalage de la boucle d'événements se stabilise.
  • Logs montrent une réduction des 5xx et des erreurs de dépendances.
  1. Capturer les notes et améliorer
  • Enregistrez le déclencheur, la correction et les suivis (panneaux à ajouter, seuils à ajuster, code à optimiser).

Démarrage de runbooks

  • Mémoire/RSS en hausse continue : prenez un instantané de heap dans un environnement sûr, vérifiez les gros buffers ou caches sans TTL.
  • Dépendance lente : ajoutez des timeouts et des réessais avec jitter ; envisagez un circuit breaker et une isolation bulkhead pour les chemins critiques.
  • Décalage de la boucle d'événements : déplacez le travail CPU-intensif ou bloquant vers un worker thread ou un processus séparé ; évitez les appels synchrones filesystem et crypto sur le thread principal.

Plan de test local

Commencez petit, mesurez et inspectez localement avant un déploiement large.

Périmètre

  • Un service, deux routes (/api/todos et /api/error).
  • Métriques : histogramme de durée de requête, compteur d'erreurs, jauge de décalage de boucle d'événements, métriques de processus par défaut.
  • Alertes : latence p95, taux d'erreur.
  • Tableau de bord : signaux dorés et décalage de boucle d'événements.

Étapes

  1. Ajoutez le middleware et les métriques de l'exemple d'application.
  2. Exposez /metrics et confirmez qu'il renvoie du texte de métriques :
   curl http://localhost:3000/metrics
  1. Générez de la charge et des erreurs localement :
  • Frappez /api/todos répétitivement pour construire les histogrammes de latence :
     for i in {1..100}; do curl -s http://localhost:3000/api/todos > /dev/null; done
  • Frappez /api/error pour incrémenter les compteurs d'erreurs :
     for i in {1..10}; do curl -s http://localhost:3000/api/error > /dev/null; done
  1. Validez les alertes en abaissant temporairement les seuils pour qu'ils se déclenchent en quelques minutes, puis rétablissez des niveaux sains.
  2. Construisez un tableau de bord minimal avec 4 panneaux : latence p95, rps, taux d'erreur, décalage de boucle d'événements.
  3. Ajoutez un ID de requête aux requêtes client (x-request-id) et confirmez que les logs et les réponses l'incluent pour la corrélation :
   curl -H "x-request-id: test-123" http://localhost:3000/api/todos

Critères de sortie

  • Vous voyez le p50/p95 bouger sous charge et se stabiliser après l'arrêt de la charge.
  • L'alerte de taux d'erreur se déclenche seulement pendant les échecs forcés et se calme quand ils cessent.
  • Le décalage de la boucle d'événements reste proche de zéro sous charge normale et augmente sous les tests de stress.

Montée en charge

  • Ajoutez des étiquettes de route à vos 5 principaux endpoints.
  • Ajoutez ensuite les métriques de dépendances (utilisation du pool MongoDB, latence/backlog Redis).
  • Déployez sur un service de production à la fois et vérifiez panneaux et alertes.

Conclusion

Une surveillance efficace de Node.js commence par quelques métriques à fort signal : latence, débit, taux d'erreur et saturation. Instrumentez-les avec des étiquettes claires, exposez un endpoint /metrics et émettez des logs structurés avec des IDs de requête. Gardez les premières alertes simples et centrées sur l'utilisateur, et construisez un tableau de bord qui rend le triage rapide. Testez localement, validez le comportement, puis étendez à plus d'endpoints et de dépendances. Ajustez les seuils après de vrais incidents et maintenez les runbooks courts et actionnables.

Score de qualité de l’article

Utilité pour le lecteur 100%
  • check_circle Guide prêt à lire
  • check_circle Exemples pratiques inclus
  • check_circle URL d’article optimisée pour le SEO