v1
Some checks failed
CI / test (3.11) (push) Failing after 5m36s
CI / build-image (push) Has been skipped

This commit is contained in:
2025-10-22 16:48:55 +02:00
commit 4cefd4e3ab
53 changed files with 5837 additions and 0 deletions

86
server/metrics.py Normal file
View File

@@ -0,0 +1,86 @@
"""Metrics registry and helpers for Prometheus and JSON fallbacks."""
from __future__ import annotations
import logging
import time
from typing import Any, Dict, Tuple
try:
from prometheus_client import CollectorRegistry, Counter, Histogram, generate_latest, CONTENT_TYPE_LATEST
except Exception:
CollectorRegistry = None # type: ignore
Counter = None # type: ignore
Histogram = None # type: ignore
generate_latest = None # type: ignore
CONTENT_TYPE_LATEST = "text/plain; version=0.0.4; charset=utf-8"
_start_time = time.time()
_total_submissions = 0
_prom_registry = CollectorRegistry() if CollectorRegistry is not None else None
_prom_total_submissions = (
Counter("contact_total_submissions", "Total contact submissions", registry=_prom_registry)
if Counter is not None
else None
)
_prom_request_counter = None
_prom_request_latency = None
if Counter is not None and _prom_registry is not None:
try:
_prom_request_counter = Counter(
"http_requests_total",
"Total HTTP requests",
["method", "endpoint"],
registry=_prom_registry,
)
except Exception:
_prom_request_counter = None
if Histogram is not None and _prom_registry is not None:
try:
_prom_request_latency = Histogram(
"http_request_duration_seconds",
"Request duration",
["method", "endpoint"],
registry=_prom_registry,
)
except Exception:
_prom_request_latency = None
def record_submission() -> None:
"""Register a completed contact submission."""
global _total_submissions
_total_submissions += 1
if _prom_total_submissions is not None:
try:
_prom_total_submissions.inc()
except Exception:
logging.debug("Failed to increment Prometheus submission counter", exc_info=True)
def observe_request(method: str, endpoint: str, start_time: float | None, status: int | None = None) -> None:
"""Update request counters and latency histograms."""
if _prom_request_counter is not None:
try:
_prom_request_counter.labels(method=method, endpoint=endpoint).inc()
except Exception:
logging.debug("Failed to increment request counter", exc_info=True)
if _prom_request_latency is not None and start_time:
try:
_prom_request_latency.labels(method=method, endpoint=endpoint).observe(time.time() - start_time)
except Exception:
logging.debug("Failed to observe request latency", exc_info=True)
def export_metrics() -> Tuple[Any, int, Dict[str, str]]:
"""Return a Flask-style response tuple for the metrics endpoint."""
uptime = int(time.time() - _start_time)
if generate_latest is not None and _prom_registry is not None:
payload = generate_latest(_prom_registry)
headers = {"Content-Type": CONTENT_TYPE_LATEST}
return payload, 200, headers
body = {"uptime_seconds": uptime, "total_submissions": _total_submissions}
return body, 200, {"Content-Type": "application/json"}