From 714a81206bb7099c9b6cf4e58bb52ff978a7fe78 Mon Sep 17 00:00:00 2001 From: viewit Date: Wed, 12 Aug 2026 14:18:01 +0200 Subject: [PATCH] fix(logging): ensure docker logs show bridge output logging.basicConfig() ran after bridge_endpoints (via bridge_logging) had already attached a handler to the root logger at import time, making basicConfig() a no-op per Python's docs (it only configures the root logger when it has none yet). Every log.info/warning call after that point silently stopped reaching stdout/stderr - "docker logs" only ever showed whatever fired before this point. Moved basicConfig() to run before any extracted-module import, and added PYTHONUNBUFFERED=1 so output isn't buffered away under a non-TTY container stdout. --- Dockerfile | 3 +++ kobrax_moonraker_bridge.py | 24 +++++++++++++++--------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index 35ce411..d841956 100644 --- a/Dockerfile +++ b/Dockerfile @@ -37,6 +37,9 @@ RUN mkdir -p /app/config && mkdir -p /app/data # und Container-Erkennung für den Bridge-Restart (Supervisor startet neu statt subprocess). ENV KX_DATA_DIR=/app/data ENV KX_IN_DOCKER=1 +# Ohne das puffert Python stdout/stderr komplett wenn kein TTY angehängt ist - +# "docker logs" bleibt dann leer bis der Prozess beendet wird. +ENV PYTHONUNBUFFERED=1 EXPOSE 7125 diff --git a/kobrax_moonraker_bridge.py b/kobrax_moonraker_bridge.py index f369b7a..ba851e2 100644 --- a/kobrax_moonraker_bridge.py +++ b/kobrax_moonraker_bridge.py @@ -40,6 +40,21 @@ import sys import time import threading +# Must run before any extracted-module import below: bridge_endpoints pulls in +# bridge_logging, which attaches its browser-log handler to the root logger at +# import time. If that happens first, basicConfig() becomes a no-op (it only +# configures the root logger when it has no handlers yet) and every log.info/ +# warning call silently stops reaching stdout/stderr - "docker logs" then only +# ever shows whatever fired before this point (e.g. an early config warning). +logging.basicConfig(level=logging.INFO, + format="[%(asctime)s] %(levelname)-5s %(name)s: %(message)s", + datefmt="%H:%M:%S") +log = logging.getLogger("bridge") +# aiohttp logs one INFO line per HTTP request (access log) — with 2s frontend +# polling that drowns out the bridge's own logs by default. Toggleable at +# runtime via the verbose_http_log setting (see handle_api_settings_post). +logging.getLogger("aiohttp.access").setLevel(logging.WARNING) + # For PyInstaller binaries everything sits next to sys.executable, otherwise next to __file__ _BASE = os.path.dirname(sys.executable) if getattr(sys, "frozen", False) else os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, _BASE) @@ -74,15 +89,6 @@ except ImportError: print("Error: aiohttp is not installed. Run: pip install aiohttp") sys.exit(1) -logging.basicConfig(level=logging.INFO, - format="[%(asctime)s] %(levelname)-5s %(name)s: %(message)s", - datefmt="%H:%M:%S") -log = logging.getLogger("bridge") -# aiohttp logs one INFO line per HTTP request (access log) — with 2s frontend -# polling that drowns out the bridge's own logs by default. Toggleable at -# runtime via the verbose_http_log setting (see handle_api_settings_post). -logging.getLogger("aiohttp.access").setLevel(logging.WARNING) - # UI theme-name validation (used in __init__); the /kx/ui asset-serving # constants live in bridge_endpoints alongside their only consumers.