Skip to content

fix: silence werkzeug access logger so Flask 200s don't false-red (#181)#182

Merged
cofade merged 1 commit into
mainfrom
claude/open-issues-review-wnlmwh
Jun 20, 2026
Merged

fix: silence werkzeug access logger so Flask 200s don't false-red (#181)#182
cofade merged 1 commit into
mainfrom
claude/open-issues-review-wnlmwh

Conversation

@cofade

@cofade cofade commented Jun 20, 2026

Copy link
Copy Markdown
Collaborator

What & why

Closes #181 (follow-up to the #178 / PR #180 structured Server Logs panel).

In the Server Logs panel, every request to a Flask service (duckdb-service, image-service) produced two ring entries:

  1. The canonical structured access entry from the @app.after_request _access_log_finish hook — GET /health 200 0.1ms, level-tagged by status. Intended.
  2. Werkzeug's own request line (… "GET /health HTTP/1.1" 200 -), which the stdout/stderr tee in services/log_ring.py captures from stderr and therefore tags error (red) — even for a healthy 200.

So every successful request was double-logged and painted red. Because docker-compose.prod.yml builds both Flask services from Dockerfile.dev (CMD ["python", "app.py"] → the Werkzeug dev server), this noise reached prod, not just dev.

The fix

Silence Werkzeug's built-in access logger at import in both app.py, right after init_log_persistence() and before any request is served:

logging.getLogger("werkzeug").setLevel(logging.ERROR)

Werkzeug's _log only forces the logger to INFO when its level is still NOTSET, so pre-setting ERROR at import wins permanently. Access request lines (emitted at info) are filtered; Werkzeug's genuine error/exception logging (emitted at error) is untouched. The _access_log_finish hook is now the sole, level-tagged access entry.

Changes

  • Silence the logger symmetrically in duckdb-service/app.py and image-service/app.py (+ import logging).
  • Updated the now-stale _access_log_finish comments in both files.
  • Updated the log_ring.py docstring in both services — kept byte-identical (enforced by test_log_ring_byte_identical_across_services).
  • New test test_werkzeug_access_logger_silenced per service (asserts the logger level filters INFO while keeping ERROR; verified it fails when the fix is neutered to INFO).
  • ADR-023 addendum recording the rationale.

Verification

  • duckdb-service: 205 passed (incl. byte-identity guard + new test)
  • image-service: 80 passed
  • make check-citations: 7 OK, 0 problems
  • Passed the senior-reviewer gate — no P0/P1; mechanism independently verified against werkzeug source.

Manual (needs the stack up): with docker compose up, hit /health on both services and confirm the Server Logs panel shows exactly one green GET /health 200 N.Nms entry — no second werkzeug line, no red.

Out of scope (per issue)

  • gunicorn migration (future hardening — at which point this setLevel becomes a no-op and should be removed).
  • No docker-compose.prod.yml change.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UUXqNmzoqszKVAtaam1A3U


Generated by Claude Code

The Server Logs panel (#178) logged every Flask request twice: the
structured _access_log_finish entry, plus werkzeug's own request line,
which the stdout/stderr tee captures from stderr and tags `error` (red).
Healthy 200s rendered red and double-logged — and since
docker-compose.prod.yml still runs the Flask dev server, this reached
prod, not just dev.

Silence werkzeug's built-in access logger at import in both app.py
(setLevel ERROR, which keeps genuine werkzeug error/exception logging),
so the structured hook is the sole, level-tagged access entry. Update
the stale _access_log_finish comments and the byte-identical log_ring.py
docstrings, add a per-service test asserting the logger is silenced, and
record the rationale in ADR-023.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UUXqNmzoqszKVAtaam1A3U
@cofade cofade marked this pull request as ready for review June 20, 2026 20:43
@cofade cofade merged commit 5900185 into main Jun 20, 2026
9 checks passed
@cofade cofade deleted the claude/open-issues-review-wnlmwh branch June 20, 2026 20:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Silence Werkzeug's built-in access logger so Flask requests don't double-log (and false-red) in the Server Logs panel

2 participants