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:
matitos committed 2026-09-22 17:10:38 +00:00
1 parent c605a0f316
commit ac92dd5ac4
12 files changed
+462 -1

No files matched your search

+26 -1
View File
@@ -4,10 +4,20 @@ import asyncio
from typing import Annotated
import httpx
from fastapi import APIRouter, Depends, HTTPException, Request, WebSocket, WebSocketDisconnect
from fastapi import (
APIRouter,
Depends,
HTTPException,
Request,
Response,
WebSocket,
WebSocketDisconnect,
)
from app.config import Settings
from app.engine.strategies import get_strategy, list_strategies, strategy_spec
from app.logs import get_log_handler
from app.metrics import render_metrics
from app.models import OrderSide, Position, PositionStatus
from app.runtime import Runtime
from app.schemas import (
@@ -65,6 +75,21 @@ def health() -> dict[str, str]:
return {"status": "ok"}
@router.get("/metrics")
def metrics(runtime: GuardedRuntimeDep) -> Response:
"""Prometheus text exposition, unauthenticated so a scraper can reach it."""
status = runtime.multi_status()
body = render_metrics(status, runtime.settings.mode, runtime.settings.exchange)
return Response(content=body, media_type="text/plain; version=0.0.4")
@router.get("/logs")
def logs(runtime: GuardedRuntimeDep, limit: int = 100) -> dict:
handler = get_log_handler()
entries = handler.snapshot(limit) if handler is not None else []
return {"entries": entries}
@router.get("/version")
def version(request: Request) -> dict[str, str]:
"""Build version of the running backend, used by the client to self-heal