File size: 2,704 Bytes
a8d69a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from routers import eda as _eda_router
from routers._suggest import router as _suggest_router
import collections
import time
from typing import Dict

app = FastAPI(title="ML EDA")
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

# ── Request monitoring ────────────────────────────────────────────────────────
_SKIP_PATHS = {"/", "/health", "/metrics"}
_req_log: collections.deque = collections.deque(maxlen=1000)
_svc_start = time.time()


@app.middleware("http")
async def _monitor(request: Request, call_next):
    t0 = time.perf_counter()
    response = await call_next(request)
    ms = round((time.perf_counter() - t0) * 1000, 1)
    if request.url.path not in _SKIP_PATHS:
        _req_log.append({
            "ts":     time.time(),
            "path":   request.url.path,
            "method": request.method,
            "status": response.status_code,
            "ms":     ms,
        })
    return response


@app.get("/health")
def health():
    return {"status": "ok", "service": "ml-eda"}


@app.get("/metrics")
def get_metrics():
    logs = list(_req_log)
    uptime_s = int(time.time() - _svc_start)
    if not logs:
        return {"service": "ml-eda", "uptime_s": uptime_s,
                "total_requests": 0, "avg_ms": 0, "p95_ms": 0,
                "error_rate": 0.0, "endpoints": []}
    by_path: Dict[str, list] = collections.defaultdict(list)
    for r in logs:
        by_path[r["path"]].append(r)
    endpoints = []
    for path, reqs in sorted(by_path.items(), key=lambda x: -len(x[1])):
        times = sorted(r["ms"] for r in reqs)
        errs  = sum(1 for r in reqs if r["status"] >= 400)
        endpoints.append({
            "path":   path,
            "count":  len(reqs),
            "avg_ms": round(sum(times) / len(times), 1),
            "p95_ms": times[min(int(len(times) * 0.95), len(times) - 1)],
            "errors": errs,
        })
    all_ms  = sorted(r["ms"] for r in logs)
    all_err = sum(1 for r in logs if r["status"] >= 400)
    return {
        "service":        "ml-eda",
        "uptime_s":       uptime_s,
        "total_requests": len(logs),
        "avg_ms":         round(sum(all_ms) / len(all_ms), 1),
        "p95_ms":         all_ms[min(int(len(all_ms) * 0.95), len(all_ms) - 1)],
        "error_rate":     round(all_err / len(logs) * 100, 1),
        "endpoints":      endpoints,
    }


app.include_router(_eda_router.router)
app.include_router(_suggest_router)