Snippets · Processors
Redact everything except an attribute allowlist
Masking by pattern only catches what you thought of. The redaction processor works the other way round: list the attribute keys you need, and every other key is removed before export, including the ones a developer adds next month. Values on kept keys that match a blocked pattern are replaced with ****. Use it where an unknown attribute is a bigger risk than a missing one; use the mask-pii snippet when you want the record intact and only the value rewritten.
- Signals
- Logs
- Category
- Compliance
- Source
- OTLP
- Components
- otlpbatchmemory_limiterotlphttp
- Destination
- OTLP/HTTP endpoint
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 attribute keys listed, removes every other one, and masks
# any kept value that matches a blocked pattern.
redaction:
allow_all_keys: false
allowed_keys:
# Resource attributes are redacted too: without these the backend
# receives every record as an unknown service.
- service.name
- service.namespace
- deployment.environment.name
- host.name
# Record attributes this pipeline needs.
- http.request.method
- http.response.status_code
- http.route
- url.path
- user.email
blocked_values:
- "[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*\\.[A-Za-z]{2,}"
- "\\b(4[0-9]{3}|5[1-5][0-9]{2}|2[2-7][0-9]{2})([ -]?[0-9]{4}){3}\\b"
summary: info
batch:
timeout: 5s
exporters:
otlphttp:
endpoint: https://your-backend:4318
service:
pipelines:
logs:
receivers: [otlp]
processors: [memory_limiter, redaction, batch]
exporters: [otlphttp]
LinkMesh
LinkMesh has no built-in template for this job. Add it as a custom processor in raw mode, with redaction as the component type and this body - the same lines as under redaction in the collector config:
allow_all_keys: false
allowed_keys:
- service.name
- service.namespace
- deployment.environment.name
- host.name
- http.request.method
- http.response.status_code
- http.route
- url.path
- user.email
blocked_values:
- "[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*\\.[A-Za-z]{2,}"
- "\\b(4[0-9]{3}|5[1-5][0-9]{2}|2[2-7][0-9]{2})([ -]?[0-9]{4}){3}\\b"
summary: info
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 log record in, 1 log record out
Before
- body
- checkout failed for anna.meier@example.ch
- attributes.http.request.method
- POST
- attributes.http.route
- /checkout
- attributes.http.response.status_code
- 502
- attributes.user.email
- anna.meier@example.ch
- attributes.http.request.header.authorization
- Bearer abc
- attributes.session.id
- s-81723
- resource.service.name
- shop
- resource.host.name
- web-1
After
- body
- checkout failed for ****
- attributes.http.request.method
- POST
- attributes.http.route
- /checkout
- attributes.http.response.status_code
- 502
- attributes.user.email
- ****
- attributes.redaction.redacted.count
- 2
- attributes.redaction.masked.count
- 1
- attributes.redaction.body.masked.count
- 1
- resource.service.name
- shop
- resource.host.name
- web-1
The gotcha
The allowlist applies to resource attributes too. In our test, with only record-level keys listed, service.name and host.name were removed from the resource, so the records left the collector with no service name at all. That is why service.name, service.namespace, deployment.environment.name and host.name are on the list here. The processor also masked the blocked e-mail pattern in the log body, not only in attributes.
What it does not catch
It removes and masks keys and values, it does not inspect meaning: a name or address in a kept attribute or in the body passes through unless a blocked_values pattern matches it. summary: info adds redaction.* count attributes to each record; set summary: silent if your backend should not see them.
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.
- Enforce required resource attributes (semantic conventions)Refuse telemetry that cannot be attributed - drop anything without service.name, service.namespace and deployment.environment.name.
- Pseudonymise user IDs with a salted hashReplace user.id with a salted SHA-256 on logs and spans - same user, same value, so per-user queries still join.
- Mask PII in logs (e-mail, card numbers, IBAN, tokens, IPs)Rewrite e-mail addresses, card numbers, IBANs, bearer tokens and IPs to placeholders on the collector, before the data leaves your network.