Ein Exemplar ist ein einzelner Beispielmesswert, der an einen aggregierten Metrik-Datenpunkt angehängt wird, typischerweise zusammen mit Trace-ID und Span-ID des Requests, der ihn erzeugt hat. Exemplars erlauben es, von einem Latenz-Peak oder Fehlerzähler im Dashboard direkt zu einem repräsentativen Distributed Trace zu springen, ohne der Metrik Labels mit hoher Kardinalität hinzuzufügen.
Was ein Exemplar ist
Metriken sind Aggregate. Ein Latenz-Histogramm zeigt, dass 2 % der Requests in der letzten Minute länger als eine Sekunde dauerten, aber nicht, welche Requests. Traces zeigen genau, was in einzelnen Requests passiert ist, doch es gibt viel zu viele, um sie alle zu lesen. Ein Exemplar verbindet beides: Neben dem aggregierten Wert speichert die Metrik einige konkrete Beispielmesswerte, jeweils versehen mit der trace_id (und oft der span_id) des zugehörigen Requests.
Im Dashboard erscheinen Exemplars meist als Punkte über dem Zeitreihengraphen. Fährt man über einen Punkt im langsamen Bucket, sieht man Messwert, Zeitstempel und Trace-ID, ein Klick öffnet den passenden Trace. So wird aus „Die Latenz ist hoch“ in einem Schritt „Hier ist ein langsamer Request, und hier hat er seine Zeit verbracht“.
Warum Exemplars wichtig sind
Der Sprung von Metriken zu Traces ist einer der häufigsten Schritte in jeder Analyse. Ohne Exemplars suchen Engineers Traces im selben Zeitfenster und hoffen, einen langsamen zu finden. Das ist mühsam und oft irreführend. Exemplars liefern einen direkten, statistisch relevanten Verweis.
Zugleich lösen sie ein Kardinalitätsproblem. Würde man trace_id als Metrik-Label verwenden, entstünde für jeden Request eine neue Zeitreihe, und jede Zeitreihendatenbank wäre überlastet. Exemplars werden neben der Zeitreihe gespeichert, nicht als Teil ihrer Identität, die Kardinalität der Metrik bleibt also unverändert.
So funktionieren Exemplars in OpenMetrics und Prometheus
Das Expositionsformat OpenMetrics definiert Exemplars als optionalen Zusatz an Samples von Countern und Histogramm-Buckets, eingeleitet durch #:
http_request_duration_seconds_bucket{le="0.5"} 1270 # {trace_id="4bf92f3577b34da6a3ce929d0e0e4736"} 0.43 1729000000.123
http_request_duration_seconds_bucket{le="1"} 1302 # {trace_id="00f067aa0ba902b74bf92f3577b34da6"} 0.91 1729000001.456
Jedes Exemplar hat ein eigenes kleines Label-Set, den beobachteten Wert und einen optionalen Zeitstempel. OpenMetrics begrenzt die Gesamtlänge der Label-Namen und -Werte eines Exemplars auf 128 UTF-8-Zeichen. Prometheus speichert Exemplars, wenn es mit --enable-feature=exemplar-storage gestartet wird, hält sie in einem Ringpuffer fester Größe im Arbeitsspeicher, scrapt sie nur im OpenMetrics-Format und stellt sie über die API query_exemplars bereit. Per Remote Write lassen sie sich weiterleiten, wenn send_exemplars aktiviert ist.
Exemplars in OpenTelemetry
Das Metrik-Datenmodell von OpenTelemetry sieht Exemplars an Histogramm- und Summen-Datenpunkten vor, mit Trace-ID, Span-ID, Zeitstempel, Wert und gefilterten Attributen. Das SDK wählt über einen Exemplar-Filter und ein Exemplar-Reservoir aus, welche Messungen zu Exemplars werden. Der Filter lässt sich mit OTEL_METRICS_EXEMPLAR_FILTER konfigurieren:
| Wert | Verhalten |
|---|---|
trace_based | Standard. Nur Messungen innerhalb eines gesampelten Spans können Exemplars werden. |
always_on | Jede Messung kann ein Exemplar werden. |
always_off | Es werden keine Exemplars erfasst. |
Weil der Standard Exemplars an gesampelte Spans koppelt, existiert der referenzierte Trace mit hoher Wahrscheinlichkeit auch im Backend. Exemplars werden per OTLP gemeinsam mit den Metriken exportiert.
Beispiel: vom p99-Peak zur Ursache
In einem beispielhaften Szenario steigt die p99-Latenz einer Checkout-API um 14:05 Uhr von 450 ms auf 2,1 Sekunden, während der Median unverändert bleibt. Das Dashboard zeigt eine Häufung von Exemplar-Punkten im 2,5-Sekunden-Bucket. Die On-Call-Engineerin öffnet einen davon: Der Trace zeigt, dass der Request 1,8 Sekunden auf eine freie Verbindung im Connection Pool zur Inventar-Datenbank gewartet hat. Zwei weitere Exemplars aus demselben Bucket bestätigen das Muster. Innerhalb weniger Minuten führt die Analyse von einem aggregierten Symptom zu einer konkreten, reproduzierbaren Ursache, ohne Zeitfenster zu raten oder Tausende Traces von Hand zu filtern.
Best Practices
- Metriken innerhalb aktiver Spans erfassen, damit das SDK den aktuellen Trace-Kontext automatisch anhängen kann.
- Sampling abstimmen. Bei starkem Trace-Sampling müssen Exemplars auf behaltene Traces zeigen. Trace-basierte Filter und Tail-based Sampling, das langsame und fehlerhafte Requests behält, helfen dabei.
- Histogramme für Latenz verwenden. Exemplars in hohen Buckets sind der schnellste Weg zu den langsamsten Requests hinter einem hohen Perzentil.
- Den gesamten Pfad prüfen. Exporter, Collector und Backend müssen Exemplars unterstützen und weiterreichen, sonst gehen sie stillschweigend verloren.
Häufige Fehler
Ein häufiger Fehler ist es, Trace-IDs in reguläre Labels zu schreiben, was eine Kardinalitätsexplosion auslöst. Ein anderer: Exemplars sind in der Anwendung aktiviert, der Scrape nutzt aber das klassische Prometheus-Textformat ohne Exemplar-Unterstützung, sodass nichts ankommt. Außerdem wird vergessen, dass Exemplars Stichproben sind: Ein einzelnes Exemplar zeigt einen repräsentativen Request, aber keinen Beweis, dass alle langsamen Requests dieselbe Ursache haben.
Wie Obseria unterstützt
Obseria nimmt OpenTelemetry-Metriken mit Exemplars per OTLP entgegen und verknüpft sie mit den in Obseria Lake gespeicherten Traces. Aus jedem Latenz- oder Fehlerdiagramm öffnen Sie einen repräsentativen Trace im Distributed Tracing und gelangen weiter zu zugehörigen Logs und Profilen. Details zur Einrichtung finden Sie unter Was ist OpenTelemetry? und in der Prometheus-Integration.