Coverage for src / lexigram / contracts / observability / metrics.py: 0%
40 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-15 18:57 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-15 18:57 +0800
1"""Monitoring protocols.
3Protocols for metrics, tracing, and health checking.
4"""
6from __future__ import annotations
8from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
10from lexigram.contracts.core.health import HealthCheckCategory
12if TYPE_CHECKING:
13 from collections.abc import Callable
16@runtime_checkable
17class AlertDispatcherProtocol(Protocol):
18 """Protocol for dispatching operational alerts.
20 Implementations route alerts to notification channels (logging,
21 PagerDuty, Slack, etc.). ``lexigram-monitor`` ships a built-in
22 :class:`~lexigram.monitor.alerts.LoggingAlertDispatcher` that writes
23 alerts to the structured logger.
25 Example::
27 class SlackAlertDispatcher:
28 async def send_alert(
29 self,
30 title: str,
31 message: str,
32 severity: str,
33 context: dict[str, Any] | None = None,
34 ) -> None:
35 await self._slack.post(
36 channel="#ops",
37 text=f"[{severity}] {title}: {message}",
38 )
39 """
41 async def send_alert(
42 self,
43 title: str,
44 message: str,
45 severity: str,
46 context: dict[str, Any] | None = None,
47 ) -> None:
48 """Dispatch a free-form operational alert.
50 Args:
51 title: Short, human-readable alert title.
52 message: Detailed alert message.
53 severity: Severity level string, e.g. ``"low"``, ``"high"``,
54 ``"critical"``.
55 context: Optional free-form mapping of additional metadata.
56 """
57 ...
59 async def send_metric_alert(
60 self,
61 metric_name: str,
62 current_value: float,
63 threshold: float,
64 context: dict[str, Any] | None = None,
65 ) -> None:
66 """Dispatch an alert triggered by a metric threshold breach.
68 Args:
69 metric_name: Name of the metric that breached its threshold.
70 current_value: Observed metric value at the time of the alert.
71 threshold: The configured threshold that was exceeded.
72 context: Optional free-form mapping of additional metadata.
73 """
74 ...
77@runtime_checkable
78class MetricsRecorderProtocol(Protocol):
79 """Record pre-defined metrics. Minimal interface for resilience, events, etc."""
81 def increment(
82 self,
83 name: str,
84 value: float = 1.0,
85 tags: dict[str, str] | None = None,
86 ) -> None:
87 """Increment a counter metric.
89 Args:
90 name: MetricProtocol name.
91 value: Value to increment by.
92 tags: Optional tags/labels.
93 """
94 ...
96 def gauge(
97 self,
98 name: str,
99 value: float,
100 tags: dict[str, str] | None = None,
101 ) -> None:
102 """Set a gauge metric.
104 Args:
105 name: MetricProtocol name.
106 value: Current value.
107 tags: Optional tags/labels.
108 """
109 ...
111 def histogram(
112 self,
113 name: str,
114 value: float,
115 tags: dict[str, str] | None = None,
116 ) -> None:
117 """Record a histogram value.
119 Args:
120 name: MetricProtocol name.
121 value: Value to record.
122 tags: Optional tags/labels.
123 """
124 ...
127@runtime_checkable
128class MetricsFactoryProtocol(Protocol):
129 """Create metric instruments. Extended interface for lexigram-monitor."""
131 def register_metric(self, metric: MetricProtocol) -> None:
132 """Register an existing metric instrument.
134 Args:
135 metric: Pre-defined metric instance.
136 """
137 ...
139 def create_counter(
140 self,
141 name: str,
142 description: str = "",
143 labels: dict[str, str] | None = None,
144 ) -> Any:
145 """Create a counter metric.
147 Args:
148 name: MetricProtocol name.
149 description: MetricProtocol description.
150 labels: Default labels.
152 Returns:
153 Counter metric instance.
154 """
155 ...
157 def create_gauge(
158 self,
159 name: str,
160 description: str = "",
161 labels: dict[str, str] | None = None,
162 ) -> Any:
163 """Create a gauge metric.
165 Args:
166 name: MetricProtocol name.
167 description: MetricProtocol description.
168 labels: Default labels.
170 Returns:
171 Gauge metric instance.
172 """
173 ...
175 def create_histogram(
176 self,
177 name: str,
178 description: str = "",
179 labels: dict[str, str] | None = None,
180 buckets: list[float] | None = None,
181 ) -> Any:
182 """Create a histogram metric.
184 Args:
185 name: MetricProtocol name.
186 description: MetricProtocol description.
187 labels: Default labels.
188 buckets: Histogram buckets.
190 Returns:
191 Histogram metric instance.
192 """
193 ...
196@runtime_checkable
197class MetricProtocol(Protocol):
198 """Protocol for metric implementations.
200 Metrics track numeric measurements over time.
201 """
203 @property
204 def name(self) -> str:
205 """MetricProtocol name."""
206 ...
208 @property
209 def description(self) -> str:
210 """MetricProtocol description."""
211 ...
213 def record(self, value: float, labels: dict[str, str] | None = None) -> None:
214 """Record a metric value.
216 Args:
217 value: Numeric value to record.
218 labels: Optional labels/tags.
219 """
220 ...
223@runtime_checkable
224class MetricsBackendProtocol(Protocol):
225 """Protocol for metrics backend implementations (metrics only).
227 Backends export metrics to external systems.
228 """
230 async def initialize(self) -> None:
231 """Initialize the metrics backend."""
232 ...
234 async def shutdown(self) -> None:
235 """Shutdown the metrics backend."""
236 ...
238 def record_metric(
239 self,
240 name: str,
241 value: Any,
242 metric_type: str,
243 labels: dict[str, str] | None = None,
244 ) -> None:
245 """Record a metric value.
247 Args:
248 name: MetricProtocol name.
249 value: MetricProtocol value.
250 metric_type: Type of metric (counter, gauge, histogram).
251 labels: Optional labels.
252 """
253 ...
256@runtime_checkable
257class MetricsCollectorProtocol(
258 MetricsRecorderProtocol, MetricsFactoryProtocol, Protocol
259):
260 """Full metrics capability. Implemented by lexigram-monitor.
262 Combines recording capabilities (increment, gauge, histogram) with
263 factory capabilities (create_counter, create_gauge, create_histogram).
264 """
267@runtime_checkable
268class HealthCheckRegistryProtocol(Protocol):
269 """Protocol for a categorised health check registry.
271 Implementations (e.g. ``HealthChecker``) store checks tagged with a
272 :class:`~lexigram.contracts.core.health.HealthCheckCategory` so that
273 callers can query subsets independently:
275 * ``run_liveness`` — is the process alive and not deadlocked?
276 * ``run_readiness`` — is the process ready to accept traffic?
277 * ``run_startup`` — has initial startup completed?
279 This maps directly to the three Kubernetes probe types.
280 """
282 def add(
283 self,
284 name: str,
285 check: Callable[[], Any],
286 *,
287 timeout: float | None = None,
288 critical: bool = True,
289 category: HealthCheckCategory = HealthCheckCategory.READINESS,
290 ) -> None:
291 """Register a categorised health check.
293 Args:
294 name: Unique identifier for the check.
295 check: Callable that performs the check.
296 timeout: Optional per-check timeout in seconds.
297 critical: Whether a non-healthy result should make the aggregate
298 readiness status ``UNHEALTHY``. Defaults to ``True``.
299 category: :class:`~lexigram.contracts.core.health.HealthCheckCategory`
300 value. Defaults to ``READINESS``.
301 """
302 ...
304 async def run_all(self) -> tuple[Any, dict[str, Any]]:
305 """Run all registered checks regardless of category.
307 Returns:
308 ``(aggregate_status, per_check_results)`` tuple.
309 """
310 ...
312 async def run_liveness(self) -> tuple[Any, dict[str, Any]]:
313 """Run only LIVENESS checks.
315 Returns:
316 ``(aggregate_status, per_check_results)`` tuple.
317 """
318 ...
320 async def run_readiness(self) -> tuple[Any, dict[str, Any]]:
321 """Run only READINESS checks.
323 Returns:
324 ``(aggregate_status, per_check_results)`` tuple.
325 """
326 ...
328 async def run_startup(self) -> tuple[Any, dict[str, Any]]:
329 """Run only STARTUP checks.
331 Returns:
332 ``(aggregate_status, per_check_results)`` tuple.
333 """
334 ...
337__all__ = [
338 "AlertDispatcherProtocol",
339 "HealthCheckRegistryProtocol",
340 "MetricProtocol",
341 "MetricsBackendProtocol",
342 "MetricsCollectorProtocol",
343 "MetricsFactoryProtocol",
344 "MetricsRecorderProtocol",
345]