PII Redaction

Automatically redact personally identifiable information from traces before storage.

Neatlogs provides two layers of PII redaction: client-side masking that runs in your process before data leaves, and server-side redaction powered by a Presidio-based microservice that runs on the backend before data is stored.

Client-Side Masking

Use the mask parameter in neatlogs.init() to redact sensitive data before it's exported from your application:

def redact(span):
    attrs = span.get("attributes", {})
    for key in list(attrs):
        if "email" in key or "phone" in key:
            attrs[key] = "***"
    return span

neatlogs.init(mask=redact)

The mask function receives the full span dict and must return the modified dict. It runs once per span before export.

The mask replaces attribute values with whatever you wish to return, so it is not limited to redaction — returning a transformed string (for example, a translation of the input/output) is equally valid.

Per-Span Masking

Apply a mask to specific spans only using @span(mask=fn) or with trace(..., mask=fn). Per-span masks take precedence over the global mask:

@neatlogs.span(kind="TOOL", mask=redact_pii)
def lookup_customer(email: str):
    ...

A per-span mask applies only to that span, not to its peer/child spans. To mask every span in a trace with the same masking function, use the global neatlogs.init(mask=fn).

Server-Side Redaction

Server-side redaction runs automatically on the Neatlogs backend using a Presidio-based microservice. It detects and redacts PII entities (names, emails, phone numbers, addresses, etc.) from span payloads before they are stored.

Configuration

Server-side redaction is configured at the team level in the Neatlogs dashboard under Settings > PII Redaction. You can configure:

  • Enabled/Disabled: toggle redaction on or off for all projects in the team
  • Entity types: which PII types to detect (e.g., PERSON, EMAIL_ADDRESS, PHONE_NUMBER)
  • Operator: how to redact detected entities:
    • replace: replace with the entity type label (e.g., John<PERSON>)
    • mask: mask characters (e.g., John****)
    • redact: remove the entity entirely
  • Span types: which span kinds to apply redaction to (e.g., only LLM and TOOL spans)

SDK Overrides

Override the team-level settings per project using neatlogs.init():

# Enable redaction regardless of the dashboard setting
neatlogs.init(pii_enabled=True)

# Only redact PII in LLM and TOOL spans
neatlogs.init(pii_span_types=["LLM", "TOOL"])

# Disable redaction for a dev/test environment
neatlogs.init(pii_enabled=False)

When pii_enabled or pii_span_types is not set (default), the team-level dashboard configuration is used.

What Gets Redacted

Server-side redaction covers all user-visible span fields:

CategoryFields
Core I/Oinput_value, output_value
LLM contentcompletion_text, intermediate_steps
Prompt templatesprompt_template, prompt_template_variables, user_prompt_template, user_prompt_template_variables
Tool fieldstool_input, tool_output, tool_parameters, tool_call_function_arguments
Retrievalretrieval_query, retrieval_documents
Rerankerreranker_query, reranker_input_documents, reranker_output_documents
MCPmcp_request_argument, mcp_response_value

Graceful Degradation

If the PII redaction microservice is unavailable or times out, the original data is stored unchanged. Ingestion is never blocked. A warning is logged server-side when this occurs.

When to Use Which

ApproachUse when
Client-side maskYou need to redact data before it leaves your infrastructure
Server-side redactionYou want automatic, team-wide PII detection without modifying application code
BothDefense in depth: mask known fields client-side, let the server catch anything else

On this page

Ask Neatlogs AI

Answers from the docs

How can I help?

Ask anything about instrumenting, tracing, or the Neatlogs dashboard.