"""In-memory live log bus. A thread-safe ring buffer that any part of Lyra can publish to and the web server streams to the browser over SSE. Deliberately process-local and ephemeral — it's an activity feed, not durable logging. """ from __future__ import annotations import sys import threading import time from collections import deque _LOCK = threading.Lock() _EVENTS: deque[dict] = deque(maxlen=500) _SEQ = 0 def log(level: str, msg: str, **fields) -> None: """Publish an event. `level` is info/debug/error/system; fields are extras.""" global _SEQ with _LOCK: _SEQ += 1 _EVENTS.append( {"seq": _SEQ, "ts": time.time(), "level": level, "msg": msg, "fields": fields} ) # Mirror to stderr so out-of-band runs (e.g. the dream service under # systemd/journald) are observable, not just via the in-process SSE feed. extra = " ".join(f"{k}={v}" for k, v in fields.items()) print(f"[{level}] {msg}{(' ' + extra) if extra else ''}", file=sys.stderr, flush=True) def since(seq: int) -> list[dict]: """All buffered events with seq greater than `seq` (for SSE catch-up/polling).""" with _LOCK: return [e for e in _EVENTS if e["seq"] > seq]