""" System logs endpoint — exposes structured log events stored in system_logs table. """ from fastapi import APIRouter from typing import Optional router = APIRouter(prefix="/api/logs", tags=["logs"]) @router.get("") def get_logs( level: Optional[str] = None, source: Optional[str] = None, cycle_id: Optional[str] = None, ticker: Optional[str] = None, date_from: Optional[str] = None, date_to: Optional[str] = None, limit: int = 300, ): """ Query system logs with optional filters. - level: INFO | WARNING | ERROR | CRITICAL - source: logger name substring (e.g. 'auto_cycle', 'iv_engine') - cycle_id: exact run_id UUID - ticker: exact ticker symbol - date_from / date_to: ISO date strings YYYY-MM-DD - limit: max rows returned (default 300, max 1000) """ from services.database import get_system_logs limit = min(limit, 1000) logs = get_system_logs( level=level, source=source, cycle_id=cycle_id, ticker=ticker, date_from=date_from, date_to=date_to, limit=limit, ) return {"logs": logs, "count": len(logs)} @router.get("/sources") def get_log_sources(): """List distinct source names present in system_logs (for filter dropdown).""" from services.database import get_conn conn = get_conn() rows = conn.execute( "SELECT DISTINCT source FROM system_logs WHERE source IS NOT NULL ORDER BY source" ).fetchall() conn.close() return {"sources": [r["source"] for r in rows]} @router.get("/cycles") def get_log_cycles(limit: int = 50): """List distinct cycle_ids (most recent first) for filter dropdown.""" from services.database import get_conn conn = get_conn() rows = conn.execute( """SELECT DISTINCT cycle_id, MIN(ts) as first_ts FROM system_logs WHERE cycle_id IS NOT NULL GROUP BY cycle_id ORDER BY first_ts DESC LIMIT ?""", (limit,) ).fetchall() conn.close() return {"cycles": [{"cycle_id": r["cycle_id"], "first_ts": r["first_ts"]} for r in rows]} @router.delete("/clear") def clear_old_logs(older_than_days: int = 30): """Delete logs older than N days.""" from services.database import clear_system_logs deleted = clear_system_logs(older_than_days) return {"deleted": deleted, "older_than_days": older_than_days} @router.delete("/purge-all") def purge_all_logs(): """Truncate the entire system_logs table.""" from services.database import get_conn conn = get_conn() conn.execute("DELETE FROM system_logs") deleted = conn.total_changes conn.commit() conn.close() return {"deleted": deleted}