> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sedata-ai.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Data processors

> Mutate or redact every attribute set before it leaves your process.

A **data processor** is a pure function `(data) => data` that runs on every
attribute object the package emits. They're the right place to redact, hash,
allow-list, or enrich attributes site-wide.

## Signature

```ts theme={null}
type DataProcessor = (data: Record<string, any>) => Record<string, any>
```

You configure them on `TelemetryConfig.dataProcessors`:

```ts theme={null}
const config: TelemetryConfig = {
  // ...
  dataProcessors: [
    addEnv,
    redactPII,
    hashUserId,
  ],
}
```

Processors run **in order**. Each receives the output of the previous one.

## What gets processed

Every call to:

* `startActiveSpan(name, attributes, fn)`
* `getHistogram(...)(value, attributes)`
* `getIncrementCounter(...)(value, attributes)`

…runs its `attributes` through every processor before recording. The session
id (`mcp.session.id`) is merged in *before* processors run, so processors see
it and can override or remove it.

## Common recipes

### Add deployment metadata

```ts theme={null}
const addEnv = (data: Record<string, any>) => ({
  ...data,
  'deployment.env': process.env.DEPLOY_ENV ?? 'dev',
  'deployment.region': process.env.AWS_REGION ?? 'local',
})
```

### Redact specific keys

```ts theme={null}
const SENSITIVE_KEYS = new Set(['mcp.request.argument.api_key', 'mcp.request.argument.password'])

const redactKeys = (data: Record<string, any>) => {
  const out: Record<string, any> = {}
  for (const [k, v] of Object.entries(data)) {
    out[k] = SENSITIVE_KEYS.has(k) ? '[REDACTED]' : v
  }
  return out
}
```

### Hash user identifiers

```ts theme={null}
import { createHash } from 'node:crypto'

const hashUserIds = (data: Record<string, any>) => {
  const out = { ...data }
  for (const k of Object.keys(out)) {
    if (k.endsWith('user.id') && typeof out[k] === 'string') {
      out[k] = createHash('sha256').update(out[k]).digest('hex').slice(0, 16)
    }
  }
  return out
}
```

### Drop attributes by pattern

```ts theme={null}
const dropDebug = (data: Record<string, any>) => {
  const out: Record<string, any> = {}
  for (const [k, v] of Object.entries(data)) {
    if (!k.startsWith('debug.')) out[k] = v
  }
  return out
}
```

## Calling processors directly

You can run the configured chain manually for one-off uses:

```ts theme={null}
const cleaned = telemetry.processTelemetryAttributes({
  'user.email': 'jane@acme.com',
})
// → { 'mcp.session.id': '...', 'user.email': '[REDACTED:email]' }
```

This is useful when you're building your own log lines and want them to share
the same redaction logic as your spans.

## Order matters

A common bug: redact-then-add ordering. If you add deployment env *after* you
redact, the env keys are not redacted (which is usually fine but worth being
deliberate about).

```ts theme={null}
dataProcessors: [
  redactPII,   // 1. scrub anything sensitive that came from user input
  addEnv,      // 2. add deployment metadata
  enrichTrace, // 3. enrich with computed signals
]
```

## Performance

Processors run on the hot path of every span and metric record. Keep them:

* **Pure** — no I/O, no awaits.
* **Fast** — use precompiled regexes, avoid full string clones when nothing
  needs replacing.
* **Allocation-light** — return the same object when nothing changed.

```ts theme={null}
const redactIfNeeded = (data: Record<string, any>) => {
  let cloned: Record<string, any> | null = null
  for (const k of Object.keys(data)) {
    const v = data[k]
    if (typeof v === 'string' && PII_RE.test(v)) {
      cloned ??= { ...data }
      cloned[k] = v.replace(PII_RE, '[REDACTED]')
    }
  }
  return cloned ?? data // unchanged → return same reference
}
```

## Next

<CardGroup cols={2}>
  <Card title="PII sanitization" icon="user-shield" href="/guides/pii-sanitization">
    A more focused recipe for the common case.
  </Card>

  <Card title="Custom spans & metrics" icon="code" href="/guides/custom-spans-and-metrics">
    Where processors fit in your custom code.
  </Card>
</CardGroup>
