3053058cdf
Co-authored-by: Cursor <cursoragent@cursor.com>
116 lines
3.2 KiB
Python
116 lines
3.2 KiB
Python
"""Host and DB metrics for SAC sidebar (best-effort, no heavy queries)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import shutil
|
|
import time
|
|
from datetime import datetime, timedelta, timezone
|
|
from pathlib import Path
|
|
|
|
from sqlalchemy import func, select, text
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.models import Event, Problem
|
|
|
|
|
|
def _disk_path() -> str:
|
|
explicit = os.environ.get("SAC_STATS_DISK_PATH", "").strip()
|
|
if explicit:
|
|
return explicit
|
|
return str(Path("/").resolve())
|
|
|
|
|
|
def _read_cpu_percent() -> float | None:
|
|
try:
|
|
import psutil # type: ignore[import-untyped]
|
|
|
|
return round(float(psutil.cpu_percent(interval=None)), 1)
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
def _read_memory() -> dict[str, float | int] | None:
|
|
try:
|
|
import psutil # type: ignore[import-untyped]
|
|
|
|
vm = psutil.virtual_memory()
|
|
return {
|
|
"used_mb": int(vm.used / (1024 * 1024)),
|
|
"total_mb": int(vm.total / (1024 * 1024)),
|
|
"percent": round(float(vm.percent), 1),
|
|
}
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
def _read_disk(path: str) -> dict[str, float | int | str] | None:
|
|
try:
|
|
usage = shutil.disk_usage(path)
|
|
total = int(usage.total)
|
|
used = int(usage.used)
|
|
if total <= 0:
|
|
return None
|
|
return {
|
|
"path": path,
|
|
"used_gb": round(used / (1024**3), 1),
|
|
"total_gb": round(total / (1024**3), 1),
|
|
"percent": round(100.0 * used / total, 1),
|
|
}
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
def collect_system_stats(db: Session) -> dict:
|
|
collected_at = datetime.now(timezone.utc).isoformat()
|
|
disk_path = _disk_path()
|
|
|
|
db_status = "ok"
|
|
db_latency_ms: float | None = None
|
|
db_connections: int | None = None
|
|
t0 = time.perf_counter()
|
|
try:
|
|
db.execute(text("SELECT 1"))
|
|
db_latency_ms = round((time.perf_counter() - t0) * 1000, 1)
|
|
try:
|
|
db_connections = int(
|
|
db.scalar(
|
|
text(
|
|
"SELECT count(*) FROM pg_stat_activity "
|
|
"WHERE datname = current_database() AND pid <> pg_backend_pid()"
|
|
)
|
|
)
|
|
or 0
|
|
)
|
|
except Exception:
|
|
db_connections = None
|
|
except Exception:
|
|
db_status = "error"
|
|
|
|
since = datetime.now(timezone.utc) - timedelta(hours=1)
|
|
events_last_hour = 0
|
|
problems_open = 0
|
|
if db_status == "ok":
|
|
events_last_hour = int(
|
|
db.scalar(select(func.count()).select_from(Event).where(Event.received_at >= since)) or 0
|
|
)
|
|
problems_open = int(
|
|
db.scalar(select(func.count()).select_from(Problem).where(Problem.status == "open")) or 0
|
|
)
|
|
|
|
return {
|
|
"collected_at": collected_at,
|
|
"cpu_percent": _read_cpu_percent(),
|
|
"memory": _read_memory(),
|
|
"disk": _read_disk(disk_path),
|
|
"database": {
|
|
"status": db_status,
|
|
"latency_ms": db_latency_ms,
|
|
"active_connections": db_connections,
|
|
},
|
|
"app": {
|
|
"events_last_hour": events_last_hour,
|
|
"problems_open": problems_open,
|
|
},
|
|
}
|