Files
KX-Bridge-Release/bridge_logging.py
viewit cdf11f6bfe refactor(bridge): extract self-contained classes/helpers into modules (stage 1)
First stage of splitting the 6368-line kobrax_moonraker_bridge.py monolith.
The low-coupling, self-contained pieces move into their own modules;
kobrax_moonraker_bridge.py re-exports them so every caller (12 test files,
the PyInstaller spec) keeps working unchanged - not a single test or the
spec needed editing.

Extracted:
- spoolman_client.py  <- SpoolmanClient (zero coupling)
- gcode_store.py      <- GCodeStore (stdlib only)
- gcode_meta.py       <- _parse_gcode_*/_extract_* metadata helpers
- camera.py           <- CameraCache + _find_ffmpeg
- bridge_logging.py   <- _BrowserLogHandler, the log ring buffer + SSE
                         queues, _set_verbose_http_log (the shared mutable
                         buffer/queues are re-imported so the log endpoints
                         still operate on the same objects the handler writes)

Facade shrinks from 6368 to 5566 lines. All 184 tests green after each
extraction. Verified the re-exported names resolve and the shared log
objects are identical by reference across modules. No behavior change.
2026-08-04 19:41:17 +02:00

61 lines
2.3 KiB
Python

"""
bridge_logging.py - browser log stream plumbing for the bridge.
Holds the ring buffer + SSE queues that feed the Web UI's live log view, the
logging.Handler that populates them, and the verbose-HTTP-log toggle.
Extracted from kobrax_moonraker_bridge.py; the buffer/queues are re-imported
there so the log-stream/download endpoints keep working against the same
shared objects.
────────────────────────────────────────────────────────────────────────────
Copyright (C) 2026 viewit (KX-Bridge contributors)
Licensed under GPLv3 — see LICENSE in the project root. See NOTICE.md.
"""
import collections as _collections
import logging
def _set_verbose_http_log(enabled: bool):
logging.getLogger("aiohttp.access").setLevel(logging.INFO if enabled else logging.WARNING)
# Ring buffer for the browser log stream + the open SSE consumer queues.
# These are shared mutable objects: kobrax_moonraker_bridge re-imports the
# same buffer/list so its log-stream/download handlers append/read the very
# same instances this handler writes to.
_log_buffer: "_collections.deque[dict]" = _collections.deque(maxlen=500)
_log_sse_queues: list = []
class _BrowserLogHandler(logging.Handler):
"""Sends log records to the ring buffer and all open SSE queues."""
_fmt = logging.Formatter(datefmt="%H:%M:%S")
def emit(self, record: logging.LogRecord):
msg = record.getMessage()
# Pass exceptions with traceback through to the browser (otherwise the
# user only sees "Error: X" without context).
if record.exc_info:
try:
msg += "\n" + self._fmt.formatException(record.exc_info)
except Exception:
pass
entry = {
"ts": self._fmt.formatTime(record, "%H:%M:%S"),
"lvl": record.levelname,
"name": record.name,
"msg": msg,
}
_log_buffer.append(entry)
for q in list(_log_sse_queues):
try:
q.put_nowait(entry)
except Exception:
pass
_browser_handler = _BrowserLogHandler()
logging.getLogger().addHandler(_browser_handler)