infra / monitoring-observabilite
Écrire des métriques applicatives
Explication
Ce que vous allez apprendre
- Exposer un endpoint
/metricscompatible Prometheus depuis une application Python ou Node.js - Choisir entre les trois types de métriques : Counter, Gauge et Histogram
- Mesurer automatiquement la latence et le débit d'une API via un middleware
- Créer des métriques métier en plus des métriques purement techniques
- Éviter dès l'écriture le piège de la cardinalité (approfondi plus loin dans le cours)
Dans quel contexte ?
Une équipe backend vient de brancher Prometheus (leçon précédente) mais il ne collecte encore rien d'utile : Prometheus sait où chercher, mais l'application mon-api n'expose aucune métrique. Le développeur doit maintenant instrumenter le code lui-même pour que chaque requête HTTP alimente des compteurs et des histogrammes consultables ensuite dans Grafana.
D'abord, il faut choisir le bon type de donnée à exposer
Prometheus ne propose pas un seul type de métrique générique : il en distingue plusieurs, chacun adapté à une forme de donnée précise. Se tromper de type rend une métrique inutilisable ou trompeuse une fois agrégée.
Le plus simple à comprendre est le Counter : une valeur qui ne fait qu'augmenter, comme un compteur de requêtes totales. Il redémarre à zéro uniquement si l'application elle-même redémarre — jamais autrement.
| Type | Comportement | Exemple concret |
|---|---|---|
| Counter | Toujours croissant | http_requests_total, orders_created_total |
| Gauge | Monte et descend librement | active_user_sessions, queue_depth |
| Histogram | Distribution en buckets, percentiles calculables côté serveur | http_request_duration_seconds |
| Summary | Comme Histogram, mais percentiles calculés côté client | rarement recommandé en multi-instances |
Une fois le Counter compris, le Gauge devient évident par contraste
Un Gauge représente une valeur instantanée qui peut aussi bien monter que descendre : le nombre de connexions actives, la taille d'une file d'attente, ou la température d'un processeur. Contrairement au Counter, on peut l'incrémenter ET le décrémenter (.inc() puis .dec()).
Prérequis
Cette leçon utilise directement le fichier prometheus.yml configuré à la leçon précédente. Assure-toi que Prometheus scrape bien mon-api:8000/metrics avant de continuer.
Il reste un problème : comment mesurer une latence, une distribution de valeurs ?
Un simple nombre ne suffit pas pour représenter "la durée des requêtes HTTP", car cette durée varie énormément d'une requête à l'autre. C'est exactement le rôle de l'Histogram : il classe chaque valeur observée dans des tranches ("buckets") prédéfinies, ce qui permet de calculer ensuite des percentiles (P50, P95, P99) directement dans Prometheus.
Piège fréquent
Les buckets par défaut de prometheus_client sont pensés pour des latences web classiques (millisecondes à quelques secondes). Un job batch qui dure plusieurs minutes, mesuré avec ces buckets par défaut, produira un histogramme totalement illisible : il faut définir des buckets adaptés à l'ordre de grandeur réel de la donnée mesurée.
Voici comment on assemble tout ça dans un vrai middleware
Le pattern devient alors mécanique : à chaque requête entrante, on incrémente un Gauge "en cours", on chronomètre la durée, on enregistre cette durée dans l'Histogram, puis on incrémente le Counter avec le status code final. C'est exactement ce que montre le middleware FastAPI ou Express du code de cette leçon.
Bonne pratique
Ne te limite pas aux métriques techniques (latence, erreurs). Ajoute aussi des métriques métier comme orders_created_total ou cart_value_euros : ce sont souvent elles qui ont le plus de valeur pour l'équipe produit, bien au-delà du seul monitoring technique.
Le piège à anticiper avant même d'écrire ton premier label
Ne mets jamais un user_id, un request_id ou un timestamp comme valeur de label : chaque valeur distincte crée une nouvelle série temporelle stockée séparément, et un identifiant unique par utilisateur peut multiplier ce nombre par centaines de milliers. Ce sujet, la cardinalité, mérite une leçon entière plus loin dans ce cours — retiens pour l'instant la règle simple : un label doit avoir un nombre de valeurs possibles connu et limité à l'avance.
Maintenant que l'application expose de vraies métriques, la prochaine étape logique est d'apprendre à les interroger avec PromQL, le langage de requête de Prometheus.
Commandes & code
Écrire des métriques applicatives
# Python — client officiel prometheus_client, avec FastAPI
from fastapi import FastAPI, Request
from prometheus_client import Counter, Histogram, Gauge, generate_latest, CONTENT_TYPE_LATEST
from starlette.responses import Response
import time
app = FastAPI()
# Counter : ne fait qu'AUGMENTER (nombre de requêtes, erreurs, tâches traitées...)
REQUEST_COUNT = Counter(
"http_requests_total", "Nombre total de requêtes HTTP",
["method", "path", "status"]
)
# Histogram : distribution de valeurs (latence, taille de payload) avec des "buckets"
REQUEST_LATENCY = Histogram(
"http_request_duration_seconds", "Durée des requêtes HTTP",
["method", "path"],
buckets=[0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]
)
# Gauge : valeur qui peut monter OU descendre (connexions actives, taille de file d'attente)
IN_PROGRESS = Gauge("http_requests_in_progress", "Requêtes en cours de traitement")
@app.middleware("http")
async def prometheus_middleware(request: Request, call_next):
IN_PROGRESS.inc()
start = time.perf_counter()
response = await call_next(request)
duration = time.perf_counter() - start
REQUEST_LATENCY.labels(request.method, request.url.path).observe(duration)
REQUEST_COUNT.labels(request.method, request.url.path, response.status_code).inc()
IN_PROGRESS.dec()
return response
@app.get("/metrics")
def metrics():
return Response(generate_latest(), media_type=CONTENT_TYPE_LATEST)// Node.js — client officiel prom-client, avec Express
const client = require("prom-client");
const express = require("express");
const app = express();
const register = new client.Registry();
client.collectDefaultMetrics({ register }); // CPU, mémoire, event loop lag... automatiques
const httpRequestDuration = new client.Histogram({
name: "http_request_duration_seconds",
help: "Durée des requêtes HTTP",
labelNames: ["method", "route", "status_code"],
buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5],
});
register.registerMetric(httpRequestDuration);
app.use((req, res, next) => {
const end = httpRequestDuration.startTimer();
res.on("finish", () => {
end({ method: req.method, route: req.path, status_code: res.statusCode });
});
next();
});
app.get("/metrics", async (req, res) => {
res.set("Content-Type", register.contentType);
res.end(await register.metrics());
});Choisir le bon type de métrique :
Counter -> requêtes totales, erreurs totales, octets envoyés (uniquement croissant, reset au redémarrage)
Gauge -> connexions actives, taille de file, température CPU (monte ET descend)
Histogram -> latence, taille de payload (distribution en buckets, permet de calculer des percentiles côté serveur)
Summary -> comme Histogram mais calcule les percentiles côté CLIENT (moins flexible en agrégation multi-instances)# Métriques métier, pas seulement techniques — souvent le plus précieux en production
ORDERS_CREATED = Counter("orders_created_total", "Commandes créées", ["payment_method"])
CART_VALUE = Histogram("cart_value_euros", "Valeur des paniers", buckets=[10, 25, 50, 100, 250, 500])
ACTIVE_SESSIONS = Gauge("active_user_sessions", "Sessions utilisateur actives")
# Exemple d'utilisation dans une route métier
@app.post("/orders")
def create_order(payment_method: str, total: float):
ORDERS_CREATED.labels(payment_method=payment_method).inc()
CART_VALUE.observe(total)
return {"status": "created"}Résumé
- Counter (toujours croissant), Gauge (monte/descend), Histogram (distribution en buckets, percentiles calculables côté Prometheus) : 3 types à connaître par coeur.
- Toujours exposer un endpoint
/metricsque Prometheus viendra scraper (pattern "pull", cohérent avec la leçon précédente). - Les métriques MÉTIER (commandes créées, valeur panier) ont souvent plus de valeur business que les métriques purement techniques.
- Limiter le nombre de labels et leurs valeurs possibles : un label avec un
user_iden valeur crée une explosion de cardinalité (leçon dédiée plus loin).
Exercices pratiques
Mission : instrumenter la route commandes sans piéger Prometheus
Objectif : Choisir les bons types de métriques (Counter, Gauge, Histogram) pour instrumenter une route métier, sans introduire de cardinalité dangereuse.
Contexte
Le endpoint POST /orders de mon-api n'expose encore aucune métrique métier. Un collègue propose d'ajouter un label order_id sur le Counter de commandes créées "pour pouvoir chercher facilement une commande précise dans Grafana". Tu dois évaluer cette proposition et instrumenter la route correctement.