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 stdout and swallows plain ``print`` / stderr writes mid-render, so a
file sink is the only guaranteed way to see DEBUG diagnostics and file sink is the only guaranteed way to see DEBUG diagnostics and
tracebacks. tracebacks.
* ``StreamHandler`` → ``sys.__stderr__``. Uses the *original* stderr * ``StreamHandler`` → ``sys.__stderr__`` (opt-in). Uses the *original*
file descriptor captured before Rich installed its Live display, so stderr FD captured before Rich installed its Live display, so records
records bypass the progress renderer and land in the terminal or would bypass the progress renderer and land in the terminal or journal
journal verbatim. Fine for production where Rich is off. 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) - ``httpx`` / ``httpcore``: WARNING (prevent request-level debug flooding)
- ``uvicorn.access``: health path filter applied - ``uvicorn.access``: health path filter applied
@@ -209,9 +214,14 @@ def setup_logging() -> None:
"could not attach file log handler: %s", exc "could not attach file log handler: %s", exc
) )
# Stderr sink — writes to the *original* FD Rich captured at import # Stderr sink — *opt-in* only. Interactive ``fast-agent go`` sessions
# time, so our records skip the progress display and hit the TTY # install Rich's Live display which owns the terminal; emitting JSON
# (or systemd journal) directly. # 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 = logging.StreamHandler(stream=sys.__stderr__)
stream_handler.setFormatter(formatter) stream_handler.setFormatter(formatter)
handlers.append(stream_handler) handlers.append(stream_handler)