"""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=400) 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: silence the chatty HTTP/download libs (every # HF model fetch logs per-request) so the console shows agent activity — # detector loads, job errors, autoscale moves — not request spam. for noisy in ( "uvicorn.access", "ultralytics", "httpx", "httpcore", "huggingface_hub", "urllib3", "filelock", ): logging.getLogger(noisy).setLevel(logging.WARNING)