from __future__ import annotations from typing import Dict, List, Tuple try: from prometheus_client import Counter as _PromCounter from prometheus_client import generate_latest as _prom_generate_latest from prometheus_client import CONTENT_TYPE_LATEST as _PROM_CONTENT_TYPE _PROM_OK = True except Exception: _PROM_OK = False if _PROM_OK: # Total send requests, labeled by routed node_id SOFIIA_SEND_REQUESTS_TOTAL = _PromCounter( "sofiia_send_requests_total", "Total number of send requests processed by sofiia-console", ["node_id"], ) # Total idempotency replays (same response served again) SOFIIA_IDEMPOTENCY_REPLAYS_TOTAL = _PromCounter( "sofiia_idempotency_replays_total", "Total number of idempotency replays served from cache", ) # Total cursor pagination requests SOFIIA_CURSOR_REQUESTS_TOTAL = _PromCounter( "sofiia_cursor_requests_total", "Total number of cursor pagination requests", ["resource"], ) def render_metrics() -> Tuple[bytes, str]: return _prom_generate_latest(), _PROM_CONTENT_TYPE else: class _FallbackCounterChild: def __init__(self, parent: "_FallbackCounter", key: Tuple[str, ...]): self._parent = parent self._key = key def inc(self, amount: float = 1.0) -> None: self._parent._values[self._key] = self._parent._values.get(self._key, 0.0) + float(amount) class _FallbackCounter: def __init__(self, name: str, doc: str, labelnames: List[str] | Tuple[str, ...] | None = None): self.name = name self.doc = doc self.labelnames = tuple(labelnames or []) self._values: Dict[Tuple[str, ...], float] = {} def labels(self, **kwargs: str) -> _FallbackCounterChild: key = tuple(str(kwargs.get(lbl, "")) for lbl in self.labelnames) if key not in self._values: self._values[key] = 0.0 return _FallbackCounterChild(self, key) def inc(self, amount: float = 1.0) -> None: key = tuple() self._values[key] = self._values.get(key, 0.0) + float(amount) def _render(self) -> List[str]: out = [f"# HELP {self.name} {self.doc}", f"# TYPE {self.name} counter"] if not self._values: if self.labelnames: out.append(f"{self.name}{{}} 0.0") else: out.append(f"{self.name} 0.0") return out for key, value in self._values.items(): if self.labelnames: labels = ",".join( f'{lname}="{lval}"' for lname, lval in zip(self.labelnames, key) ) out.append(f"{self.name}{{{labels}}} {value}") else: out.append(f"{self.name} {value}") return out SOFIIA_SEND_REQUESTS_TOTAL = _FallbackCounter( "sofiia_send_requests_total", "Total number of send requests processed by sofiia-console", ["node_id"], ) SOFIIA_IDEMPOTENCY_REPLAYS_TOTAL = _FallbackCounter( "sofiia_idempotency_replays_total", "Total number of idempotency replays served from cache", ) SOFIIA_CURSOR_REQUESTS_TOTAL = _FallbackCounter( "sofiia_cursor_requests_total", "Total number of cursor pagination requests", ["resource"], ) _ALL = [ SOFIIA_SEND_REQUESTS_TOTAL, SOFIIA_IDEMPOTENCY_REPLAYS_TOTAL, SOFIIA_CURSOR_REQUESTS_TOTAL, ] def render_metrics() -> Tuple[bytes, str]: lines: List[str] = [] for c in _ALL: lines.extend(c._render()) text = "\n".join(lines) + "\n" return text.encode("utf-8"), "text/plain; version=0.0.4; charset=utf-8"