Snippets · Processors
Reduce metric cardinality (aggregate away per-request attributes)
One attribute with a value per user or per request turns a metric into thousands of series, and most backends bill per series. aggregate_on_attributes keeps only the attributes you name and sums every data point that becomes identical, so a request counter per route stays a request counter per route.
OpenTelemetry Collector
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
processors:
memory_limiter:
check_interval: 1s
limit_percentage: 80
spike_limit_percentage: 25
# Keeps only the attributes dashboards group by, and sums the data points
# that become identical once the per-request attributes are gone.
transform/reduce-cardinality:
error_mode: ignore
metric_statements:
- context: metric
statements:
- aggregate_on_attributes("sum", ["http.request.method", "http.route", "http.response.status_code"]) where name == "http.server.request.count"
batch:
timeout: 5s
exporters:
otlphttp:
endpoint: https://your-backend:4318
service:
pipelines:
metrics:
receivers: [otlp]
processors: [memory_limiter, transform/reduce-cardinality, batch]
exporters: [otlphttp]
LinkMesh
LinkMesh has no built-in template for this job. Add it as a custom processor in raw mode, with transform as the component type and this body - the same lines as under transform/reduce-cardinality in the collector config:
error_mode: ignore
metric_statements:
- context: metric
statements:
- aggregate_on_attributes("sum", ["http.request.method", "http.route", "http.response.status_code"]) where name == "http.server.request.count"
How custom components are validated and rolled out: create a custom processor.
Download config.yamlValidated with otelcol-contrib v0.161.0 on 2026-10-26
Complete config files, each checked with the collector's own validate command (otelcol-contrib v0.161.0). Replace https://your-backend:4318 with your own OTLP endpoint before you run it.
Before and after
Example telemetry in, and what this exact config sent out when we ran it in otelcol-contrib v0.161.0. CI re-runs it on every change and fails if the output differs. Changed values are highlighted.
1 metric in, 1 metric out (4 data points in, 2 out)
Before
- metric
- http.server.request.count
- point 1
- 5 {"http.request.method":"GET","http.route":"/orders/{id}","http.response.status_code":200,"user.id":"u-1"}
- point 2
- 3 {"http.request.method":"GET","http.route":"/orders/{id}","http.response.status_code":200,"user.id":"u-2"}
- point 3
- 7 {"http.request.method":"GET","http.route":"/orders/{id}","http.response.status_code":200,"user.id":"u-3"}
- point 4
- 1 {"http.request.method":"GET","http.route":"/orders/{id}","http.response.status_code":404,"user.id":"u-1"}
- resource.service.name
- checkout
After
- metric
- http.server.request.count
- point 1
- 15 {"http.request.method":"GET","http.route":"/orders/{id}","http.response.status_code":200}
- point 2
- 1 {"http.request.method":"GET","http.route":"/orders/{id}","http.response.status_code":404}
- resource.service.name
- checkout
The gotcha
Deleting the attribute is not enough. In our test delete_key(attributes, "user.id") left all four data points in place, three of them now with identical attributes: duplicates a backend either rejects or overwrites, so the count is wrong either way. aggregate_on_attributes merged them instead: 4 points in, 2 out, 5 + 3 + 7 = 15.
Manage this across a fleet
LinkMesh manages collector configuration across a fleet from one self-hosted control plane, so a change like this is made once rather than host by host. The first 25 Collectors are free after a no-card registration in the OpenSight Customer Portal (5 without one).
Doing this in LinkMesh: Create a custom processor
Related snippets
- Drop sensitive attributes before exportDelete named attributes, and every captured HTTP request header, from logs, spans and metrics before they are exported.
- Deduplicate repeated log recordsCollapse a burst of identical errors into one record with a count, instead of storing it a thousand times.
- Drop all telemetry from a serviceDrop everything a load generator or synthetic probe sends, by service.name, across logs, metrics and traces.
- Drop health-check logs and spansStop paying to store Kubernetes probe traffic: drop /healthz, /readyz and /livez logs and spans on the collector.