log: gate stderr handler behind PALLAS_LOG_STDERR so fast-agent TUI is usable

The stderr StreamHandler, even using sys.__stderr__ captured before
Rich installed its Live display, still corrupts the fast-agent TUI in
interactive 'fast-agent go' sessions — Rich redraws on top of our JSON
log lines but leaks through every repaint, making the interface
effectively unusable.

Keep the RotatingFileHandler as the always-on durable capture (that's
what survives fast-agent's progress_display takeover and what we rely
on for diagnostics).  Gate the stderr sink behind PALLAS_LOG_STDERR=1
for operators who explicitly want journal/terminal capture on a
systemd-managed deployment.
This commit is contained in:
2026-05-06 20:06:17 -04:00
parent 082b6111ae
commit dde7d4fa30

View File

@@ -171,10 +171,15 @@ def setup_logging() -> None:
stdout and swallows plain ``print`` / stderr writes mid-render, so a
file sink is the only guaranteed way to see DEBUG diagnostics and
tracebacks.
* ``StreamHandler`` → ``sys.__stderr__``. Uses the *original* stderr
file descriptor captured before Rich installed its Live display, so
records bypass the progress renderer and land in the terminal or
journal verbatim. Fine for production where Rich is off.
* ``StreamHandler`` → ``sys.__stderr__`` (opt-in). Uses the *original*
stderr FD captured before Rich installed its Live display, so records
would bypass the progress renderer and land in the terminal or journal
verbatim. In interactive ``fast-agent go`` sessions this still
smashes the Rich UI (Rich redraws on top but our JSON lines leak
through every repaint), so we only attach it when ``PALLAS_LOG_STDERR``
is set to a truthy value — production systemd deployments that want
journal capture should set ``PALLAS_LOG_STDERR=1``; interactive users
get a clean TUI and rely on the rotating file sink.
- ``httpx`` / ``httpcore``: WARNING (prevent request-level debug flooding)
- ``uvicorn.access``: health path filter applied
@@ -209,12 +214,17 @@ def setup_logging() -> None:
"could not attach file log handler: %s", exc
)
# Stderr sink — writes to the *original* FD Rich captured at import
# time, so our records skip the progress display and hit the TTY
# (or systemd journal) directly.
stream_handler = logging.StreamHandler(stream=sys.__stderr__)
stream_handler.setFormatter(formatter)
handlers.append(stream_handler)
# Stderr sink — *opt-in* only. Interactive ``fast-agent go`` sessions
# install Rich's Live display which owns the terminal; emitting JSON
# log records to stderr during that session corrupts the TUI on every
# redraw and made Pallas effectively unusable in dev. Operators who
# want systemd/journal capture can re-enable by exporting
# ``PALLAS_LOG_STDERR=1`` before launching. The rotating file sink
# above is the always-on durable capture.
if os.environ.get("PALLAS_LOG_STDERR", "").lower() in ("1", "true", "yes"):
stream_handler = logging.StreamHandler(stream=sys.__stderr__)
stream_handler.setFormatter(formatter)
handlers.append(stream_handler)
# Root logger carries everything from libraries we do NOT own.
# We set the level low enough to accept our configured level; each