Skip to content

Mask PII

Sensitive values — card numbers, email addresses, phone numbers, national IDs — slip into logs constantly. Once they reach an observability backend they are indexed, retained, and eventually queried by people who should never have seen them.

The PII Masking processor redacts them on the collector, before any data leaves the host. You build rules from a library of maintained detectors; LinkMesh compiles those rules into the collector’s own configuration and pushes it to the edge. There is no “trust the backend to scrub it later” step.

A masking rule is three choices: what to find (a detector, or your own pattern), what to do with it (a mode), and which text to look in (a scope).

What gets stored on the pipeline is those three choices — not the resulting expression. LinkMesh compiles the rules into collector configuration each time it generates a config. This matters more than it looks: a pipeline that stored a rendered expression would keep whatever the pattern looked like on the day it was saved, and would quietly stop matching new formats as the detector library improved. Storing the intent means an improved detector reaches every pipeline already using it.

Open the pipeline you want to redact in (or create one — see Build your first pipeline) and click + Add Processor, then choose PII Masking.

The processor picker. 'PII Masking' opens the rule builder; the generic 'Transform (custom)' entry is still there for non-PII rewriting.
  1. Pick a detector. The picker groups the library by category — contact details, network addresses, financial identifiers, national IDs. Each detector is pinned to a version, so the rule you save keeps behaving the way it did when you tested it.

  2. Pick a mode — what replaces the value it finds.

  3. Pick a scope — which text the rule looks in.

  4. Read the notes. The builder shows the detector’s own over-match and miss notes, plus what the chosen mode costs. These are the two reasons a masking rule behaves differently from what its name suggests, so they are shown before you save rather than discovered later in a dashboard.

  5. Save. Modes that cannot work for the detector and scope you picked are never offered in the first place, so a rule you can build is a rule that compiles.

Screenshot pendingpipelines/pii-mask-rule-builder
The masking rule builder: detector, mode and scope per rule, with the detector's over-match and miss notes shown inline before saving.
Detector What it finds
email Email address
phone_e164 Phone number (international)
phone_nanp Phone number (North American)
ipv4 IPv4 address
ipv6 IPv6 address
card Payment card number (shape only)
card_visa Payment card — Visa
card_mastercard Payment card — Mastercard
card_amex Payment card — American Express
iban IBAN (bank account number)
us_ssn US Social Security Number
ch_ahv Swiss AHV/AVS number
eu_vat EU VAT identification number

Detectors are referenced with their version, as in card_visa@1. The builder always writes the pinned reference for you, so an improved detector never changes what a saved rule matches until you move it to the new version.

Every detector carries its own over-match and miss notes, shown in the builder before you save — read them, because they are the difference between what a detector is called and what it actually matches.

Mode What it does What it costs
Full redaction Replaces the detected value with a fixed token. Only the matched substring is replaced, so the rest of the line stays readable. Every occurrence becomes the same token, so two lines about two different people are indistinguishable. Nothing masked this way can be correlated or counted per subject.
Consistent hash Replaces the value with its SHA-256 digest. The same input always produces the same digest, so records about the same subject can still be joined without the value being present. See the warning below — at body and all-attributes scope the hash is unsalted.
Partial reveal Keeps a bounded, conventional part of the value — the last four digits of a card, the domain of an email — and masks the rest. A revealed tail is still data leaving your pipeline. Not every detector has a reveal form; where none is defined the mode is unavailable rather than approximated.

The default replacement token is **** for every detector, deliberately: a token that named the detector would tell a reader of the log which class of sensitive data used to be there. You can override it per rule.

Scope What it replaces
Body Matched substrings inside the log body.
All attributes Matched substrings in every log attribute value.
Named attribute The entire value of one attribute you name, and only when that whole value is the detected data. This is the only scope that can carry a salt.

The named-attribute scope matches the whole value rather than “contains”, because it is the only scope that can destroy surrounding text.

3. Mask something the library doesn’t cover

Section titled “3. Mask something the library doesn’t cover”

For values specific to your organisation — internal ticket IDs, employee numbers, customer references — choose Custom pattern in the detector picker and supply your own regular expression and a label.

A custom rule carries either a library detector or its own pattern, never both: two sources of truth for what a rule matches would mean nothing could report which one won.

Custom patterns use RE2 — the same engine the collector runs — so there are no backreferences and no lookaround. They offer full redaction and consistent hash only; partial reveal needs a per-detector notion of which part of a value is safe to show, and there is none for a pattern you just wrote. A pattern is also rejected if it can match empty (it would fire at every position and destroy the record) or if it is dramatically slower than the built-in detectors on a large line.

  1. Enter the pattern and a label — the label is what identifies the rule in the list and in metrics.

  2. Paste a sample line that contains the value you want masked.

  3. Click Test. You see exactly what the rule would do to that line.

  4. Save. For a new or changed pattern the server requires that the test matched. Read what that does and does not cover below.

Rules are validated on the server as you edit them, so the notes and the compiled result you read before saving come from the same code that will compile the rule for your fleet.

For an end-to-end check, open the dry-run panel and paste a sample event:

{
  "resourceLogs": [{
    "scopeLogs": [{
      "logRecords": [{
        "body": {
          "stringValue": "POST /api/v2/cart/checkout [email protected] card=4242 4242 4242 4242"
        }
      }]
    }]
  }]
}

Run it, and the Final Output panel shows the body rewritten with the matched values replaced.

Dry-run with masking applied: the body field is rewritten, with placeholders in place of the matched substrings.

Save the pipeline and attach it via a route on a collector handling real traffic. The collector picks up the new config on its next fetch.

The fastest confirmation: add a debug exporter alongside your real destination for a minute, watch journalctl -u alloy -f (or -u otelcol-contrib -f), and confirm that outbound records carry the masked values rather than the raw ones. Remove the debug exporter once you have seen one masked record — it is noisy at sustained traffic.