Перейти до вмісту

ADR-0011 — Mask PII in logs by default; opt-in DEBUG verbosity

Цей контент ще не доступний вашою мовою.

Accepted2026-04-28

The bot processes financial transactions whose payload — amounts, free-text categories, account names — is privacy-sensitive even at two-user scale. Logs are written to stderr in JSON and end up on a shared VPS, in CI run artifacts, and (later) in any log shipper.

We want enough information in logs to debug production issues without leaking spend patterns of either user. Local debugging occasionally needs the full payload.

OptionSummaryProsConsOutcome
A — Log everythingDefault to DEBUG-style logs in productionEasiest to debugEvery retained log archive becomes a soft data leak; reviewers/operators see partner’s spendingrejected
B — Log nothing PII-shapedStrip amounts/raw_text/category at all levelsBulletproof privacyProduction debugging becomes guesswork (we lose the ability to correlate “what message produced what error”)rejected
C (chosen) — Mask by default, opt-in verboseINFO/WARNING/ERROR redact known PII keys; DEBUG passes through. LOG_LEVEL=DEBUG is opt-in via env, not a defaultProduction logs safe by construction; local debugging keeps full power; one knob to flipDiscipline required: new code must use the standard PII keys (amount_minor, raw_text, category_name, account_name, display_name) so the redactor catches themselected

We will configure structlog with a redact_pii_processor that replaces a fixed set of PII-flagged keys with the literal string <redacted> whenever the log call is at INFO, WARNING, or ERROR. At DEBUG the processor is a no-op.

The PII key set is centralized in finance_bot/adapters/observability/logging.py:

  • amount_minor, amount
  • raw_text
  • category_name, account_name, display_name

LOG_LEVEL=DEBUG must never be set in production. Local development turns it on through .env.

  • Default-safe: a fresh deployment cannot leak PII through logs.
  • Single source of truth (PII_KEYS constant) — adding a new sensitive field means adding a name to one frozenset.
  • Test asserts the contract: test_redact_pii_replaces_known_keys_at_info, test_redact_pii_passthrough_at_debug.
  • New code that introduces a new sensitive field but forgets to add it to PII_KEYS will leak. Mitigation: code review checklist + a fitness test in Plan 7 that scans for log calls with raw amounts.
  • <redacted> makes some production traces hard to correlate. When needed, the operator flips LOG_LEVEL=DEBUG for one process, reproduces, then flips back.
  • Plan 7: add a structlog filter that drops the entire log line if a sensitive key is detected at WARNING/ERROR with a non-string value (defensive: catches “I forgot to redact” by failing closed).
  • Add LOG_LEVEL=DEBUG to runbook with a warning banner.
  • src/finance_bot/adapters/observability/logging.py — implementation.
  • tests/unit/test_logging_redaction.py — contract tests.