Sampling bedeutet, nur einen Teil der Telemetrie zu behalten, meist Traces, um Volumen und Kosten zu steuern. Head-based Sampling entscheidet beim Start eines Traces, in der Regel per Wahrscheinlichkeit. Tail-based Sampling entscheidet nach Abschluss des Traces und kann daher alle Fehler und langsamen Requests behalten, während Routine-Traffic verworfen wird.
Was ist Sampling?
Sampling reduziert die Menge der übertragenen und gespeicherten Telemetrie, indem nur eine repräsentative oder gezielt ausgewählte Teilmenge erhalten bleibt. Am wichtigsten ist das für Distributed Tracing: Ein Service mit 5.000 Requests pro Sekunde und 20 Spans pro Request erzeugt 100.000 Spans pro Sekunde, rund 8,6 Milliarden pro Tag. Alle zu behalten ist selten nötig, denn die meisten erfolgreichen Requests sehen gleich aus.
Ziel ist es, Redundanz zu verwerfen, ohne das Signal zu verlieren: Fehler, langsame Requests, seltene Codepfade und genug Basis-Traffic, um das Normalverhalten zu verstehen.
Sampling betrifft nicht nur Traces. Auch umfangreiche Debug-Logs, RUM-Events und Profiling-Daten lassen sich sampeln, Metriken werden dagegen auf anderem Weg reduziert, nämlich durch Aggregation. Unabhängig vom Signal sollte die Entscheidung bewusst getroffen und dokumentiert werden: Ein gesampelter Datenbestand ist nur dann nützlich, wenn alle, die ihn abfragen, wissen, was behalten und was verworfen wurde und zu welcher Rate. Nur so lassen sich Zählwerte richtig interpretieren oder hochrechnen.
Head-based Sampling
Beim Head-based Sampling fällt die Entscheidung beim Start des Root Spans, bevor bekannt ist, wie der Request endet. In OpenTelemetry-SDKs trifft ein Sampler diese Entscheidung:
AlwaysOn/AlwaysOff: alles behalten oder alles verwerfen.TraceIdRatioBased: einen festen Anteil behalten, deterministisch aus der Trace-ID abgeleitet. Jeder Service, der dieselbe Trace-ID sieht, kommt so zur selben Entscheidung.ParentBased: der Entscheidung des Parent Spans folgen, die im sampled-Flag des W3C-Headerstraceparentmitgeführt wird. Standard im SDK istParentBasedmitAlwaysOnfür Root Spans.
Head Sampling ist günstig und einfach, verworfene Spans werden nie exportiert. Seine Schwäche: Es entscheidet blind. Bei 5 Prozent werden 95 Prozent der fehlgeschlagenen Requests zusammen mit den erfolgreichen verworfen. Außerdem setzt es korrekte Context Propagation voraus; ignoriert ein Service das sampled-Flag, zerfallen Traces in Fragmente.
Tail-based Sampling
Tail-based Sampling wartet, bis alle Spans eines Traces eingetroffen sind, und entscheidet dann auf Basis des gesamten Traces. Im OpenTelemetry Collector übernimmt das der Processor tail_sampling: Er puffert Spans pro Trace-ID für eine konfigurierbare Dauer (decision_wait) und wertet anschließend Policies aus. Eine Beispielkonfiguration zur Veranschaulichung:
processors:
tail_sampling:
decision_wait: 10s
policies:
- name: keep-errors
type: status_code
status_code: {status_codes: [ERROR]}
- name: keep-slow
type: latency
latency: {threshold_ms: 1000}
- name: baseline
type: probabilistic
probabilistic: {sampling_percentage: 5}
Ein Trace bleibt erhalten, sobald eine Policy zutrifft. Diese Konfiguration behält also jeden Fehler, jeden Trace über einer Sekunde und 5 Prozent des Rests. Die Kehrseite: Der Collector braucht Speicher zum Puffern, Entscheidungen kommen verzögert, und alle Spans eines Traces müssen dieselbe Collector-Instanz erreichen. In skalierten Umgebungen leitet eine erste Collector-Schicht die Spans mit dem loadbalancing-Exporter nach Trace-ID an eine zweite Schicht weiter, die das Sampling übernimmt.
Head- und Tail-based Sampling im Vergleich
| Kriterium | Head-based | Tail-based |
|---|---|---|
| Entscheidungszeitpunkt | Beginn des Traces | Nach Abschluss des Traces |
| Alle Fehler und langsamen Traces behalten | Nein | Ja |
| Ressourcenbedarf | Minimal, im SDK | Speicher und CPU im Collector |
| Netzwerkersparnis | Ab der Anwendung | Erst nach dem Collector |
| Betriebsaufwand | Gering | Höher: Routing, Pufferung, Skalierung |
Viele Teams kombinieren beides: eine großzügige Head-Rate in sehr volumenstarken Services zum Schutz des Netzwerks und Tail Sampling im Collector, um zu entscheiden, was gespeichert wird.
Best Practices
- Metriken vor dem Sampling ableiten. Berechnen Sie Request-Rate, Fehlerrate und Latenz-Histogramme aus allen Spans, etwa mit dem Span-Metrics-Connector des Collectors, damit Dashboards und SLOs exakt bleiben.
- Sampling-Rate mitschreiben. Backends benötigen sie zur Hochrechnung: Bei 10 Prozent steht jeder behaltene Trace für zehn.
- Relevanten Traffic explizit behalten: Fehler, hohe Latenz, bestimmte Kunden oder Endpunkte und neue Releases.
- Logs und Traces konsistent sampeln, wenn sie korreliert sind, oder Logs zu nicht gesampelten Traces mit geringerem Detailgrad aufbewahren.
- Den Sampler selbst überwachen: Pufferauslastung, verspätete Spans und verworfene Traces zeigen, dass
decision_waitoder Kapazität angepasst werden müssen.
Typische Fehler
- Fehlerraten-Alerts auf gesampelten Traces aufbauen und sich dann über schwankende Werte wundern.
- Unabhängiges Zufalls-Sampling in verschiedenen Services, das unvollständige Traces erzeugt.
- Tail Sampling über mehrere Collector-Replikate ohne Routing nach Trace-ID.
- Eine einzige globale Rate, wodurch kritische Services mit wenig Traffic kaum noch Traces liefern.
Wie Obseria unterstützt
Obseria unterstützt Tail-based Sampling: Sie behalten jeden Fehler und jeden langsamen Trace und reduzieren gleichzeitig Routine-Traffic, während RED-Metriken vor dem Sampling aus dem vollständigen Datenstrom berechnet werden. Gefilterte Daten kosten nur Ingestion, damit bleibt konsequentes Sampling lauter Services auf Ihrer Rechnung planbar. Mehr dazu in unserem Leitfaden zu Tail-based Sampling mit dem OpenTelemetry Collector und zum Thema Observability-Kosten senken mit Pipeline-first.