Intro
Cette version française explique REST API monitoring and alerts with practical examples avec le même objectif pratique que l article source : aider le lecteur à comprendre le contexte, les décisions à prendre et les points à vérifier avant de passer à l action.
Les REST APIs fiables font trois choses très bien : elles exposent des signaux de santé clairs, déclenchent des alertes pertinentes au bon moment et soutiennent une réponse aux incidents fluide. Ce guide pratique détaille quoi surveiller, quelles métriques importent, comment écrire des règles db9alerte qui évitent le bruit et comment concevoir logs et dashboards pour un diagnostic rapide. Vous trouverez des exemples copiés-collés en Node.js/Express avec MongoDB, plus des appels vers Stripe et OpenAI API, ainsi qub9un Plan Pilote Local pour tout valider sur votre machine.
Public visé :
- Développeurs qui construisent ou exploitent des REST APIs
- Consultants DevOps qui déploient des standards de production
- Équipes tech en startup qui veulent du signal sans sur-ingénierie
Résultats attendus :
- Une checklist concrète de REST API metrics et db9alertes
- Un logging structuré qui accélère le triage
- Des dashboards centrés sur lb9essentiel
- Un workflow db9incident répétable
- Un pilote local exécutable aujourdb9hui
Vue db9ensemble du workflow
Un REST API monitoring fiable suit six étapes :
- Instrumenter
- Emettre des métriques RED par route : Rate, Errors, Duration
- Ajouter des métriques de ressources : CPU, mémoire, latence de lb9event loop
- Envelopper les dépendances : MongoDB, Stripe, OpenAI
- Utiliser un request_id pour corréler logs et métriques
- Collecter
- Exposer /metrics pour le scraping
- Envoyer des logs JSON vers votre collecteur (stdout en conteneur convient)
- Alerter
- Limiter à un petit ensemble db9alertes nettes et actionnables
- Préférer des conditions basées sur des SLO à des seuils bruyants
- Visualiser
- Des dashboards qui répondent : est-ce up, est-ce rapide, qui est en panne ?
- Répondre
- Runbooks légers, propriétaires clairs, chemins de rollback rapides
- Apprendre
- Après incident : affiner métriques, alertes et runbooks
Que surveiller et pourquoi
Signaux côur db9une REST API
- Disponibilité : taux 2xx vs 5xx, par route
- Latence : p50, p95, p99, par route et méthode
- Trafic : RPS par route et méthode
- Erreurs : motifs 4xx (auth, validation) et 5xx (bugs, timeouts)
- Saturation : CPU, mémoire, latence event loop, longueur de file
- Timeouts et retries : comptage et taux par dépendance
- Auth et rate limiting : tendances 401/403/429
Dépendances
- MongoDB : latence par opération (find, insert, update), pool de connexions, timeouts, codes db9erreur
- Caches/queues : hit rate, profondeur de file, lag des consommateurs
Fournisseurs externes
- Stripe : succès et latence par endpoint, 4xx vs 5xx, 429, crédits de rate limit restants, conflits db9idempotence
- OpenAI API : succès, latence, usage de tokens par requête, 429, timeouts, erreurs de filtrage de contenu si applicables
Métriques et règles db9alerte
Appliquez RED et USE :
- RED par route : RPS, taux db9erreur, histogrammes de durée
- USE par ressource : CPU (utilisation), mémoire (saturation), erreurs
Exemples de noms de métriques
- http_server_requests_total{method, route, status_code}
- http_server_request_duration_seconds_bucket
- process_cpu_seconds_total, process_resident_memory_bytes
- nodejs_eventloop_lag_seconds (si dispo)
- mongo_query_duration_seconds_bucket{operation, collection, outcome}
- third_party_request_total{provider, operation, outcome}
- third_party_request_duration_seconds_bucket{provider, operation}
- third_party_rate_limit_exhausted_total{provider}
Idées db9alertes PromQL (adaptez vos labels)
- Taux db9erreur élevé
sum(rate(http_server_requests_total{status_code=~"5.."}[5m]))
/
sum(rate(http_server_requests_total[5m]))
> 0.05
for: 10m
- Régression de latence (exclure /metrics)
histogram_quantile(
0.95,
sum by (le) (rate(http_server_request_duration_seconds_bucket{route!="/metrics"}[5m]))
) > 0.300
for: 10m
- Lag de lb9event loop (Node.js)
max_over_time(nodejs_eventloop_lag_seconds[5m]) > 0.050
for: 10m
- Timeouts ou erreurs MongoDB
sum(rate(mongo_query_duration_seconds_count{outcome="timeout"}[5m])) > 1
or
sum(rate(mongo_query_errors_total[5m])) > 1
- Pannes fournisseurs et 429
sum(rate(third_party_request_total{provider=~"stripe|openai",outcome="error"}[5m])) > 2
or
increase(third_party_rate_limit_exhausted_total{provider=~"stripe|openai"}[15m]) > 0
Pensée SLO
- Définir lb9SLI : success_rate, request_latency
- Fixer lb9SLO : ex. 99,5 % de succès, p95 < 300 ms, 24x7
- Alerter sur la consommation de budget : fenêtres rapides et lentes combinées
Logs et événements structurés
Concevez des logs JSON qui répondent à qui, quoi, of9, quand et pourquoi :
Dans chaque log de requête
- ts (ISO), level (info|warn|error)
- request_id (UUID à lb9entrée), method, route, path, status
- duration_ms
- remote_ip (sanitisée), user_agent (optionnel)
- user_id ou tenant_id si autorisé
Appels de dépendance
- dependency : mongo|stripe|openai
- operation : findOne|charges.create|chat.completions
- outcome : ok|error|timeout|rate_limited
- status ou code, duration_ms, retry_after_ms ou rate_limit_remaining
Privacy et sécurité
- Ne jamais logger secrets, tokens, données carte, ni prompts avec PII
- Hacher ou omettre les identifiants si doute
- Échantillonner finement les payloads verbeux hors production
Tableaux de bord qui comptent
Vue db9accueil
- Ligne du haut : taux de succès, latence p95, RPS, 5xx par route
- Tendances : p50-p99, consommation de budget db9erreur, marqueurs de déploiement
Détail par route
- Percentiles de latence, débit, répartition 4xx/5xx
- Top messages db9erreur et signatures de stack
Dépendances
- MongoDB : p50-p99 par opération, connexions en usage, timeouts
- Fournisseurs : taux de succès, latence, 429, crédits restants
Saturation
- CPU, mémoire, lag event loop, pauses GC, redémarrages conteneur
Vue on-call
- Alertes ouvertes, principaux fautifs, liens runbooks
Exemples pratiques par stack
Les extraits ci-dessous montrent une configuration minimale mais proche prod pour une Express API avec métriques Prometheus-style et logs structurés, incluant lb9instrumentation MongoDB et des wrappers Stripe et OpenAI API.
Express : métriques et logs
Install
npm i express prom-client pino pino-http uuid
server.js
const express = require('express');
const client = require('prom-client');
const pino = require('pino');
const pinoHttp = require('pino-http');
const { v4: uuidv4 } = require('uuid');
const app = express();
const logger = pino({ level: process.env.LOG_LEVEL || 'info' });
// Prometheus registry et métriques process par défaut
const register = new client.Registry();
client.collectDefaultMetrics({ register });
// Métriques RED
const httpDuration = new client.Histogram({
name: 'http_server_request_duration_seconds',
help: 'HTTP request duration in seconds',
labelNames: ['method', 'route', 'status_code']
});
const httpRequests = new client.Counter({
name: 'http_server_requests_total',
help: 'Total HTTP requests',
labelNames: ['method', 'route', 'status_code']
});
register.registerMetric(httpDuration);
register.registerMetric(httpRequests);
// Request ID + logging
app.use((req, res, next) => {
req.id = req.headers['x-request-id'] || uuidv4();
res.setHeader('x-request-id', req.id);
next();
});
app.use(pinoHttp({ logger, genReqId: req => req.id }));
// Mesure de temps
app.use((req, res, next) => {
const end = httpDuration.startTimer({ method: req.method });
res.on('finish', () => {
const route = req.route ? req.route.path : req.path;
const labels = { method: req.method, route, status_code: String(res.statusCode) };
httpRequests.inc(labels, 1);
end({ route, status_code: String(res.statusCode) });
req.log.info({
msg: 'request_complete', request_id: req.id, method: req.method,
route, status: res.statusCode, duration_ms: res.responseTime
});
});
next();
});
// Routes d'exemple
app.get('/healthz', (req, res) => res.status(200).json({ ok: true }));
app.get('/api/v1/items/:id', async (req, res) => {
// Appel DB fictif
res.json({ id: req.params.id, name: 'example' });
});
// Endpoint metrics
app.get('/metrics', async (req, res) => {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
});
// Gestion d'erreurs
app.use((err, req, res, next) => {
req.log.error({ msg: 'unhandled_error', request_id: req.id, err });
res.status(500).json({ error: 'internal_error', request_id: req.id });
});
const port = process.env.PORT || 3000;
app.listen(port, () => logger.info({ msg: 'listening', port }));
Notes
- Corrélation : x-request-id est renvoyé au client pour aligner traces et logs
- Métriques : le label route préfère req.route.path pour éviter lb9explosion de cardinalité
Instrumentation MongoDB
Install
npm i mongodb
mongo.js
const { MongoClient } = require('mongodb');
const client = require('prom-client');
const mongoDuration = new client.Histogram({
name: 'mongo_query_duration_seconds',
help: 'MongoDB query duration',
labelNames: ['operation', 'collection', 'outcome']
});
async function withTiming(op, collection, fn) {
const end = mongoDuration.startTimer({ operation: op, collection });
try {
const result = await fn();
end({ outcome: 'ok' });
return result;
} catch (e) {
const outcome = /timeout/i.test(e.message) ? 'timeout' : 'error';
end({ outcome });
throw e;
}
}
async function createClient(uri) {
const cli = new MongoClient(uri, { maxPoolSize: 20 });
await cli.connect();
return cli;
}
module.exports = { withTiming, createClient, mongoDuration };
Utilisation dans une route
// dans server.js
const { createClient, withTiming } = require('./mongo');
let db;
(async () => {
const cli = await createClient(process.env.MONGO_URI || 'mongodb://localhost:27017');
db = cli.db('app');
})();
app.get('/api/v1/items/:id', async (req, res, next) => {
try {
const id = req.params.id;
const doc = await withTiming('findOne', 'items', () => db.collection('items').findOne({ _id: id }));
if (!doc) return res.status(404).json({ error: 'not_found' });
res.json(doc);
} catch (e) { next(e); }
});
Wrapper Stripe
Install
npm i stripe
stripe.js
const Stripe = require('stripe');
const client = require('prom-client');
const stripe = new Stripe(process.env.STRIPE_API_KEY || 'sk_test_xxx');
const tpReqs = new client.Counter({
name: 'third_party_request_total',
help: 'Third-party requests',
labelNames: ['provider', 'operation', 'outcome']
});
const tpDur = new client.Histogram({
name: 'third_party_request_duration_seconds',
help: 'Third-party request duration',
labelNames: ['provider', 'operation']
});
const tpRateExhaust = new client.Counter({
name: 'third_party_rate_limit_exhausted_total',
help: 'Third-party rate limit exhausted',
labelNames: ['provider']
});
async function createCharge(params) {
const op = 'charges.create';
const end = tpDur.startTimer({ provider: 'stripe', operation: op });
try {
const res = await stripe.charges.create(params);
tpReqs.inc({ provider: 'stripe', operation: op, outcome: 'ok' });
if (res.headers && Number(res.headers['stripe-rate-limit-remaining']) === 0) {
tpRateExhaust.inc({ provider: 'stripe' });
}
end();
return res;
} catch (e) {
const outcome = e.statusCode === 429 ? 'rate_limited' : 'error';
tpReqs.inc({ provider: 'stripe', operation: op, outcome });
if (e.statusCode === 429) tpRateExhaust.inc({ provider: 'stripe' });
end();
throw e;
}
}
module.exports = { createCharge };
Wrapper OpenAI API
Install
npm i openai
openai.js
const { OpenAI } = require('openai');
const client = require('prom-client');
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY || 'sk-xxx' });
const tpReqs = new client.Counter({
name: 'third_party_request_total',
help: 'Third-party requests',
labelNames: ['provider', 'operation', 'outcome']
});
const tpDur = new client.Histogram({
name: 'third_party_request_duration_seconds',
help: 'Third-party request duration',
labelNames: ['provider', 'operation']
});
const tpRateExhaust = new client.Counter({
name: 'third_party_rate_limit_exhausted_total',
help: 'Third-party rate limit exhausted',
labelNames: ['provider']
});
async function createChatCompletion(msgs) {
const op = 'chat.completions';
const end = tpDur.startTimer({ provider: 'openai', operation: op });
try {
const res = await openai.chat.completions.create({ model: 'gpt-4o-mini', messages: msgs });
tpReqs.inc({ provider: 'openai', operation: op, outcome: 'ok' });
end();
return res;
} catch (e) {
const code = e.status || e.code || 0;
const outcome = code === 429 ? 'rate_limited' : 'error';
tpReqs.inc({ provider: 'openai', operation: op, outcome });
if (code === 429) tpRateExhaust.inc({ provider: 'openai' });
end();
throw e;
}
}
module.exports = { createChatCompletion };
Ajouter une route qui utilise ces wrappers
// dans server.js
const { createCharge } = require('./stripe');
const { createChatCompletion } = require('./openai');
app.post('/api/v1/payments', express.json(), async (req, res, next) => {
try {
const charge = await createCharge({ amount: 100, currency: 'usd', source: 'tok_visa' });
res.json({ id: charge.id, status: charge.status });
} catch (e) { next(e); }
});
app.post('/api/v1/assist', express.json(), async (req, res, next) => {
try {
const { prompt } = req.body;
const chat = await createChatCompletion([{ role: 'user', content: prompt }]);
res.json({ reply: chat.choices?.[0]?.message?.content || '' });
} catch (e) { next(e); }
});
Workflows db9intervention
Rendre les alertes actionnables
- Titre : ce qui casse et of9 (route, fournisseur)
- Symptômes : détails de taux db9erreur ou de latence
- Liens : dashboard de la route, déploiements récents, runbook
Checklist on-call
- Accuser réception, désigner un seul owner
- Vérifier la home et la route affectée
- Confirmer lb9impact utilisateur. Si sévère, rollback la dernière modif
- Si un fournisseur externe tombe, dégrader gracieusement ou mettre en file
- Utiliser le request_id db9une ligne db9erreur pour tracer les appels
- Après correctif : vérifier les métriques, écrire un bref REX et next steps
Plan Pilote Local
Objectif
- Valider métriques, logs et quelques formules db9alerte en local
Portée
- Deux routes : un GET rapide et un POST qui touche MongoDB
- Un appel wrapper Stripe et un wrapper OpenAI (pouvant être stub)
- Collecte des métriques par défaut + RED, exposer /metrics
Étapes
- Instrumenter
- Ajouter le middleware Express, le timing MongoDB, et les wrappers tiers
- Simuler du trafic
# Route rapide
hey -z 30s -c 10 http://localhost:3000/healthz
# Route DB
hey -z 30s -c 5 http://localhost:3000/api/v1/items/123
- Inspecter les métriques
curl -s localhost:3000/metrics | grep http_server_request_duration_seconds
- Déclencher erreurs et 429
- Utiliser une clé test Stripe et provoquer des erreurs de validation
- Pour OpenAI, simuler un 429 en faisant temporairement échouer les appels via un flag
- Valider les champs de log
- Vérifier request_id, route, status, duration_ms, champs de dépendance
- Esquisser les expressions db9alerte
- Coller les exemples PromQL dans lb9outil db9alerte et vérifier lb9existence des séries
Critères de succès
- Voir la p95 et le taux de 5xx par route
- Tracer une requête en échec entre app, MongoDB et fournisseurs via request_id
- Une alerte db9exemple se déclenche sous échec induit
Suite
- Déployer le même motif sur une route plus chargée, puis sur db9autres services
Conclusion
Surveillez ce que ressent lb9utilisateur et ce dont les systèmes ont besoin. Démarrez petit avec un pilote local, branchez des IDs de corrélation, émettez des métriques RED, ajoutez quelques REST API alerts solides et construisez un REST API dashboard qui répond à de vraies questions. Entraeenez la REST API incident response avec des runbooks légers et une responsabilité claire. Une fois la valeur prouvée, appliquez ces mêmes patterns à vos autres services REST.