Skip to content

build(flake8): Add S019 rule banning reserved LogRecord keys in logging extra=#117562

Merged
NicoHinderling merged 2 commits into
masterfrom
tools/flake8-s019-reserved-logrecord-key
Jun 12, 2026
Merged

build(flake8): Add S019 rule banning reserved LogRecord keys in logging extra=#117562
NicoHinderling merged 2 commits into
masterfrom
tools/flake8-s019-reserved-logrecord-key

Conversation

@NicoHinderling

Copy link
Copy Markdown
Contributor

Summary

Passing a reserved LogRecord attribute (name, message, args, module, …) as a key in a logging extra= dict raises KeyError: "Attempt to overwrite 'X' in LogRecord" at runtime, when the log line actually fires. Nothing static catches it — it passes ruff, mypy, and a human skim — and it recently caused a silent production incident in preprod snapshots (the log call meant to aid debugging was the thing that crashed; see #117550).

This adds a custom S019 flake8 rule (Sentry's S### family in tools/flake8_plugin.py) that flags this class before runtime, and fixes the only two existing violations it surfaces repo-wide.

This follows up a review question on #117550 ("can this class of problem be prevented before runtime?").

Changes

  • S019 rule (tools/flake8_plugin.py): flags logger.<level>(..., extra={...}) calls whose extra dict has a literal string key colliding with a reserved LogRecord attribute. Auto-registered via the existing S= prefix plugin (no config change).
  • test_S019 (tests/tools/test_flake8_plugin.py): covers name/message hits, a non-reserved key (ok), and a dynamic extra=var (not flagged).
  • Fixes for the two surfaced violations:

Known limitation

Only literal extra={...} dicts with constant string keys are checked. Dynamic dicts (extra=some_var, extra={**spread}, computed keys) can't be detected statically — but the literal case is the one that actually bites.

Test Plan

  • test_S019 passes; full tests/tools/test_flake8_plugin.py suite green (18 tests)
  • flake8 --select=S019 src tests tools reports clean after the two fixes (was 2 hits before)
  • ruff + mypy clean on changed files

Passing a reserved LogRecord attribute (name, message, args, module,
etc.) as a key in a logging extra= dict raises
KeyError: "Attempt to overwrite 'X' in LogRecord" at runtime. Nothing
static caught this, and it recently caused a silent production incident.

Add a custom S019 flake8 rule that flags literal extra= dict keys
colliding with reserved LogRecord attributes on logging calls. Only
literal dicts with constant string keys are checked; dynamic dicts
cannot be detected statically.
Two existing logging calls passed reserved LogRecord keys in extra=,
which raise KeyError when the log line fires:
- apigateway/proxy.py: "message" -> "error" (breaker-config warning)
- preprod/snapshots/tasks.py: "name" -> "image_name" (also fixed in #117550)

These are the only repo-wide violations the new S019 rule surfaces.
@NicoHinderling NicoHinderling requested review from a team as code owners June 12, 2026 19:02
@github-actions github-actions Bot added the Scope: Backend Automatically applied to PRs that change backend components label Jun 12, 2026
@NicoHinderling NicoHinderling merged commit a891ef8 into master Jun 12, 2026
67 checks passed
@NicoHinderling NicoHinderling deleted the tools/flake8-s019-reserved-logrecord-key branch June 12, 2026 19:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants