v1
This commit is contained in:
86
server/metrics.py
Normal file
86
server/metrics.py
Normal 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"}
|
||||
Reference in New Issue
Block a user