Guide · OpenTelemetry Signal Loss

The Engineer’s Guide to OpenTelemetry Signal Loss

Why sampling, filtering, and transforming drop the data you’ll need. A guide to OpenTelemetry volume, noise, and the bill.

By , Founder, ControlTheory Published: September 11, 2026 20 minute read For SREs, platform teams, and anyone who owns a collector and a bill

The short answer: every tool the OpenTelemetry Collector gives you for controlling volume works by deciding in advance what to throw away. The filter processor, the transform processor, cardinality limits, and tail sampling all encode what you already know isn’t worth keeping. The failures that hurt are the ones nobody wrote a rule for, and a rule-based pipeline drops them first, because dropping them is what saved the money. This guide maps where signal is lost, what each technique actually costs you, and the alternative, Telemetry Distillation: reduce volume by collapsing repetition and baselining each service, so the bill goes down and the unknowns stay visible.

Key takeaways

  • OpenTelemetry signal loss is caused by telemetry removed by a Collector pipeline that was tuned for the failures you already know about, so the failure you didn’t predict leaves with it.
  • Log filtering by severity, transform statements, memory-limiter backpressure, cardinality limits, and tail sampling each lose signal in a specific, predictable place.
  • Observability bills are driven by three volume axes: ingested bytes, indexed or retained events, and distinct metric series. Most of that volume is repetition, not information. A cardinality explosion is the one that moves nonlinearly: one unbounded attribute can multiply series count overnight and take the backend down with the bill.
  • Telemetry Distillation reads the content of every record, not its severity or status code, and reduces volume by collapsing repetition into patterns with counts and baselining each service. Nothing is sampled or filtered; every record is read, and what comes out is the distilled signal with the context that supports it. Signal is separated from noise by what the record says, so what’s new survives instead of what a rule predicted.
  • It fits an existing pipeline as one added otlphttp exporter. Start on a staging collector, keep your current backend exporter, and run redaction before both. Nothing is replaced.
Definition

What is OpenTelemetry signal loss?

OpenTelemetry signal loss is telemetry that would have answered a question you hadn’t asked yet, removed by a Collector pipeline that was configured to answer the questions you already had. It is caused by rule-based volume reduction (sampling, filtering, transforming, cardinality limits) deciding at configuration time which data won’t matter. It produces no error. It shows up later, as a debugging session with thin logs, a missing trace, or a metric whose identifying dimension was dropped. It has a second form: the answer is still in the data, but buried in volume no rule surfaced and no human will read. Both forms get worse as AI-generated code, autonomous agents, and continuous deploys expand the surface area faster than anyone can write rules for it.

The term is deliberate. “Volume reduction” describes what a processor does. “Signal loss” describes what it costs, and the cost is invisible until the one time you needed the data. This guide uses the term for two cases. The first is removal: reduction dropped the evidence of a failure nobody had predicted. The second is burial: the evidence survived, but in a volume no rule flagged and no person will read, which is loss in every practical sense. Pre-configuration can’t cover either, because the questions keep changing. New AI behaviors, new services shipped by agents, and a blast radius that widens with every deploy generate questions nobody had asked when the config was written. That is the condition a pipeline has to be designed for now: not a fixed set of known failures, but a continuous, adaptive loop where the thing that matters is whatever is new.

The volume problem

Why does OpenTelemetry produce more telemetry than you can afford?

OpenTelemetry produces more telemetry than most teams can afford to store because auto-instrumentation, replicas, and sidecars multiply the number of streams, and every backend charges by volume. OpenTelemetry solved the collection problem so thoroughly that it created the next one. Auto-instrumentation attaches to every framework call. Every replica emits its own stream. Every sidecar adds another. A Kubernetes cluster of modest size can produce tens of millions of log lines a day, and the Collector sits in the middle of all of it as the one place you can actually do something about the flow.

Which is why the Collector has become a cost-control device. Most of the processors teams reach for after the first bill are there to make telemetry smaller before it reaches a backend that charges by the gigabyte, the indexed event, or the custom metric. The deployment pattern you choose is often chosen for where the reduction happens: at the agent, at the gateway, or both.

None of that is wrong. Storing everything is a bad idea for reasons that have nothing to do with money; nobody reads forty million lines. The question this guide is about is narrower: how you reduce, and what the method you pick does to your ability to find the failure you didn’t predict.

The Collector’s reduction toolkit is a set of rules. Every rule is a prediction about which data won’t matter.

That’s the shape of the problem. A rule in a processor config is a decision made at configuration time about the value of data you haven’t seen yet. It’s correct for the failures you’ve already had, because those are the failures you wrote the rule around. It is exactly wrong for the failures you haven’t had, because the data that would reveal them is, by definition, the data the rule considered unremarkable.

Call what happens next signal loss: telemetry that would have answered a question you hadn’t asked yet, removed by a pipeline that was tuned to answer the questions you already had. It doesn’t show up as an error. It shows up as a debugging session where the logs from the relevant window are thin, the sampled trace isn’t there, and the metric that would have shown the ramp had its dimension dropped for cardinality.

The reduction toolkit

Where does each technique lose signal?

Signal is lost at five points in a typical Collector pipeline: log filtering by severity, transform statements, memory-limiter backpressure, cardinality control, and tail sampling. Batching, compression, and cold-storage tiering reduce cost without losing signal, because they’re reversible. Five techniques account for most volume reduction in production collectors. Each has a legitimate job. Each also has a specific blind spot, and the blind spots are rarely written down next to the config examples.

TechniqueWhat it dropsWhat it costs youWhere it fails silently
filter (logs) Records matching an OTTL condition: usually severity below a threshold, or known-noisy sources. Signal loss INFO is where new patterns first appear. Filtering by severity keeps the failures you already log as errors and drops the ones you don’t yet know are failures. A rule written for one service’s noise applied fleet-wide. The condition is still true; the data behind it changed.
transform (OTTL) Attributes, bodies, or whole fields, rewritten or removed. Redaction, normalization, truncation. Diagnostic blindness A truncated body or a stripped attribute isn’t missing data, it’s data that can no longer be grouped, joined, or explained. Statements that were correct for the log shape at write time. A framework upgrade changes the shape; the statement keeps running.
memory_limiter and backpressure Whatever arrives when the collector is over its limit. Refused, retried, or dropped depending on the receiver. Signal loss Data lost exactly when the system is under stress, which is exactly when you wanted it. Drops are logged by the collector, not by the backend. If nobody watches collector self-telemetry, the loss is invisible downstream.
Cardinality control (filter, attributes, metric aggregation) High-cardinality dimensions on metrics: pod IDs, request IDs, user IDs, anything unbounded. Signal loss The dimension you dropped is the one that would have told you the regression was one pod, one tenant, or one code path. The metric still exists and still looks healthy. The aggregate hides the outlier that the dropped dimension would have exposed.
tail_sampling Traces that don’t match a policy (no error, under the latency threshold, not in the probabilistic slice). Signal loss The slow-but-succeeded path that’s about to become the incident. The trace of the request that was fine right before the ones that weren’t. Spans from one trace landing on different collector instances. Decisions made on partial traces look like decisions made on whole ones.
batch, compression, S3 tiering Nothing. These reduce cost by changing where or how data is stored, not whether it exists. Cost only Latency to query, rehydration effort for cold data. Cold storage you can rehydrate from is a different class of decision: it’s reversible. Everything above this row isn’t.

The filter processor: severity is the wrong axis

The most common log-volume rule in production is some version of “drop DEBUG and INFO in prod.” It’s the cheapest rule to write and it usually cuts volume in half. It also assumes that the person who wrote the logging knew which lines would matter, and encoded that as severity. For hand-written services that was roughly true. For code written at AI speed it isn’t, because the severity levels are whatever the model emitted, and the new failure mode almost always announces itself first as an INFO line that says something slightly different from yesterday’s INFO lines. Filter by severity and you’ve kept the errors you already handle and dropped the sentence that would have told you a new one was starting.

This is the same structural problem the Invisible LLM Failures guide documents from the other side: for LLM APIs, the 200s are where the failures live. For logs, the INFOs are.

The transform processor: a correct statement about a shape that changed

The transform processor and OTTL are the right tools for redaction, normalization, and attribute hygiene, and nothing in this guide argues against them. The loss here is subtler than dropping records: a truncated body or a removed attribute leaves the record in place but removes what made it groupable. A pattern detector downstream sees ten thousand distinct lines instead of one pattern with a count of ten thousand. The statement did what it said. The data lost its shape.

Cardinality: the dimension you dropped was the diagnosis

Metric cardinality is the reduction nobody feels good about, because the backend forces it. Dropping high-cardinality attributes at the collector is often the only thing between you and a bill that doubles. But the unbounded dimension is unbounded because it identifies something specific: a pod, a tenant, a request. When the p99 moves, the question is always which one, and the aggregate that survived cardinality control can’t answer it. The metric stays green while one tenant burns.

Tail sampling: keeping the traces you already know matter

Tail sampling (the tail_sampling processor in collector-contrib) is, in many situations, the most powerful reduction technique in the toolkit, because it decides after seeing the whole trace. Keep every error, keep everything over the latency threshold, keep a probabilistic slice of the rest. That’s a good policy. It’s also a precise description of what it can’t see: the request that succeeded in 400ms, three minutes before the same code path started failing. That trace is the one that shows what normal looked like right before it stopped. It didn’t match a policy, so it’s gone.

There’s a mechanical failure mode too. Tail sampling only works when all spans of a trace reach the same collector instance, which means a load-balancing exporter tier in front of it, or trace-aware routing. Without that, the processor is making decisions on partial traces and reporting them as complete. The gateway pattern exists partly to make this work.

What it optimizes for: the failures you’ve already classified. What it drops: the baseline that would explain the next one.

Every technique in the toolkit works by predicting what you won’t need. None of them can predict what you don’t know yet.
The bill

Where does the money actually go?

Observability bills are driven by three volume axes: bytes ingested, events indexed or retained, and the count of distinct metric series. Signal loss happens because someone was trying to control one of those three, so it’s worth being precise about the bill. Backend pricing models differ in detail and agree in shape: you pay for what you ingest, you pay again for what you index or retain, and you pay a third time for custom metrics by the count of distinct series. Every one of those axes is a volume axis, and every reduction technique above is aimed at one of them. Sampling and filtering attack ingest. Transform attacks indexed size. Cardinality control attacks series count.

The third axis deserves its own warning. Ingest and index grow roughly with traffic. Series count doesn’t; it grows with the product of attribute values, so a single new unbounded attribute on a busy metric (a request ID, a pod name in a rolling deploy, a user ID) can multiply series by orders of magnitude between one invoice and the next. The bill is the visible damage. The invisible damage is backend ingestion limits and query timeouts during exactly the incident the metric was meant to explain. That’s the cardinality explosion incident, and it’s why cardinality control is the one reduction rule almost nobody argues against, even though it removes the answer to “which one.”

The Datadog cost work we’ve published over the years comes down to a short list of levers: control what leaves the collector, tier what you don’t need hot into object storage you can rehydrate from, and stop paying index prices for repetition. The last one is the interesting one, because repetition is most of the volume and almost none of the information. A retry storm is thirty thousand lines and one fact.

That’s the opening for a different approach. If most of the bill is repetition, the cheapest way to cut it is not to guess which repetition to drop, but to collapse it: keep the fact, keep the count, keep one exemplar, drop the copies. That reduces volume by the same order of magnitude as aggressive filtering, without a rule that predicts what won’t matter. The rest of this guide is about doing that in the pipeline.

The alternative

How do you reduce volume without deciding in advance what to drop?

You reduce volume without deciding in advance what to drop by collapsing repetition instead of selecting records: read every record, group identical patterns into one entry with a count, baseline each service against itself, and flag what’s new against that baseline. That is content-based reduction rather than rule-based reduction. We call it Telemetry Distillation. (Not to be confused with model distillation in machine learning, which compresses a model; this compresses the telemetry.) It’s the same idea as the “distill, enrich, explain, remember” pipeline in How to Trust What Your Agents Ship, applied at the collector boundary instead of at the AI layer.

Rule-based reduction versus Telemetry Distillation Two pipelines side by side. In the top pipeline, a rule-based processor drops data according to a fixed policy before export, and the unknown failure is lost with it. In the bottom pipeline, a Telemetry Distillation tap receives a copy of the full stream, collapses repetition into patterns with counts, baselines each service, and surfaces the unknown failure as a new pattern while the backend receives only what it needs. RULE-BASED REDUCTION Receivers everything arrives sample · filter · drop attributes policy decided at config time Backend smaller bill the failure nobody wrote a rule for leaves here TELEMETRY DISTILLATION Receivers everything arrives collapse · baseline · detect new decided by content, continuously Backend smaller bill the failure nobody wrote a rule for surfaces as a new pattern DSTL8 · CONTROLTHEORY.COM

Rules drop by policy set at config time. Telemetry Distillation drops repetition by content, and the thing that’s new is the thing that survives.

Rule-based reductionTelemetry Distillation
Decision madeAt configuration time, by a personContinuously, by content
What it removesRecords matching a policy (severity, sample, attribute)Copies of repeated patterns; the pattern and count remain
What it keepsWhat you predicted would matterOne exemplar per pattern, plus anything new against baseline
Unknown failuresDropped first; no rule anticipated themSurfaced first; they’re the patterns that don’t match
Per-service tuningManual, drifts across a fleetLearned per stream
Where it runsIn the export path; changes what the backend receivesBeside the export path, as an added exporter
ReversibleNoNo for the copies; the pattern, count, and exemplar are the record

It also answers both forms of signal loss from the definition. Removal, because nothing is dropped by prediction; what’s new is what survives. Burial, because collapsing thirty thousand copies into one pattern with a count is exactly what makes the one line that’s different visible. The needle was never hard to see. The haystack was.

Five properties make Telemetry Distillation a different kind of reduction:

It reads the content, not the metadata. Every reduction rule in the toolkit keys on something outside the message: a severity level, a status code, a sample rate, an attribute name. Telemetry Distillation reads the actual text of every record and scores it: what it says, how it reads (a line that reads like a failure is a failure regardless of the level someone gave it), whether it’s a pattern seen before, whether it’s anomalous against this service’s baseline. That’s the mechanism behind the rest of this list. Signal and noise get separated by what the record contains, which is the only place the distinction actually lives.

It collapses, it doesn’t select. Thirty thousand identical retry lines become one pattern with a count of thirty thousand and a timestamp range. The information is intact; the copies are gone. This is where most of the volume goes, and it goes without anyone predicting anything. To be exact about it: nothing is sampled and nothing is filtered. Every record is read. The reduction is in repetition, not in coverage. What comes out the other side is the distilled information (the pattern, its count, its time range, the attributes it carries) with an exemplar and the surrounding context to prove it, which is the property that lets it catch the failure no rule anticipated. A sampled stream might have kept that record. A read stream did.

Nothing sampled. Nothing filtered. Every record read. What comes out is the distilled signal, with the context to prove it.

It’s baselined per service. What counts as noise in checkout-api is not what counts as noise in auth-service. Rules are written once and applied everywhere. Baselines are learned per stream and move with the service.

New is the signal. A pattern that exists today and didn’t yesterday is the definition of an unknown unknown. A rule can’t find it because the rule would have had to anticipate it. A baseline finds it because it’s the thing that doesn’t match.

It sits beside the backend, not in front of it. The collector still exports to whatever you export to today. Telemetry Distillation receives a copy. Nothing in the existing path changes, which matters for the same reason tail sampling’s gateway tier matters: you don’t restructure a production pipeline to try something.

What this looks like in a collector config

The mechanical change is one exporter, added alongside the one you have. The snippet below points a staging collector at a Telemetry Distillation endpoint; the redaction block is there because the data leaving your cluster should be data you chose to send.

processors:
  transform/redact:
    log_statements:
      - context: log
        statements:
          # keep the shape, drop the payload you don't want to leave the cluster
          - replace_pattern(body, "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}", "[email]")
          - delete_key(attributes, "authorization")

exporters:
  otlphttp/dstl8:
    endpoint: https://<org_id>.app.dstl8.ai/src-<id>
    headers:
      Authorization: "Bearer <your-token>"

service:
  pipelines:
    logs:
      receivers: [otlp, filelog]
      processors: [k8sattributes, transform/redact, batch]
      exporters: [otlp/existing-backend, otlphttp/dstl8]   # add dstl8, keep what you have

Two things to notice. The existing exporter is still there; this is a tee, not a replacement. And the redaction runs before either export, so the same rules protect both paths. If you want to see the effect before you send anything anywhere, the remote tap processor shows you the actual payloads leaving a pipeline, which is the right first step for any change to an exporter.

The fleet

What changes when you have fifty collectors instead of one?

At fleet scale the problem changes from “is this rule right” to “is this rule still right everywhere,” because reduction rules drift across collectors and their failure mode is silence. Everything above assumes one collector config you can reason about. Most production estates have dozens, and the reduction rules in them drift. The filter that was right for one namespace gets copied to another. A transform statement written for one log shape runs against a service that upgraded its framework. Nobody notices, because the failure mode of a wrong rule is silence, not an error.

This is the case for OpAMP (the Collector management approach the project documents): centralized configuration, health, and rollout for the collector fleet, so the rules are at least the same everywhere and changes are deliberate. It solves the config side of the problem. It doesn’t solve the signal side, because a consistent rule that drops the wrong thing drops it consistently.

Fleet-level config wants fleet-level signal to go with it. If Telemetry Distillation runs against every collector’s output, “what’s new” is answered across the fleet, not per instance, and the effect of a config rollout shows up as a change in the pattern baseline. That’s the feedback loop the collector management problem has been missing: not just “did the config apply” but “what did it do to the signal.”

Testing the pipeline

How do you know what your pipeline is dropping?

You find out what a pipeline is dropping by comparing known input to observed output: generate telemetry with known counts, tap the exporter to see the payloads actually leaving, and watch the collector’s own refused and dropped counters. Most teams don’t do any of the three, because there’s no test for absence. These practices make it visible.

Generate known telemetry and count what arrives. telemetrygen and otelgen produce traces, metrics, and logs with known shapes and counts. Run them through the pipeline and compare what the backend received to what was sent. The difference is your reduction. Look at what’s in it.

Tap the pipeline at the exporter. The remote tap processor lets you watch the payloads actually leaving, after every processor has run. This is the only way to see what a transform statement did to a body, or whether the filter is dropping what you think it’s dropping.

Watch collector self-telemetry. The collector reports its own refused, dropped, and failed counts. If memory_limiter is shedding data under load, that’s the only place it shows up. Almost nobody alerts on it. It’s the difference between knowing you lost signal and finding out from a customer.

All three are worth doing regardless of anything else in this guide. They’re also how you’d measure whether Telemetry Distillation is keeping what the rules were dropping: generate the pattern the rules don’t know about, and see which path surfaces it.

The AI-speed connection

Why is this getting worse?

The definition ended on the condition a pipeline has to be designed for: questions that keep changing. This is why. Signal loss is getting worse because AI-assisted development produces telemetry with no human intent behind the instrumentation, which removes the assumption every reduction rule depends on. Two things happened to telemetry at the same time. Auto-instrumentation made it cheap to emit. AI-assisted development made it cheap to write the code that emits it. The second one changes the character of the data, not just the volume: when an agent writes a service, nobody decided what got logged. The severity levels, the messages, the attributes are whatever the model produced. There’s no intent encoded in the instrumentation for a rule to key on.

That’s the structural reason rule-based reduction is losing ground. Rules worked when a human wrote the logs with the same mental model that would later write the filter. When code ships faster than anyone can follow, the platform engineer inherits telemetry they didn’t shape, from code nobody in the room wrote, and is asked to decide in advance which of it won’t matter.

The same fact is the reason Telemetry Distillation serves two readers at once. The platform engineer needs it because they’re paying for volume they can’t predict. The developer shipping with Claude Code or Cursor needs it because they need to know what their agent’s code actually did at runtime, and the answer has to arrive distilled, cited, and in their editor, not as a firehose. How to Trust What Your Agents Ship makes that case for the leadership reader; The Agentic Engineer’s Guide to Runtime Feedback shows the wiring. This guide is the collector-side view of the same problem: the signal has to survive the pipeline before anyone, human or agent, can read it.

This is what Dstl8 does with OTLP

Dstl8 is Telemetry Distillation for the stream your collector already produces

Dstl8 receives OTLP natively: one otlphttp exporter added to an existing pipeline, alongside whatever you export to today. It reads the content of every record regardless of severity, collapses repetition into named patterns with counts, baselines each stream against itself, and surfaces what’s new. Möbius, the agent inside Dstl8, correlates a new pattern to the deploy, config rollout, or upstream change that preceded it, with the evidence cited. The result is a smaller bill for what you store hot, and a view of the failures your rules never knew to keep. Start with a staging collector. Nothing in the existing path changes.

Add the exporter (OTLP source docs) Book a 20-minute pipeline session
The honest part

Should you stop sampling and filtering?

No. Here is the part vendors tend to skip: some rules are correct and should stay. Health-check spam from a load balancer is not signal and never will be. A redaction statement that keeps PII inside the cluster is a requirement, not a reduction. Cardinality limits on a metric that would otherwise create a million series are keeping the backend alive. Keep those.

What this guide argues against is narrower: using rules as the primary mechanism for making telemetry affordable, because the rules that save the most money are the ones that guess the most about what won’t matter. Severity filters. Broad probabilistic sampling. Dropping the dimension that identifies the tenant. Those are the rules that produce signal loss, and they’re the ones Telemetry Distillation replaces.

The practical posture: keep the rules you can defend as requirements, remove the rules you wrote to hit a number, and let content-based reduction hit the number instead. Then test it, with the three practices above, so “we’re not dropping anything important” is a measurement and not a belief.

The resource files

Where do you go when you’re configuring one of these right now?

Every technique in this guide has a working how-to behind it: configuration, examples, and the failure modes we’ve hit. In the order you’d actually read them.

Collector fundamentals

Reducing volume, and what it costs

Datadog and the collector

Fleet management

Testing the pipeline

When the collector itself is the incident

Sources

References

1. OpenTelemetry project, Collector management and OpAMP.
2. OpenTelemetry project, Collector deployment patterns.
3. opentelemetry-collector-contrib, tail_sampling processor README: policies, decision_wait, and the requirement that all spans of a trace reach one instance.
4. ControlTheory, How to Trust What Your Agents Ship: the distill, enrich, explain, remember pipeline and the 2026 survey data on AI-generated code in production.
5. ControlTheory OpenTelemetry resource library, linked throughout and indexed above.

FAQ

Frequently asked questions

What is OpenTelemetry signal loss?

Telemetry that would have answered a question you hadn’t asked yet, removed by a Collector pipeline tuned to answer the questions you already had. Sampling, filtering, transforming, and cardinality limits all decide at configuration time what won’t matter. Signal loss is what happens when the failure you didn’t predict is in the data those rules dropped, or is still in the data but buried in volume no rule surfaced and no person will read.

Does tail sampling lose important traces?

It keeps the traces that match a policy, typically errors and high latency, and drops most of the rest. What it can’t keep is the trace that didn’t match yet: the request that succeeded normally right before the same path started failing. It also makes decisions on partial traces if spans for one trace reach different collector instances, which is why it needs a load-balancing tier in front of it.

Is filtering logs by severity a bad idea?

It’s the cheapest volume rule and it assumes the person who wrote the logging encoded importance as severity. For code written at AI speed that assumption fails, because the severity levels are whatever the model emitted. New failure modes usually appear first as an INFO line that says something slightly different from yesterday’s. Severity filtering keeps the errors you already handle and drops the sentence that announces a new one.

How do you reduce OpenTelemetry volume without sampling or filtering?

By collapsing repetition instead of selecting records: read every record, group identical patterns into one entry with a count, baseline each service against itself, and surface what’s new against that baseline. This is Telemetry Distillation: nothing is sampled or filtered, every record is read, and what comes out is the distilled signal with its supporting context rather than a subset of raw records. Most telemetry volume is repetition with almost no information, so this cuts volume by a similar order of magnitude to aggressive filtering, without a rule that predicts what won’t matter.

How does Dstl8 fit into an existing Collector pipeline?

As one additional otlphttp exporter alongside the one you already have. Dstl8 receives the OTLP stream, distills it into patterns and baselines, and Möbius correlates new patterns to the changes that caused them. The existing export path is unchanged. Most teams start with a staging collector and a redaction transform in front of both exporters.

Your rules are keeping the failures you’ve already had. See what they’re dropping.

One exporter on a staging collector. Nothing replaced. Patterns, baselines, and what’s new, cited to the record.

Add the exporter Book a pipeline session
brew install control-theory/dstl8/dstl8 && dstl8 setup

Want to see the patterns in one stream first, without anything leaving your machine? Gonzo reads OTLP and log streams in the terminal, free and open source. When you want it across the fleet, Dstl8 is one exporter away.