Add frontend performance metrics pipeline to Prometheus

Collect browser-side worker round-trips, computation times, main-thread
operations, and feature counts, batch them client-side, and expose as
Prometheus histograms via a new POST /api/perf endpoint.
This commit is contained in:
Viktor Barzin 2026-02-22 17:30:29 +00:00
parent c24c3a545c
commit d90fa38776
No known key found for this signature in database
GPG key ID: 0EB088298288D958
10 changed files with 188 additions and 5 deletions

43
api/perf_routes.py Normal file
View file

@ -0,0 +1,43 @@
"""Frontend performance metrics ingestion endpoint."""
from __future__ import annotations
from fastapi import APIRouter
from pydantic import BaseModel, Field, field_validator
import api.metrics as app_metrics
ALLOWED_METRICS = {"worker_roundtrip", "worker_compute", "main_thread", "feature_count"}
MAX_BATCH_SIZE = 100
class PerfSample(BaseModel):
metric: str
operation: str = Field(max_length=50)
value: float = Field(ge=0, le=3600)
@field_validator("metric")
@classmethod
def validate_metric(cls, v: str) -> str:
if v not in ALLOWED_METRICS:
raise ValueError(f"Unknown metric: {v}")
return v
perf_router = APIRouter(tags=["perf"])
@perf_router.post("/api/perf", status_code=204)
async def record_perf(samples: list[PerfSample]) -> None:
if len(samples) > MAX_BATCH_SIZE:
samples = samples[:MAX_BATCH_SIZE]
for s in samples:
attrs = {"operation": s.operation}
if s.metric == "worker_roundtrip":
app_metrics.frontend_worker_roundtrip.record(s.value, attrs)
elif s.metric == "worker_compute":
app_metrics.frontend_worker_compute.record(s.value, attrs)
elif s.metric == "main_thread":
app_metrics.frontend_main_thread.record(s.value, attrs)
elif s.metric == "feature_count":
app_metrics.frontend_feature_count.record(s.value)