Skip to content

Commit 3e5ffe4

Browse files
📝 Add docstrings to codex/create-json-dashboards-and-configure-alerts
Docstrings generation was requested by @shayancoin. * #76 (comment) The following files were modified: * `backend/api/main.py` * `backend/api/metrics.py` * `backend/api/routes_observability.py` * `backend/tests/test_observability_metrics.py` * `frontend/src/app/reportWebVitals.ts`
1 parent 310e008 commit 3e5ffe4

File tree

5 files changed

+93
-6
lines changed

5 files changed

+93
-6
lines changed

backend/api/main.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ async def _ensure_sqlite_tables() -> None:
5757

5858

5959
def _resolve_route_label(request: Request) -> str:
60+
"""
61+
Resolve a human-readable route label for the given request.
62+
63+
Parameters:
64+
request (Request): The incoming ASGI request from which to derive the route label.
65+
66+
Returns:
67+
str: The matched route's configured path if present (e.g. '/items/{id}'); otherwise the request URL path. If neither is available, returns "unknown".
68+
"""
6069
route = request.scope.get("route")
6170
if route and hasattr(route, "path") and route.path:
6271
return route.path
@@ -65,6 +74,18 @@ def _resolve_route_label(request: Request) -> str:
6574

6675
class PrometheusInstrumentationMiddleware(BaseHTTPMiddleware):
6776
async def dispatch(self, request: Request, call_next): # type: ignore[override]
77+
"""
78+
Instruments HTTP requests with Prometheus metrics, forwards the request to the next handler, and returns its response.
79+
80+
Skips instrumentation for the "/metrics" endpoint.
81+
82+
Parameters:
83+
request (Request): Incoming HTTP request.
84+
call_next (Callable[[Request], Response]): Callable that processes the request and returns a Response.
85+
86+
Returns:
87+
Response: The response produced by the next handler.
88+
"""
6889
if request.url.path == "/metrics":
6990
return await call_next(request)
7091

backend/api/metrics.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,16 @@ def observe_http_request(
6464
status: str,
6565
duration_seconds: float,
6666
) -> None:
67-
"""Record a single HTTP request observation."""
67+
"""
68+
Record a single HTTP request observation for Prometheus metrics.
69+
70+
Parameters:
71+
service (str): Logical name of the service handling the request (e.g., "api", "frontend").
72+
route (str): HTTP route or path being requested (e.g., "/users/{id}").
73+
method (str): HTTP method used for the request (e.g., "GET", "POST").
74+
status (str): HTTP response status code or status label (e.g., "200", "500").
75+
duration_seconds (float): Request duration in seconds to record in the latency histogram.
76+
"""
6877

6978
http_requests_total.labels(
7079
service=service, route=route, method=method, status=status
@@ -78,4 +87,3 @@ def observe_lcp(*, app: str, seconds: float) -> None:
7887
"""Record a Largest Contentful Paint measurement in seconds."""
7988

8089
web_vitals_lcp.labels(app=app).observe(seconds)
81-

backend/api/routes_observability.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,18 @@ class WebVitalPayload(BaseModel):
2121

2222
@router.post("/web-vitals", status_code=status.HTTP_202_ACCEPTED)
2323
async def ingest_web_vitals(payload: WebVitalPayload) -> dict[str, bool]:
24-
"""Ingest Web Vital measurements emitted by the frontend."""
24+
"""
25+
Accept a WebVitalPayload for the LCP metric, convert its value from milliseconds to seconds, and record the measurement.
26+
27+
Parameters:
28+
payload (WebVitalPayload): Web vital measurement; `name` must be "LCP", `value` is in milliseconds, and `app` identifies the source application.
29+
30+
Returns:
31+
result (dict[str, bool]): {"ok": True} on successful ingestion.
32+
33+
Raises:
34+
HTTPException: Raised with status 400 if `payload.name` is not "LCP".
35+
"""
2536

2637
if payload.name != "LCP":
2738
raise HTTPException(
@@ -32,4 +43,4 @@ async def ingest_web_vitals(payload: WebVitalPayload) -> dict[str, bool]:
3243
# web-vitals reports timings in milliseconds; convert to seconds for Prometheus
3344
seconds = payload.value / 1000.0
3445
observe_lcp(app=payload.app, seconds=seconds)
35-
return {"ok": True}
46+
return {"ok": True}

backend/tests/test_observability_metrics.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,26 @@
99

1010

1111
def _get_sample(name: str, labels: dict[str, str]) -> float:
12+
"""
13+
Retrieve the numeric value of a Prometheus metric sample identified by name and label set.
14+
15+
Parameters:
16+
name (str): Metric name to look up.
17+
labels (dict[str, str]): Label key-value pairs used to identify the specific metric sample.
18+
19+
Returns:
20+
float: The metric's value as a float if found, otherwise 0.0.
21+
"""
1222
value = REGISTRY.get_sample_value(name, labels)
1323
return float(value) if value is not None else 0.0
1424

1525

1626
def test_lcp_ingest_records_histogram() -> None:
27+
"""
28+
Verify posting an LCP web-vital increments the LCP count histogram for the specified app.
29+
30+
Sends an LCP payload to the observability ingest endpoint and asserts the `web_vitals_lcp_count` metric for `app="frontend"` increases by 1.
31+
"""
1732
before = _get_sample("web_vitals_lcp_count", {"app": "frontend"})
1833

1934
response = client.post(
@@ -38,6 +53,11 @@ def test_lcp_ingest_rejects_unsupported_metrics() -> None:
3853

3954

4055
def test_http_metrics_recorded_for_requests() -> None:
56+
"""
57+
Verifies that an HTTP request to /healthcheck increments the Prometheus http_requests_total metric for the corresponding route labels.
58+
59+
Reads the metric value for service="backend", route="/healthcheck", method="GET", status="200" before and after making a GET request to /healthcheck and asserts the metric increases by 1.
60+
"""
4161
route_labels = {
4262
"service": "backend",
4363
"route": "/healthcheck",
@@ -50,4 +70,4 @@ def test_http_metrics_recorded_for_requests() -> None:
5070

5171
assert resp.status_code == 200
5272
after = _get_sample("http_requests_total", route_labels)
53-
assert after == before + 1
73+
assert after == before + 1

frontend/src/app/reportWebVitals.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@ import type { Metric } from 'next/app';
33
const DEFAULT_ENDPOINT = '/observability/web-vitals';
44
const APP_LABEL = process.env.NEXT_PUBLIC_WEB_VITALS_APP ?? 'frontend';
55

6+
/**
7+
* Constructs the full endpoint URL used to report web-vitals metrics.
8+
*
9+
* When executed outside the browser (no `window`), returns `DEFAULT_ENDPOINT`. In a browser,
10+
* the URL is composed from `NEXT_PUBLIC_BACKEND_PROTOCOL`, `NEXT_PUBLIC_BACKEND_HOST`,
11+
* `NEXT_PUBLIC_BACKEND_PORT`, and `NEXT_PUBLIC_WEB_VITALS_ENDPOINT` environment variables with
12+
* fallbacks to `window.location` and sensible defaults, formatted as `protocol//host[:port]endpoint`.
13+
*
14+
* @returns The endpoint URL string for reporting web-vitals
15+
*/
616
function buildEndpoint(): string {
717
if (typeof window === 'undefined') {
818
return DEFAULT_ENDPOINT;
@@ -17,6 +27,14 @@ function buildEndpoint(): string {
1727
return `${protocol}//${host}${portSegment}${endpoint}`;
1828
}
1929

30+
/**
31+
* Sends a JSON metric payload to the given endpoint using a best-effort delivery strategy.
32+
*
33+
* Tries to use `navigator.sendBeacon` when available and falls back to a POST `fetch` with `keepalive`; network errors are ignored.
34+
*
35+
* @param endpoint - Destination URL to which the metric will be sent
36+
* @param body - JSON-serialized metric payload
37+
*/
2038
function sendMetric(endpoint: string, body: string) {
2139
if (typeof navigator !== 'undefined' && 'sendBeacon' in navigator) {
2240
const blob = new Blob([body], { type: 'application/json' });
@@ -38,6 +56,15 @@ function sendMetric(endpoint: string, body: string) {
3856
});
3957
}
4058

59+
/**
60+
* Report the largest contentful paint (LCP) web-vital metric to the configured backend endpoint.
61+
*
62+
* If `metric.name` is not `'LCP'`, the metric is ignored. For LCP metrics, a JSON payload
63+
* containing `name`, `value`, `app`, and `id` is constructed and transmitted using a
64+
* best-effort delivery method.
65+
*
66+
* @param metric - The web-vital metric object; only metrics with `name === 'LCP'` are reported. The `value` and `id` properties are included in the reported payload.
67+
*/
4168
export function reportWebVitals(metric: Metric) {
4269
if (metric.name !== 'LCP') {
4370
return;
@@ -52,4 +79,4 @@ export function reportWebVitals(metric: Metric) {
5279
});
5380

5481
sendMetric(endpoint, body);
55-
}
82+
}

0 commit comments

Comments
 (0)