from dotenv import load_dotenv load_dotenv('/opt/rinet-gpu/.env.master') # auto-added by patch_scrapers_with_dotenv.sh """Debug observability dashboard endpoint.""" import json, os, time from pathlib import Path from fastapi import APIRouter, Query from fastapi.responses import JSONResponse, HTMLResponse, PlainTextResponse from typing import Optional router = APIRouter(prefix="/api/debug", tags=["debug"]) LOGDIR = Path("/var/log/pgz-sport-debug") @router.get("/health") def debug_health(): """Quick service status.""" import subprocess services = ['pgz-sport', 'pgz-debug-tail', 'pgz-auto-triage', 'nginx', 'redis-server'] status = {} for s in services: try: r = subprocess.run(['systemctl', 'is-active', s], capture_output=True, text=True, timeout=2) status[s] = r.stdout.strip() except Exception as e: status[s] = f"error:{e}" # DB db_status = "unknown" try: import psycopg2 with psycopg2.connect(f"host=10.10.0.2 port=6432 dbname=rinet_v3 user=rinet password={os.environ['DB_PASSWORD']}", connect_timeout=2) as conn: with conn.cursor() as cur: cur.execute("SELECT 1") db_status = "ok" except Exception as e: db_status = f"error:{e}" # Recent errors count err_count = 0 if (LOGDIR / "errors.jsonl").exists(): with open(LOGDIR / "errors.jsonl") as f: err_count = sum(1 for _ in f) return { "ts": time.time(), "services": status, "db": db_status, "total_errors_logged": err_count, "log_dir": str(LOGDIR), } @router.get("/errors") def recent_errors(limit: int = Query(100, ge=1, le=1000)): """Last N errors from errors.jsonl.""" f = LOGDIR / "errors.jsonl" if not f.exists(): return {"errors": [], "note": "errors.jsonl not yet created"} lines = f.read_text(errors='ignore').strip().split('\n')[-limit:] parsed = [] for line in lines: try: parsed.append(json.loads(line)) except: continue return {"errors": parsed, "count": len(parsed)} @router.get("/decisions") def triage_decisions(limit: int = Query(50, ge=1, le=500)): """Last N auto-triage decisions.""" f = LOGDIR / "triage_decisions.jsonl" if not f.exists(): return {"decisions": [], "note": "no decisions yet"} lines = f.read_text(errors='ignore').strip().split('\n')[-limit:] parsed = [] for line in lines: try: parsed.append(json.loads(line)) except: continue return {"decisions": parsed, "count": len(parsed)} @router.get("/stream") def stream_tail(lines: int = Query(200, ge=10, le=2000)): """Last N lines of full stream.jsonl.""" f = LOGDIR / "stream.jsonl" if not f.exists(): return {"stream": []} raw = f.read_text(errors='ignore').strip().split('\n')[-lines:] parsed = [] for line in raw: try: parsed.append(json.loads(line)) except: continue return {"stream": parsed} @router.get("/dashboard", response_class=HTMLResponse) def dashboard(): """Live HTML dashboard.""" return """
loading…