"""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)