OpenTelemetry Tail Sampling: Full Fidelity on Errors, Sparse on Success

OpenTelemetry Tail Sampling: Full Fidelity on Errors, Sparse on Success
24 views
Head sampling decides at the root span whether a trace lives—before you know it failed. Tail sampling waits until the trace completes, then keeps 100% of errors and slow requests while sparsely keeping healthy fast paths. That is how you stay useful under high RPS without drowning exporters.
⚡ TL;DR: Collect with a Collector pipeline that buffers traces, then apply policies: always_sample on status=ERROR, latency thresholds, and rare attributes; probabilistic sample the rest. Propagate consistent trace IDs. Pair with OpenTelemetry Sampling for Node, OpenTelemetry for LLMs, and X-Ray Service Maps Lie.

OpenTelemetry Tail Sampling: production guidance

text
Head sampling:
 ingress decides keep/drop immediately
 → cheap, but drops failures that look fine at start

Tail sampling:
 all spans land in Collector briefly
 → policy: keep errors + p99 latency + 1% success
 → drop the rest
At fifty thousand RPS you cannot keep everything; you also cannot afford to miss the only failing trace for a bad deploy.

Collector tail_sampling sketch

yaml
processors:
 tail_sampling:
 decision_wait: 10s
 num_traces: 100000
 expected_new_traces_per_sec: 20000
 policies:
 - name: errors
 type: status_code
 status_code: { status_codes: [ERROR] }
 - name: slow
 type: latency
 latency: { threshold_ms: 1500 }
 - name: checkout
 type: string_attribute
 string_attribute:
 key: http.route
 values: ["/v1/checkout", "/v1/pay"]
 enabled_regex_matching: false
 - name: probabilistic-rest
 type: probabilistic
 probabilistic: { sampling_percentage: 1 }
typescript
// App still records all spans locally; sampling decision is centralized
import { NodeSDK } from "@opentelemetry/sdk-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";

const sdk = new NodeSDK({
 traceExporter: new OTLPTraceExporter({ url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT }),
 // Prefer parent-based + Collector tail policies over aggressive SDK head drop
});
✅ Central Collector policies owned by platform.
❌ Each service invents incompatible head-sample ratios.

Cost and incident playbooks

During sev-1, temporarily raise probabilistic percentage or add an attribute policy for the suspected service—then revert. Same mindset as Lambda X-Ray Sampling. Watch Collector memory: decision_wait and num_traces are knobs that OOM if undersized. For LLM tool chains, keep error spans and high token-latency traces—see OpenTelemetry for LLMs. Fix missing edges so tail policies see complete traces (X-Ray Service Maps Lie).

Closing checklist

  • [ ] Collector tail_sampling keeps ERROR and high-latency traces at 100%
  • [ ] Success paths use low probabilistic sample (e.g. 0.5–2%)
  • [ ] Critical routes can force-keep via attribute policies
  • [ ] decision_wait / buffer sized for peak RPS without OOM
  • [ ] Incident runbook includes temporary sample bump + revert
  • [ ] Trace context propagates across Node, Lambda, and LLM tools

Related reading

Last updated on September 11, 2026 Related: TypeScript Generics Deep Dive: Write Reusable Code With Full Type Safety; SQL Joins Explained: INNER, LEFT, RIGHT, FULL, CROSS, and Self Joins; Python PyO3 Extensions: Port Hot Paths Without Full Rewrite Risk.

Watch: 100 Days of AI on YouTube

Short videos from CheatCoders, one AI engineering topic per day.

Open the playlist on YouTube · CheatCoders on YouTube

Deep-dive PDF

Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.

Already subscribed? or open the subscribe page.


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a comment

No account needed. Name and email are optional.