Skip to content

Mask PII

Sensitive fields — credit-card numbers, email addresses, phone numbers, internal IDs — slip into logs constantly. Once they reach an observability backend they’re indexed, retained, and eventually queried by people who shouldn’t see them.

LinkMesh-managed collectors run the OpenTelemetry transform processor with OTTL expressions, so masking happens on the host, before any data leaves your network. There is no “trust the backend to redact it later” step.

What you’ll do

  1. Add a Transform step to a pipeline with one or more OTTL replace_pattern statements.
  2. Verify with the dry-run panel that the patterns get rewritten.
  3. Save + attach the pipeline to a route on the collector that handles sensitive logs.

1. Add the Transform step

Open the pipeline you want to add redaction to (or create a fresh one — see Build your first pipeline for the chain-editor walkthrough). Click + Add Processor.

The processor picker. 'Transform (custom)' opens an empty OTTL rule builder; the canned 'PII Redaction' template includes email + phone + card patterns out of the box.

For the common case, pick the canned PII Redaction template — it ships with three replace_pattern statements covering emails, phone numbers, and credit-card patterns. For project-specific PII (internal IDs, IBANs, custom tokens), pick Transform (custom) and write your own statements:

replace_pattern(body, "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}", "<email>")
replace_pattern(body, "[0-9]{4}[ -]?[0-9]{4}[ -]?[0-9]{4}[ -]?[0-9]{4}", "<card>")
replace_pattern(body, "INT-[A-Z0-9]{8}", "<internal-id>")

Each statement scans body (the log record’s message) and rewrites every match to a placeholder.

2. Dry-run against a sample

Open the dry-run panel and paste a sample event containing the PII you want to mask:

{
"resourceLogs": [{
"scopeLogs": [{
"logRecords": [{
"body": {
"stringValue": "POST /api/v2/cart/checkout user=jdoe@example.com card=4242 4242 4242 4242"
}
}]
}]
}]
}

Click Run. The Final Output panel shows the body rewritten: user=<email> and card=<card> replacing the originals.

Dry-run with PII Redaction applied. The step badge is OK + 3ms; the body field is rewritten with placeholders in place of the matched substrings.

3. Verify on a live collector

Save the pipeline and attach it via a route on a collector that’s actually handling sensitive traffic.

The fastest verification path: add a debug exporter alongside your real destination for a minute, watch journalctl -u alloy -f (or -u otelcol-contrib -f), and confirm the body field on outbound records contains the placeholders rather than the raw values. Remove the debug exporter once you’ve seen one redacted record — it’s noisy at sustained traffic.

How the generated config looks

For operators editing collector config directly (or auditing what the UI pushed), the underlying OTel YAML is:

processors:
transform/pii_mask:
log_statements:
- context: log
statements:
- replace_pattern(body, "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}", "<email>")
- replace_pattern(body, "[0-9]{4}[ -]?[0-9]{4}[ -]?[0-9]{4}[ -]?[0-9]{4}", "<card>")
service:
pipelines:
logs:
processors: [transform/pii_mask, batch]

The order in processors: matters: transform/pii_mask runs before batch so unredacted values are gone before records are buffered for export.

  • Pipeline — the reusable processor chain this recipe modifies
  • Route — wires the pipeline to a specific source-destination pair on a collector
  • Drop noisy logs — the Filter sibling of this Transform recipe
  • Trust on linkmesh.io — the broader rationale for redacting at the collector edge rather than the backend