Skip to content

Fix FD leak in LogSingleDriver.change_format - #257

Closed
tmgbedu wants to merge 1 commit into
mainfrom
task/log-fd-leak
Closed

tmgbedu wants to merge 1 commit into
mainfrom
task/log-fd-leak

Conversation

@tmgbedu

@tmgbedu tmgbedu commented Sep 27, 2026

Copy link
Copy Markdown
Contributor

Fixes #2098.

Root cause

LogSingleDriver.change_format() ran on every log call. It removed every handler from logging.getLogger("root") (which is the root logger) without closing them, then opened a new logging.FileHandler. Each log call leaked one open file descriptor, and long-running servers eventually hit the OS open-file limit.

Fix

  • The driver logs through a dedicated, non-propagating logger per file path (fastapi_startkit.logging.single.<abspath>) that owns exactly one FileHandler.
  • Drivers for the same path reuse that handler, so building channels repeatedly doesn't add handlers.
  • change_format() only swaps the formatter on the existing handler. Format changes still apply.
  • New close() removes and closes the handler.
  • The daily and stack channels go through LogSingleDriver, so the fix covers them too. Syslog doesn't create handlers per call and is left unchanged.

Behaviour changes

  1. The driver no longer strips root logger handlers. The LoggingHandler bridge and any user or pytest handlers stay attached.
  2. The driver no longer calls setLevel() on the root logger. Its own logger is fixed at DEBUG, and the channel max_level still filters.
  3. File log records no longer propagate to the root logger.

Tests

  • Regression test: 400 log calls leave the handler count at 1 and the open FD count (/dev/fd) unchanged.
  • The root logger's handlers (including LoggingHandler) and its level are unchanged after logging.
  • Two drivers for the same file share one handler. change_format keeps the same handler and applies the new format. close() releases one FD.
  • All 8 levels write the correct label.
  • The new tests fail against the old driver. Full suite passes (--ignore=tests/masoniteorm/postgres: 2486 passed). LogSingleDriver.py has 100% coverage. ruff is clean, basedpyright reports 0 errors.

🤖 Generated with Claude Code

change_format ran on every log call, stripped every handler from the root
logger without closing it, and opened a fresh FileHandler, leaking one file
descriptor per call. The driver now logs through a dedicated non-propagating
logger per file that owns a single reused FileHandler; change_format only
swaps the formatter on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@tmgbedu tmgbedu closed this Sep 27, 2026
@tmgbedu
tmgbedu deleted the task/log-fd-leak branch September 27, 2026 19:14
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.

1 participant