"""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"}