Lab 4.1 — Exemplars : du point de métrique à la trace
Le Lab 4 s’est arrêté sur une frustration : le panel « Latence p95 » dit que le service est lent, mais pas quelle requête l’a été. Une métrique est une agrégation — « 30 requêtes, p95 à 400 ms » ne désigne personne.
Les exemplars sont le chaînon manquant. Ici, rien à construire : on lit un dashboard que la démo livre exprès pour ça.
Prérequis
- Lab 4 terminé.
- Les accès ouverts (
./scripts/open-ui.sh) et les variables chargées :. ./scripts/env.sh.
Étapes
- Ouvrir le dashboard « Cart Service Exemplars » livré par la démo :
Il porte sur le service cart (le panier de la boutique), et non sur le vôtre — l’étape 5 explique pourquoi.
- Ce qu’est un exemplar. C’est une mesure individuelle conservée à côté de l’agrégat, avec le
trace_idde la requête qui l’a produite :
La série dit « il y a eu tant de requêtes dans ce seau » ; l’exemplar ajoute « et en voici une, la voilà ».
- Le câblage qui rend le clic possible. Un
trace_iddans une métrique ne sert à rien si Grafana ne sait pas où aller chercher la trace. C’est le rôle d’une ligne de configuration de la datasource Prometheus, vue au Lab 4 :
Traduction : « quand tu rencontres un trace_id dans une métrique, va ouvrir la trace dans la datasource dont l’UID est webstore-traces » — c’est-à-dire Jaeger. Sans cette ligne, Grafana saurait qu’il tient un identifiant, mais pas où aller. La même configuration se lit dans l’interface, sur la page de la datasource, et à la source dans la ConfigMap qui la provisionne : kubectl get configmap grafana-datasources -n otel-demo -o yaml.
- Lire la première heatmap. Le dashboard a deux rangées, une par opération du panier (GetCart, AddItem), et dans chacune deux vues de la même mesure. Commençons par la première, « GetCart Latency Heatmap with Exemplars » :
C’est la requête du p95 du Lab 4 sans le histogram_quantile — et c’est tout l’intérêt : au lieu de résumer la distribution en un seul chiffre, on l’affiche entière.
- En abscisse, le temps, comme sur n’importe quel graphe.
- En ordonnée, les seaux de latence — les valeurs du label
lede la métrique (le= less or equal, la borne supérieure du seau). Attention à l’unité : cette métrique est en secondes, l’axe affiche donc0.005pour 5 ms. - La couleur d’une cellule, le nombre de requêtes tombées dans ce seau pendant cet intervalle. Plus c’est vif, plus il y en a eu.
Une colonne de la heatmap, lue de bas en haut, c’est donc la distribution des latences à cet instant : où se concentre le gros du trafic, et ce qui traîne au-dessus. Là où le panel voisin réduit chaque instant à un point — le p95 —, la heatmap montre toute la population. C’est la même métrique, le même sum by(le), et deux lectures.
💡 Le
rate(...)n’a pas disparu : les seaux sont des compteurs cumulés depuis le démarrage du service. Sans lui, la heatmap afficherait la distribution depuis toujours, une image qui ne bouge quasiment plus. Avec lui, chaque colonne ne montre que ce qui vient de se passer.$__rate_intervalest la variable de Grafana qui adapte la fenêtre au zoom du dashboard.
- Repérer les exemplars. Ils ne sont pas sur la courbe : ce sont des marqueurs à part, de petits carrés magenta sur la heatmap, de petits losanges verts sur la courbe du p95 juste à côté (« 95th Pct Cart GetCart Latency with Exemplars »).
Chacun est posé à sa propre valeur — le plus souvent sous la courbe du p95, parfois au-dessus. Ce n’est pas un tirage au hasard parmi toutes les requêtes, et le mécanisme réel explique mieux ce que vous voyez : le SDK garde un échantillon par seau de l’histogramme. Chaque seau qui a reçu du trafic conserve donc ses requêtes témoins, et Grafana les affiche toutes, seaux confondus.
Relevé sur le cluster de la formation, sur un quart d’heure :
Les seaux du bas sont les plus peuplés — la plupart des requêtes sont rapides — donc la plupart des marqueurs se posent sous une courbe qui, elle, suit le haut de la distribution. Un marqueur au-dessus du p95 est une requête d’un seau plus lent : exactement celle qu’on veut ouvrir.
💡 La heatmap et la courbe affichent les mêmes exemplars, tous seaux confondus — et non, la courbe du p95 ne montre pas seulement les requêtes du seau où tombe le p95. Quand la case Exemplars est cochée, Grafana envoie l’expression du panel à l’API
query_exemplars, qui n’en retient que le sélecteur de série (app_cart_get_cart_latency_seconds_bucket) et ignore tout le reste :rate,sum by(le)ethistogram_quantilen’ont aucun effet sur les marqueurs renvoyés. Vérifié sur le cluster de la formation : les trois écritures rendent les mêmes 31 exemplars, sur les mêmes 4 seaux.
Survolez un marqueur : une infobulle donne la valeur, le trace_id et un lien. Cliquez : Jaeger s’ouvre sur cette requête précise. Au lieu de chercher dans Jaeger une trace qui ressemblerait au symptôme, c’est le symptôme qui vous donne son identifiant.
⚠️ Sur le serveur partagé, l’infobulle propose deux liens. La démo officielle déclare deux destinations pour le même exemplar dans sa datasource Prometheus (
kubectl get cm grafana-datasources -n otel-demo -o yaml) : une correcte, résolue côté serveur Grafana par sondatasourceUid; l’autre porte une URL en dur,http://localhost:8080/jaeger/ui/trace/…. Un artefact du chart amont, pas de ce cours — et un cas de plus du piège du Lab 1 :localhost, sur ce serveur, c’eststudent1. Si vous cliquez ce second lien et n’êtes passtudent1, Jaeger s’ouvre chez votre voisin, sur une trace qu’il n’a probablement pas. Prenez le premier lien de l’infobulle ; à défaut, copiez letrace_idet collez-le dans votre Jaeger (Search → Trace ID).
💡 La seule différence avec vos panels du Lab 4 tient en une case cochée : dans les options de la requête, Exemplars. Elle vaut
"exemplar": truedans le JSON du panel — allez le vérifier, Panel → Inspect → Panel JSON.
- Pourquoi cette métrique en porte, et pas les vôtres.
app_cart_get_cart_latency_seconds_bucketest produite par le SDK OpenTelemetry du servicecart: au moment où il enregistre la durée, le SDK a letrace_iddu span en cours sous la main, et l’attache à la mesure. Vos panels, eux, affichenttraces_span_metrics_*, que le collecteur recalcule après coup à partir des spans — il ne joint aucuntrace_id, sauf si on le lui demande.
- Le constater sans Grafana. L’API de Prometheus répond directement :
La première réponse est pleine de trace_id en clair. La seconde tient en une ligne : {"status":"success","data":[]} — aucun exemplar, comme annoncé.
À retenir
Un exemplar est le pont entre deux signaux : la métrique repère l’incident et le situe dans le temps, l’exemplar désigne une requête précise, la trace l’explique. C’est le trajet complet que fait un astreinte — et il tient en un clic quand la chaîne est câblée de bout en bout : SDK qui attache le trace_id, Prometheus qui le stocke, datasource qui sait où ouvrir la trace.
La lecture détaillée du PromQL de ces panels — les seaux, le p95, la heatmap — est dans le Lab 4 bonus.