From 3d2d894a33cc788b6d7f8582ebc8d04f1967f7c2 Mon Sep 17 00:00:00 2001 From: SwiftWing21 Date: Tue, 28 Apr 2026 15:28:33 -0700 Subject: [PATCH] fix(proxy): defer file logging install to create_app() Move _setup_file_logging() from module import to create_app() so importing headroom.proxy.server in tests or library contexts no longer silently attaches a RotatingFileHandler to the user's live proxy.log. This was discovered via PR #303: running the test suite produced "Optimization failed: TimeoutError" entries in ~/.headroom/logs/proxy.log because TestClient instantiations from test_proxy_anthropic_compression_diagnostics.py inherited the module-level handler. Add a regression test that imports the server module in a subprocess and asserts no RotatingFileHandler is attached to the headroom logger. Refs #303 (review thread). Co-Authored-By: Claude Sonnet 4.6 --- headroom/proxy/server.py | 9 ++++++--- tests/test_pr208_changes.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/headroom/proxy/server.py b/headroom/proxy/server.py index 584e98225..7db6f1300 100644 --- a/headroom/proxy/server.py +++ b/headroom/proxy/server.py @@ -188,9 +188,6 @@ logger = logging.getLogger("headroom.proxy") _MULTI_WORKER_CONFIG_ENV = "HEADROOM_PROXY_CONFIG_JSON" -# Always-on file logging to ~/.headroom/logs/ for `headroom perf` analysis -_setup_file_logging() - # Compression pipeline timeout in seconds @@ -1064,6 +1061,12 @@ def create_app(config: ProxyConfig | None = None) -> FastAPI: from contextlib import asynccontextmanager + # Always-on file logging to ~/.headroom/logs/ for `headroom perf` analysis. + # Installed here (not at module import) so importing headroom.proxy.server + # in tests or library contexts does not silently attach a RotatingFileHandler + # to the user's live proxy.log. + _setup_file_logging() + config = config or ProxyConfig() proxy = HeadroomProxy(config) diff --git a/tests/test_pr208_changes.py b/tests/test_pr208_changes.py index 4e020936f..ab99310cb 100644 --- a/tests/test_pr208_changes.py +++ b/tests/test_pr208_changes.py @@ -371,6 +371,39 @@ class TestSetupFileLogging: # Should not raise _setup_file_logging() + def test_importing_server_does_not_install_file_handler(self, tmp_path: Path) -> None: + """Importing headroom.proxy.server must NOT attach a RotatingFileHandler + to the user's live proxy.log. The handler is installed by create_app() + instead, so test runs and library imports do not pollute logs. + """ + import os + import subprocess + import sys + import textwrap + + script = textwrap.dedent( + """ + import logging + from logging.handlers import RotatingFileHandler + import headroom.proxy.server # noqa: F401 + hr = logging.getLogger("headroom") + installed = any(isinstance(h, RotatingFileHandler) for h in hr.handlers) + print("INSTALLED" if installed else "CLEAN") + """ + ).strip() + env = {**os.environ, "HEADROOM_WORKSPACE_DIR": str(tmp_path)} + result = subprocess.run( + [sys.executable, "-c", script], + capture_output=True, + text=True, + env=env, + check=True, + creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0), + ) + assert result.stdout.strip() == "CLEAN", ( + f"Importing headroom.proxy.server attached a file handler: {result.stdout!r}" + ) + # --------------------------------------------------------------------------- # Repro script URL helpers and stats tests