ADR-0011 — Mask PII in logs by default; opt-in DEBUG verbosity
Цей контент ще не доступний вашою мовою.
Status
Section titled “Status”Accepted — 2026-04-28
Context
Section titled “Context”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.
Considered alternatives
Section titled “Considered alternatives”| Option | Summary | Pros | Cons | Outcome |
|---|---|---|---|---|
| A — Log everything | Default to DEBUG-style logs in production | Easiest to debug | Every retained log archive becomes a soft data leak; reviewers/operators see partner’s spending | rejected |
| B — Log nothing PII-shaped | Strip amounts/raw_text/category at all levels | Bulletproof privacy | Production debugging becomes guesswork (we lose the ability to correlate “what message produced what error”) | rejected |
| C (chosen) — Mask by default, opt-in verbose | INFO/WARNING/ERROR redact known PII keys; DEBUG passes through. LOG_LEVEL=DEBUG is opt-in via env, not a default | Production logs safe by construction; local debugging keeps full power; one knob to flip | Discipline required: new code must use the standard PII keys (amount_minor, raw_text, category_name, account_name, display_name) so the redactor catches them | selected |
Decision
Section titled “Decision”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,amountraw_textcategory_name,account_name,display_name
LOG_LEVEL=DEBUG must never be set in production. Local development
turns it on through .env.
Consequences
Section titled “Consequences”Positive
Section titled “Positive”- 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.
Negative / trade-offs
Section titled “Negative / trade-offs”- New code that introduces a new sensitive field but forgets to add
it to
PII_KEYSwill 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 flipsLOG_LEVEL=DEBUGfor one process, reproduces, then flips back.
Neutral / follow-ups
Section titled “Neutral / follow-ups”- 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=DEBUGto runbook with a warning banner.
References
Section titled “References”src/finance_bot/adapters/observability/logging.py— implementation.tests/unit/test_logging_redaction.py— contract tests.