c1b099e5a3
- logbuf.py: bounded in-memory log ring buffer + a logging.Handler on the root logger; GET /logs serves it; the control page polls it into a console pane — so runs are monitorable without `docker logs`. worker now logs autoscale moves (one line per change, with jobs/s + util + VRAM) and job failures (job + image + reason); detectors already log load/disable. - Restyled the whole control page: a proper dark layout with a header + live connection pill, cards (Control / Status / Logs), a styled Auto switch + worker stepper, status tiles, separate GPU-util and VRAM meters, and the log console. No longer feels like an afterthought; all the existing control hooks are preserved. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ttrj5P7upUTueSfoJcxEqa
41 lines
1.3 KiB
Python
41 lines
1.3 KiB
Python
"""In-memory log ring buffer so the control UI can show recent agent logs
|
|
(detector loads, job errors, autoscaler decisions, outage back-offs) without
|
|
needing `docker logs`. A bounded deque holds the last N formatted lines; a
|
|
logging.Handler appends to it; the UI polls /logs."""
|
|
import logging
|
|
from collections import deque
|
|
|
|
LINES: deque[str] = deque(maxlen=600)
|
|
|
|
|
|
class RingHandler(logging.Handler):
|
|
def emit(self, record: logging.LogRecord) -> None:
|
|
try:
|
|
LINES.append(self.format(record))
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
_installed = False
|
|
|
|
|
|
def install(level: int = logging.INFO) -> None:
|
|
"""Attach the ring handler to the root logger once. fc_agent module loggers
|
|
propagate to root, so their records land here."""
|
|
global _installed
|
|
if _installed:
|
|
return
|
|
_installed = True
|
|
h = RingHandler()
|
|
h.setFormatter(
|
|
logging.Formatter("%(asctime)s %(levelname)s %(name)s: %(message)s", "%H:%M:%S")
|
|
)
|
|
root = logging.getLogger()
|
|
root.addHandler(h)
|
|
if root.level == logging.NOTSET or root.level > level:
|
|
root.setLevel(level)
|
|
# Keep the buffer signal-rich: drop the per-request access spam + ultralytics
|
|
# banner noise to WARNING.
|
|
logging.getLogger("uvicorn.access").setLevel(logging.WARNING)
|
|
logging.getLogger("ultralytics").setLevel(logging.WARNING)
|