Metadata-Version: 2.5
Name: lexigram-monitor
Version: 0.1.5007
Summary: Monitoring and observability for Lexigram Framework - Health checks, metrics, and system monitoring
Project-URL: Homepage, https://lexigram.dev
Project-URL: Repository, https://github.com/dbtinoy-/lexigram-dev
Project-URL: Documentation, https://docs.lexigram.dev
Project-URL: Issues, https://github.com/dbtinoy-/lexigram-dev/issues
Project-URL: Changelog, https://github.com/dbtinoy-/lexigram-dev/blob/main/CHANGELOG.md
Author-email: Lexigram Framework Team <team@lexigram.dev>
Maintainer-email: Lexigram Framework Team <team@lexigram.dev>
License: MIT
License-File: LICENSE
Keywords: async,framework,grafana,health-checks,metrics,monitoring,observability,prometheus
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: System :: Monitoring
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: lexigram-contracts>=0.1.4
Requires-Dist: lexigram>=0.1.4
Requires-Dist: starlette>=0.28.0
Requires-Dist: typer>=0.9.0
Requires-Dist: typing-extensions>=4.0.0
Provides-Extra: all
Requires-Dist: lexigram-testing>=0.1.4; extra == 'all'
Requires-Dist: opentelemetry-distro>=0.43b0; extra == 'all'
Requires-Dist: opentelemetry-exporter-otlp>=1.22.0; extra == 'all'
Requires-Dist: opentelemetry-instrumentation>=0.43b0; extra == 'all'
Requires-Dist: prometheus-client>=0.19.0; extra == 'all'
Requires-Dist: psutil>=5.9.0; extra == 'all'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'all'
Requires-Dist: pytest-cov>=4.0.0; extra == 'all'
Requires-Dist: pytest-mock>=3.10.0; extra == 'all'
Requires-Dist: pytest>=8.0.0; extra == 'all'
Provides-Extra: dev
Requires-Dist: black>=23.0.0; extra == 'dev'
Requires-Dist: mypy>=1.0.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Provides-Extra: otel
Requires-Dist: opentelemetry-distro>=0.43b0; extra == 'otel'
Requires-Dist: opentelemetry-exporter-otlp>=1.22.0; extra == 'otel'
Requires-Dist: opentelemetry-instrumentation>=0.43b0; extra == 'otel'
Provides-Extra: prometheus
Requires-Dist: prometheus-client>=0.19.0; extra == 'prometheus'
Provides-Extra: system
Requires-Dist: psutil>=5.9.0; extra == 'system'
Provides-Extra: test
Requires-Dist: lexigram-tasks>=0.1.4; extra == 'test'
Requires-Dist: lexigram-testing>=0.1.4; extra == 'test'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'test'
Requires-Dist: pytest-cov>=4.0.0; extra == 'test'
Requires-Dist: pytest-mock>=3.10.0; extra == 'test'
Requires-Dist: pytest>=8.0.0; extra == 'test'
Description-Content-Type: text/markdown

# lexigram-monitor

Observability, health checks, and metrics for the Lexigram Framework.
Supports Prometheus, OpenTelemetry, structured log export, and `/health` endpoints
that integrate with Kubernetes probes and load-balancer health checks.

---

## Overview

lexigram-monitor provides metrics collection, distributed tracing, health checks, and alerting for Lexigram applications. It integrates with Prometheus and OpenTelemetry backends, supports composable health checks with liveness and readiness flavours, and includes decorators for instrumenting services with custom metrics and traces. All services are wired via `MonitorProvider`, which registers monitoring protocols with the DI container.

> Full documentation: [docs.lexigram.dev](https://docs.lexigram.dev)
## Install

```bash
uv add lexigram-monitor
# Optional extras
uv add "lexigram-monitor[prometheus]"    # Prometheus + Grafana
uv add "lexigram-monitor[opentelemetry]" # OTLP / Jaeger / Zipkin
```

## Quick Start

```python
from lexigram import Application
from lexigram.monitor import MonitorModule


async def main() -> None:
    async with Application.boot(modules=[MonitorModule.configure()]) as app:
        # ... metrics, health checks and /health endpoints active ...
        ...


if __name__ == "__main__":
    import asyncio

    asyncio.run(main())
```

## Configuration

| Field | Default | Env var | Description |
|-------|---------|---------|-------------|
| `prometheus.enable_default_metrics` | `true` | `LEX_MONITOR__PROMETHEUS__ENABLE_DEFAULT_METRICS` | Enable default process metrics |
| `prometheus.port` | `8000` | `LEX_MONITOR__PROMETHEUS__PORT` | Port for the Prometheus metrics endpoint |
| `prometheus.path` | `/metrics` | `LEX_MONITOR__PROMETHEUS__PATH` | URL path for metrics scraping |
| `tracing.enabled` | `true` | `LEX_MONITOR__TRACING__ENABLED` | Enable distributed tracing via OTLP |
| `tracing.sample_rate` | `1.0` | `LEX_MONITOR__TRACING__SAMPLE_RATE` | Trace sampling rate (0.0–1.0; use 0.1 in production) |
| `health.path` | `/health` | `LEX_MONITOR__HEALTH__PATH` | Base path for health check endpoints |
| `health.interval` | `30` | `LEX_MONITOR__HEALTH__INTERVAL` | Seconds between background health polls |
| `health.timeout` | `5` | `LEX_MONITOR__HEALTH__TIMEOUT` | Per-check timeout in seconds |
| `logging.level` | `INFO` | `LEX_MONITOR__LOGGING__LEVEL` | Minimum log level (`DEBUG`, `INFO`, `WARNING`, `ERROR`) |
| `logging.format` | `json` | `LEX_MONITOR__LOGGING__FORMAT` | Log output format (`json` or `text`) |
| `slo.enabled` | `true` | `LEX_MONITOR__SLO__ENABLED` | Enable periodic SLO evaluation worker |
| `slo.evaluation_interval` | `60` | `LEX_MONITOR__SLO__EVALUATION_INTERVAL` | Seconds between SLO evaluation cycles |
| `slo.suppression_window_seconds` | `300` | `LEX_MONITOR__SLO__SUPPRESSION_WINDOW_SECONDS` | Min seconds between duplicate alerts |

## Endpoint protection

`HealthCheckProvider` and `PrometheusMiddleware` expose their endpoints
(`/health` and `/metrics` by default) **without authentication** — the
intentional default, because Kubernetes probes and Prometheus scrapers
usually run inside a trusted network and cannot always carry credentials.

If these endpoints are reachable from outside that boundary, pass an
`auth_token` to require `Authorization: Bearer <token>` on every request:

```python
from lexigram.monitor.middleware import HealthCheckProvider, PrometheusMiddleware

app = HealthCheckProvider(path="/health", auth_token=os.environ["HEALTH_TOKEN"])
app = PrometheusMiddleware(app, path="/metrics", auth_token=os.environ["METRICS_TOKEN"])
```

Requests without the matching token receive `401` with
`WWW-Authenticate: Bearer`. Configure the same token on the scraper side
(e.g. Prometheus `scrape_configs` → `authorization.credentials`).

Failed dependency checks never echo the raw driver message into the JSON
health payload — the response carries only the exception type name
(`"ConnectionError: connection check failed"`), while the full message is
written to the application logs.

## Module Factory Methods

| Method | Description |
|--------|-------------|
| `MonitorModule.configure(backend, config)` | Configure with explicit backend and optional MonitorConfig |
| `MonitorModule.stub()` | Minimal config for testing |
| `MonitorModule.with_slo(backend, config)` | Configure with SLO exports for the DI container |

## Key Features

- **Prometheus** — Auto `/metrics` endpoint; request counters, histograms, gauges
- **OpenTelemetry** — Distributed tracing via OTLP exporter to Jaeger / Honeycomb
- **Health checks** — Composable checks with liveness + readiness flavours
- **Cached checks** — Per-check TTL to avoid thundering-herd on slow dependencies
- **DB instrumentation** — Automatic query timing and error tagging
- **HTTP instrumentation** — Outbound request tracking for `lexigram-http`
- **Messaging instrumentation** — Kafka / RabbitMQ consumer lag, publish rate
- **Alerting** — Configurable alert rules with tier-aware webhook delivery
- **SLO Monitoring** — Burn-rate evaluation with configurable suppression window
- **Tiered alerts** — P0 (PagerDuty) / P1 (business hours Slack) / P2 (weekly digest) routing
- **Structured logging** — `json` / `text` log output via `logging.level` / `logging.format`
- **Grafana dashboards** — Pre-built dashboard JSON in `lexigram-monitor/dashboards/`

## Testing

```python
async with Application.boot(modules=[MonitorModule.stub()]) as app:
    # your test code
    ...
```

## Key Source Files

| File | What it contains |
|------|----------------|
| `src/lexigram/monitor/module.py` | `MonitorModule` class with factory methods |
| `src/lexigram/monitor/di/provider.py` | `MonitorProvider` — wires monitoring protocols into DI container |
| `src/lexigram/monitor/config.py` | `MonitorConfig` and sub-config dataclasses |
| `src/lexigram/monitor/health/` | Health check registration and registry (`base.py`, `checker.py`, `registry.py`, ...) |
| `src/lexigram/monitor/instrumentation/decorators.py` | `@metered` and `@traced` decorators |
| `src/lexigram/monitor/slo/` | SLO evaluation, tiered alert dispatchers, channel implementations |
| `src/lexigram/monitor/alerts/` | Alert dispatcher protocols and tier routing |
| `dashboards/projection-health.json` | Grafana dashboard for SLO health and alerting |

## SLO Monitoring

Service Level Objectives are evaluated on a configurable interval. Each SLO tracks a
metric percentile against a threshold and fires alerts on budget exhaustion.

### Defining an SLO

```python
from datetime import timedelta
from lexigram.contracts.monitor import ProjectionTier
from lexigram.monitor.slo import SLO, SLOMonitor

monitor = SLOMonitor()

slo = SLO(
    name="api.p99_latency",
    metric="http.request.duration",
    percentile=0.99,
    threshold_ms=200.0,
    window=timedelta(hours=1),
    tier=ProjectionTier.P1_BUSINESS_HOURS,
    owner="team-api",
    runbook_url="https://ops.runbook/api-slo",
)
monitor.register(slo)
```

### Recording Samples

```python
monitor.record_sample("http.request.duration", 150.0)
monitor.record_sample("http.request.duration", 350.0)
```

### Evaluating and Dispatching

```python
violations = await monitor.evaluate_and_dispatch()
```

Violations are routed through the configured `AlertDispatcherProtocol`. Alerts for the
same SLO are suppressed within the suppression window (default 300s) to avoid storms.

### Projection Tiers

| Tier | Enum Value | Behaviour |
|------|------------|-----------|
| P0 — Page | `ProjectionTier.P0_PAGE` | Routes to PagerDuty (or equivalent paging channel) immediately |
| P1 — Business Hours | `ProjectionTier.P1_BUSINESS_HOURS` | Queues outside business hours, flushes on schedule |
| P2 — Digest | `ProjectionTier.P2_DIGEST` | Accumulates in a weekly digest buffer |

### Worker Configuration

Enable periodic evaluation via config:

```yaml
# application.yaml
monitor:
  slo:
    enabled: true
    evaluation_interval: 60
    suppression_window_seconds: 300
```

Or via environment variables:

```bash
export LEX_MONITOR__SLO__ENABLED=true
export LEX_MONITOR__SLO__EVALUATION_INTERVAL=60
export LEX_MONITOR__SLO__SUPPRESSION_WINDOW_SECONDS=300
```
