fix(gateway): drain async log queue on os._exit shutdown backstop

The QueueListener change routes rotating file handlers through an
in-memory queue drained on a dedicated thread, with an atexit hook to
flush on shutdown. But _exit_after_graceful_shutdown() uses os._exit,
which bypasses atexit — so on the early-exit and #53107 hard-exit paths
the queued records (including the shutdown reason) were silently lost.

Explicitly flush_log_queue() before os._exit, and correct the now-stale
comment that claimed handlers are synchronous with nothing pending.
fix/verification-admin-route-recovery
kshitijk4poor 2026-07-05 11:27:55 +05:30 committed by kshitij
parent eb0cc27201
commit ac68a6411a
1 changed files with 16 additions and 4 deletions

View File

@ -20239,16 +20239,28 @@ def _exit_after_graceful_shutdown(exit_code: int) -> None:
released so this is a no-op on the normal shutdown path and the actual released so this is a no-op on the normal shutdown path and the actual
cleanup on the early-exit paths. cleanup on the early-exit paths.
Logging is not flushed here: the gateway's handlers are synchronous Logging IS flushed here: the rotating file handlers are driven by an
``RotatingFileHandler``s that write each record immediately (no async ``QueueListener`` on a dedicated thread (see
``MemoryHandler``/``QueueHandler`` buffering), so there is nothing pending. ``hermes_logging._register_queued_handler``), so records emitted right
Only stdio is buffered, so only stdio is flushed. before shutdown may still be sitting in the in-memory queue. ``os._exit``
below bypasses ``atexit``, so the ``atexit``-registered listener drain
never runs on this path we must drain explicitly here or lose the last
log lines (including the shutdown reason on the early-exit paths). Stdio
is flushed too.
""" """
for stream in (sys.stdout, sys.stderr): for stream in (sys.stdout, sys.stderr):
try: try:
stream.flush() stream.flush()
except Exception: except Exception:
pass pass
# Drain the async log queue: os._exit bypasses atexit, so the listener's
# atexit drain won't fire. flush_log_queue() no-ops when logging never
# initialized a queue (e.g. very early aborts), so this is always safe.
try:
from hermes_logging import flush_log_queue
flush_log_queue()
except Exception:
pass
# Guaranteed cleanup chokepoint: os._exit skips atexit, and the early # Guaranteed cleanup chokepoint: os._exit skips atexit, and the early
# SystemExit exit paths never run _stop_impl, so release here (idempotent). # SystemExit exit paths never run _stop_impl, so release here (idempotent).
try: try: