02 — Métriques : compteurs, jauges, histogrammes, RED et USE
Ce que tu vas apprendre
- Les 4 types de métriques Prometheus et quand utiliser chacun
- La méthode RED (services orientés requêtes)
- La méthode USE (ressources système)
- Implémentation avec prom-client (TypeScript) et prometheus-client (Python)
- PromQL : les requêtes de base pour alerter
Prérequis
Les 4 types de métriques Prometheus
Prometheus est le standard de facto pour les métriques dans les systèmes cloud-native. Il définit 4 types de métriques.
Counter
Un compteur qui ne peut qu'augmenter. Se remet à zéro au redémarrage.
Usage : nombre de requêtes, nombre d'erreurs, nombre de tâches traitées.
typescriptconst httpRequestsTotal = new Counter({
name: "http_requests_total",
help: "Nombre total de requêtes HTTP",
labelNames: ["method", "route", "status"],
});
// À chaque requête
httpRequestsTotal.inc({ method: "POST", route: "/orders", status: "201" });
On ne lit jamais un counter brut — on calcule son taux : rate(http_requests_total[5m]) = requêtes par seconde sur les 5 dernières minutes.
Gauge
Une valeur qui peut monter et descendre.
Usage : utilisation mémoire, connexions actives, longueur d'une file.
typescriptconst activeConnections = new Gauge({
name: "db_active_connections",
help: "Connexions actives vers la base de données",
});
pool.on("connect", () => activeConnections.inc());
pool.on("remove", () => activeConnections.dec());
Histogram
Répartit les observations dans des buckets prédéfinis. Permet de calculer des percentiles (p50, p95, p99).
Usage : durée des requêtes, taille des réponses.
typescriptconst httpDuration = new Histogram({
name: "http_request_duration_seconds",
help: "Durée des requêtes HTTP en secondes",
labelNames: ["method", "route"],
buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5], // en secondes
});
const end = httpDuration.startTimer({ method: req.method, route: req.path });
// ... traitement ...
end(); // Enregistre la durée dans le bon bucket
PromQL pour le p99 : histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
Summary
Comme l'histogram, mais calcule les percentiles côté client sur une fenêtre glissante. Moins flexible que l'histogram (pas d'agrégation côté serveur possible).
Préférer l'histogram dans la grande majorité des cas.
La méthode RED
Proposée par Tom Wilkie (Grafana Labs) en 2015, RED définit les 3 métriques à instrumenter sur tout service qui reçoit des requêtes :
- Rate — le nombre de requêtes par seconde
- Errors — le taux d'erreurs
- Duration — la distribution de la latence
typescript// Les 3 métriques RED pour un service HTTP
const requestsTotal = new Counter({
name: "requests_total",
help: "Nombre total de requêtes",
labelNames: ["route", "method", "status_class"], // "2xx", "4xx", "5xx"
});
const errorsTotal = new Counter({
name: "errors_total",
help: "Nombre total d'erreurs",
labelNames: ["route", "method", "error_type"],
});
const requestDuration = new Histogram({
name: "request_duration_seconds",
help: "Durée des requêtes",
labelNames: ["route", "method"],
buckets: [0.01, 0.05, 0.1, 0.5, 1, 5],
});
Alertes RED typiques :
promql# Rate — requêtes/s sur les 5 dernières minutes
rate(requests_total[5m])
# Error rate — % d'erreurs
rate(errors_total[5m]) / rate(requests_total[5m]) > 0.05
# Alerte si > 5% d'erreurs
# Duration — p99 > 1 seconde
histogram_quantile(0.99, rate(request_duration_seconds_bucket[5m])) > 1
La méthode USE
Proposée par Brendan Gregg dans Systems Performance (2020), USE s'applique aux ressources système (CPU, mémoire, réseau, disque) :
- Utilization — % du temps où la ressource est occupée
- Saturation — profondeur de la file d'attente quand la ressource est saturée
- Errors — nombre d'erreurs de la ressource
CPU :
Utilization → cpu_usage_percent
Saturation → load_average_15m (processus en attente CPU)
Errors → machine_cpu_thermal_throttle_events_total
Mémoire :
Utilization → memory_used_bytes / memory_total_bytes
Saturation → memory_swap_used_bytes (swap = saturation mémoire)
Errors → memory_oom_kill_total
Disque :
Utilization → node_disk_io_time_seconds_total (% I/O occupé)
Saturation → node_disk_io_time_weighted_seconds_total
Errors → node_disk_read_errors_total
RED pour les services, USE pour les ressources — les deux ensemble couvrent l'essentiel.
Implémentation TypeScript avec prom-client
bashnpm install prom-client
typescript// metrics.ts
import { Registry, Counter, Histogram, Gauge, collectDefaultMetrics } from "prom-client";
export const registry = new Registry();
// Métriques système (CPU, mémoire, Node.js event loop)
collectDefaultMetrics({ register: registry });
// Métriques RED
export const httpRequestsTotal = new Counter({
name: "http_requests_total",
help: "Nombre total de requêtes HTTP",
labelNames: ["method", "route", "status"],
registers: [registry],
});
export const httpRequestDuration = new Histogram({
name: "http_request_duration_seconds",
help: "Durée des requêtes HTTP en secondes",
labelNames: ["method", "route"],
buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5],
registers: [registry],
});
// Métriques métier
export const ordersTotal = new Counter({
name: "orders_total",
help: "Nombre total de commandes",
labelNames: ["status"], // "created", "confirmed", "cancelled"
registers: [registry],
});
Middleware Express pour instrumenter automatiquement :
typescript// middleware/metrics.middleware.ts
import { httpRequestsTotal, httpRequestDuration } from "../metrics";
export function metricsMiddleware(req: Request, res: Response, next: NextFunction): void {
const end = httpRequestDuration.startTimer({
method: req.method,
route: req.route?.path ?? req.path,
});
res.on("finish", () => {
const route = req.route?.path ?? "unknown";
httpRequestsTotal.inc({
method: req.method,
route,
status: String(res.statusCode),
});
end({ route });
});
next();
}
// Endpoint Prometheus scrape
app.get("/metrics", async (req, res) => {
res.set("Content-Type", registry.contentType);
res.send(await registry.metrics());
});
Implémentation Python avec prometheus-client
bashpip install prometheus-client
python# metrics.py
from prometheus_client import Counter, Histogram, Gauge, CollectorRegistry, generate_latest, CONTENT_TYPE_LATEST
import time
registry = CollectorRegistry()
http_requests_total = Counter(
"http_requests_total",
"Nombre total de requêtes HTTP",
["method", "route", "status"],
registry=registry,
)
http_request_duration_seconds = Histogram(
"http_request_duration_seconds",
"Durée des requêtes HTTP",
["method", "route"],
buckets=[0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0],
registry=registry,
)
orders_total = Counter(
"orders_total",
"Nombre total de commandes",
["status"],
registry=registry,
)
Middleware FastAPI :
python# middleware/metrics.py
import time
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
from .metrics import http_requests_total, http_request_duration_seconds
class MetricsMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
start = time.perf_counter()
response = await call_next(request)
duration = time.perf_counter() - start
route = request.scope.get("path", "unknown")
http_requests_total.labels(
method=request.method,
route=route,
status=str(response.status_code),
).inc()
http_request_duration_seconds.labels(
method=request.method,
route=route,
).observe(duration)
return response
# Endpoint scrape
from fastapi import APIRouter
from fastapi.responses import Response
from prometheus_client import generate_latest, CONTENT_TYPE_LATEST, CollectorRegistry
router = APIRouter()
@router.get("/metrics")
def metrics():
return Response(generate_latest(registry), media_type=CONTENT_TYPE_LATEST)
Nommage des métriques
Prometheus a des conventions strictes à respecter :
# Format : {namespace}_{subsystem}_{name}_{unit}
http_requests_total ✓ — counter, unité implicite (total)
http_request_duration_seconds ✓ — histogram, unité explicite en secondes
process_memory_bytes ✓ — gauge, unité en bytes
httpRequests ✗ — pas de camelCase
http-requests-total ✗ — pas de tirets
requests ✗ — trop vague, pas de namespace
Règles :
- Snake_case uniquement
- Toujours inclure l'unité dans le nom pour les histogrammes et gauges
- Terminer les counters par
_total - Préfixer par le namespace du service
Résumé
| Type | Monte/descend | Usage | Opération PromQL |
|---|---|---|---|
| Counter | Monte seulement | Requêtes, erreurs, tâches | rate() |
| Gauge | Les deux | Mémoire, connexions, file | Valeur brute |
| Histogram | N/A | Latence, taille | histogram_quantile() |
| Méthode | S'applique à | Les 3 métriques |
|---|---|---|
| RED | Services (API, workers) | Rate, Errors, Duration |
| USE | Ressources (CPU, RAM, disque) | Utilization, Saturation, Errors |
Étape suivante : 03 — Traces distribuées — corréler les métriques et les logs avec le chemin exact d'une requête entre services.
Sources
- Wilkie, T. (2015). The RED Method: Key Metrics for Microservices Architecture. Grafana blog. (Rate, Errors, Duration)
- Gregg, B. (2020). Systems Performance: Enterprise and the Cloud (2nd ed.), Chapitre 2 "Methodologies". Addison-Wesley. (Modèle USE)
- Prometheus Authors. Metric Types. prometheus.io/docs/concepts/metric_types/
- Prometheus Authors. Naming best practices. prometheus.io/docs/practices/naming/
- Beyer, B., et al. (2016). Site Reliability Engineering, Chapitre 6. O'Reilly. sre.google/sre-book/monitoring-distributed-systems/