Observability: Prometheus metrics, log panel, runbook
- GET /api/metrics renders Prometheus text (equity, P&L, per-strategy equity/pnl/allocation/halted, info) with no client library. - In-memory ring log handler (app/logs.py) exposed at GET /api/logs and a dashboard "Server logs" panel; Runtime mirrors bot events into the logger. - docs/runbook.md covers deploys, the kill switch, resetting paper state and going live. Adds tests for metrics rendering and the log ring.
This commit is contained in:
1 parent
c605a0f316
commit
ac92dd5ac4
12 files changed
+462
-1
No files matched your search
@@ -32,6 +32,15 @@ from app.store import Store
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_LOG_LEVELS = {
|
||||
"debug": logging.DEBUG,
|
||||
"info": logging.INFO,
|
||||
"trade": logging.INFO,
|
||||
"warning": logging.WARNING,
|
||||
"error": logging.ERROR,
|
||||
"critical": logging.CRITICAL,
|
||||
}
|
||||
|
||||
FeedFactory = Callable[[Settings], MarketData]
|
||||
BrokerFactory = Callable[[Settings], Broker]
|
||||
NotifierFactory = Callable[[Settings], Notifier | None]
|
||||
@@ -91,8 +100,15 @@ class Runtime:
|
||||
with suppress(asyncio.QueueFull):
|
||||
queue.put_nowait(payload)
|
||||
if payload.get("type") == "event":
|
||||
self._log_event(payload["data"])
|
||||
self._maybe_notify(payload["data"])
|
||||
|
||||
def _log_event(self, event: dict[str, Any]) -> None:
|
||||
"""Mirror bot events into the logger so the dashboard log view is useful."""
|
||||
level = str(event.get("level", "info")).lower()
|
||||
message = event.get("message", "")
|
||||
logger.log(_LOG_LEVELS.get(level, logging.INFO), "%s", message)
|
||||
|
||||
def _maybe_notify(self, event: dict[str, Any]) -> None:
|
||||
if event.get("level") not in self.notify_levels:
|
||||
return
|
||||
|
||||
Reference in new issue
Block a user