p7: add Observability section — metrics, OTel tracing, structured logging, health checks
swadhinbiswas committed 3 months ago
230c2cd
1 parent fe3c643
commit 230c2cd
4 files changed
+264 -0
Showing 4 changed files+264-0
▼docs_site/src/content/docs/observability/health-checks.md
+59-0
| @@ -0,0 +1,59 @@ | ||
1 | +--- | |
2 | +title: Health Checks | |
3 | +description: Liveness, readiness, and custom health check endpoints. | |
4 | +--- | |
5 | + | |
6 | +## Built-in Endpoints | |
7 | + | |
8 | +Every JustAPI application exposes three health endpoints by default: | |
9 | + | |
10 | +| Endpoint | Type | Returns | Use | | |
11 | +|---|---|---|---| | |
12 | +| `GET /health` | Liveness | `200 {"status":"ok"}` | Process alive check | | |
13 | +| `GET /live` | Liveness | `200 {"status":"ok"}` | Lightweight alias | | |
14 | +| `GET /ready` | Readiness | `200` or `503` | Dependency health | | |
15 | + | |
16 | +## Readiness Probe | |
17 | + | |
18 | +`/ready` returns `503` if any registered health check fails: | |
19 | + | |
20 | +```bash | |
21 | +curl http://localhost:8000/ready | |
22 | +# When healthy: {"status":"ok","checks":{"postgres":"healthy"}} | |
23 | +# When unhealthy: {"status":"unavailable","checks":{"postgres":"unhealthy"}} | |
24 | +``` | |
25 | + | |
26 | +## Custom Health Checks | |
27 | + | |
28 | +```python | |
29 | +app.register_health_check("postgres", lambda: check_db_connection()) | |
30 | +app.register_health_check("redis", lambda: check_redis_connection()) | |
31 | +app.register_health_check("external_api", lambda: ping_external_api()) | |
32 | +``` | |
33 | + | |
34 | +Each check function should return `True` (healthy) or raise an exception. | |
35 | + | |
36 | +## Kubernetes Probe Configuration | |
37 | + | |
38 | +```yaml | |
39 | +livenessProbe: | |
40 | + httpGet: | |
41 | + path: /health | |
42 | + port: 8080 | |
43 | + initialDelaySeconds: 5 | |
44 | + periodSeconds: 15 | |
45 | + | |
46 | +readinessProbe: | |
47 | + httpGet: | |
48 | + path: /ready | |
49 | + port: 8080 | |
50 | + initialDelaySeconds: 10 | |
51 | + periodSeconds: 10 | |
52 | + failureThreshold: 3 | |
53 | +``` | |
54 | + | |
55 | +## See Also | |
56 | + | |
57 | +- [Metrics & Monitoring](/observability/metrics-monitoring/) — Prometheus metrics | |
58 | +- [OpenTelemetry](/observability/opentelemetry/) — Distributed tracing | |
59 | +- [Production Checklist](/deployment/production-checklist/) — Production setup | |
0 | 60 | |
▼docs_site/src/content/docs/observability/metrics-monitoring.md
+77-0
| @@ -0,0 +1,77 @@ | ||
1 | +--- | |
2 | +title: Metrics & Monitoring | |
3 | +description: Export Prometheus metrics and monitor your JustAPI application. | |
4 | +--- | |
5 | + | |
6 | +## Metrics Endpoint | |
7 | + | |
8 | +Every JustAPI application exposes Prometheus-compatible metrics at `/metrics`: | |
9 | + | |
10 | +```bash | |
11 | +curl http://localhost:8000/metrics | |
12 | +``` | |
13 | + | |
14 | +## Available Metrics | |
15 | + | |
16 | +| Metric | Type | Description | | |
17 | +|---|---|---| | |
18 | +| `requests_total` | Counter | Total requests by route and status | | |
19 | +| `request_duration_seconds` | Histogram | Request latency (p50, p95, p99, p999) | | |
20 | +| `inbound_bytes_total` | Counter | Total bytes received | | |
21 | +| `outbound_bytes_total` | Counter | Total bytes sent | | |
22 | +| `connections_active` | Gauge | Currently active connections | | |
23 | +| `errors_total` | Counter | Error count by type | | |
24 | + | |
25 | +## Prometheus Scrape Configuration | |
26 | + | |
27 | +```yaml | |
28 | +scrape_configs: | |
29 | + - job_name: 'justapi' | |
30 | + static_configs: | |
31 | + - targets: ['localhost:8000'] | |
32 | + metrics_path: /metrics | |
33 | + scrape_interval: 15s | |
34 | +``` | |
35 | + | |
36 | +## Grafana Dashboard | |
37 | + | |
38 | +Key panels to create: | |
39 | + | |
40 | +1. **Request Rate** — `rate(requests_total[1m])` by route | |
41 | +2. **Latency** — `histogram_quantile(0.99, rate(request_duration_seconds_bucket[5m]))` | |
42 | +3. **Error Ratio** — `rate(errors_total[5m]) / rate(requests_total[5m])` | |
43 | +4. **Active Connections** — `connections_active` | |
44 | + | |
45 | +## Alerting Rules | |
46 | + | |
47 | +```yaml | |
48 | +groups: | |
49 | + - name: justapi | |
50 | + rules: | |
51 | + - alert: HighErrorRate | |
52 | + expr: rate(errors_total[5m]) / rate(requests_total[5m]) > 0.05 | |
53 | + for: 5m | |
54 | + labels: { severity: critical } | |
55 | + | |
56 | + - alert: HighLatency | |
57 | + expr: histogram_quantile(0.99, rate(request_duration_seconds_bucket[5m])) > 1 | |
58 | + for: 5m | |
59 | + labels: { severity: warning } | |
60 | +``` | |
61 | + | |
62 | +## Built-in Alerting | |
63 | + | |
64 | +JustAPI supports webhook-based alerting: | |
65 | + | |
66 | +```python | |
67 | +app.enable_alerting( | |
68 | + slack_webhook="https://hooks.slack.com/services/...", | |
69 | + pagerduty_key="...", | |
70 | +) | |
71 | +``` | |
72 | + | |
73 | +## See Also | |
74 | + | |
75 | +- [OpenTelemetry](/observability/opentelemetry/) — Distributed tracing | |
76 | +- [Structured Logging](/observability/structured-logging/) — Log configuration | |
77 | +- [Health Checks](/observability/health-checks/) — Health endpoints | |
0 | 78 | |
▼docs_site/src/content/docs/observability/opentelemetry.md
+67-0
| @@ -0,0 +1,67 @@ | ||
1 | +--- | |
2 | +title: OpenTelemetry Tracing | |
3 | +description: Distributed tracing with OpenTelemetry and Jaeger. | |
4 | +--- | |
5 | + | |
6 | +JustAPI includes built-in OpenTelemetry tracing with OTLP gRPC export. | |
7 | + | |
8 | +## Enabling Tracing | |
9 | + | |
10 | +```python | |
11 | +from justapi.tracing import init_otlp_tracing | |
12 | + | |
13 | +init_otlp_tracing( | |
14 | + endpoint="http://localhost:4317", | |
15 | + service_name="my-justapi-service", | |
16 | +) | |
17 | +``` | |
18 | + | |
19 | +## Jaeger Stack | |
20 | + | |
21 | +Generated projects include `docker-compose.otel.yml`: | |
22 | + | |
23 | +```yaml | |
24 | +services: | |
25 | + jaeger: | |
26 | + image: jaegertracing/all-in-one:latest | |
27 | + ports: | |
28 | + - "16686:16686" # Jaeger UI | |
29 | + - "4318:4318" # OTLP HTTP | |
30 | + - "4317:4317" # OTLP gRPC | |
31 | +``` | |
32 | + | |
33 | +```bash | |
34 | +docker compose -f docker-compose.otel.yml up -d | |
35 | +``` | |
36 | + | |
37 | +Access the Jaeger UI at `http://localhost:16686`. | |
38 | + | |
39 | +## Trace Spans | |
40 | + | |
41 | +Each request generates spans: | |
42 | + | |
43 | +| Span | Description | | |
44 | +|---|---| | |
45 | +| `request` | Full HTTP request lifecycle | | |
46 | +| `handler.execute` | Python handler execution | | |
47 | +| `middleware.*` | Each middleware in the chain | | |
48 | +| `db.query` | Database query execution | | |
49 | +| `db.execute` | Database write execution | | |
50 | + | |
51 | +## Context Propagation | |
52 | + | |
53 | +Trace context propagates across the Python↔Rust boundary using thread-local variables. No manual instrumentation is needed. | |
54 | + | |
55 | +## Environment Variables | |
56 | + | |
57 | +| Variable | Description | | |
58 | +|---|---| | |
59 | +| `OTEL_SERVICE_NAME` | Service name for traces | | |
60 | +| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP collector endpoint | | |
61 | +| `RUST_LOG` | Log level (set to `info` for production) | | |
62 | + | |
63 | +## See Also | |
64 | + | |
65 | +- [Metrics & Monitoring](/observability/metrics-monitoring/) — Prometheus metrics | |
66 | +- [Structured Logging](/observability/structured-logging/) — Log configuration | |
67 | +- [Health Checks](/observability/health-checks/) — Health endpoints | |
0 | 68 | |
▼docs_site/src/content/docs/observability/structured-logging.md
+61-0
| @@ -0,0 +1,61 @@ | ||
1 | +--- | |
2 | +title: Structured Logging | |
3 | +description: Configure structured JSON logging with tracing-subscriber. | |
4 | +--- | |
5 | + | |
6 | +JustAPI uses `tracing` for structured logging, supporting both human-readable text and machine-parseable JSON formats. | |
7 | + | |
8 | +## Default Logging | |
9 | + | |
10 | +By default, JustAPI logs to stdout in a human-readable format: | |
11 | + | |
12 | +```bash | |
13 | +RUST_LOG=info python app.py | |
14 | +``` | |
15 | + | |
16 | +## JSON Log Format | |
17 | + | |
18 | +Enable JSON logging for production log aggregation: | |
19 | + | |
20 | +```python | |
21 | +from justapi.logging import init_json_logging | |
22 | + | |
23 | +init_json_logging() | |
24 | +``` | |
25 | + | |
26 | +JSON output: | |
27 | + | |
28 | +```json | |
29 | +{"timestamp":"2026-07-24T15:00:00Z","level":"INFO","message":"Server started","addr":"127.0.0.1:8000"} | |
30 | +{"timestamp":"2026-07-24T15:00:01Z","level":"INFO","message":"Request completed","method":"GET","path":"/items/42","status":200,"duration_ms":1.2} | |
31 | +``` | |
32 | + | |
33 | +## Log Levels | |
34 | + | |
35 | +Set via `RUST_LOG` environment variable: | |
36 | + | |
37 | +| Level | Description | | |
38 | +|---|---| | |
39 | +| `error` | Only errors | | |
40 | +| `warn` | Warnings and errors | | |
41 | +| `info` | Normal operational messages (default) | | |
42 | +| `debug` | Detailed debugging | | |
43 | +| `trace` | Very detailed, high-frequency events | | |
44 | + | |
45 | +```bash | |
46 | +RUST_LOG=warn python app.py # Less verbose | |
47 | +RUST_LOG=debug python app.py # More verbose | |
48 | +``` | |
49 | + | |
50 | +## Production Guidelines | |
51 | + | |
52 | +- Set `RUST_LOG=info` in production | |
53 | +- Do **not** log request bodies (may contain PII/secrets) | |
54 | +- Use JSON format for log aggregation (ELK, Loki, Datadog, etc.) | |
55 | +- Configure log sampling for high-traffic endpoints | |
56 | + | |
57 | +## See Also | |
58 | + | |
59 | +- [Metrics & Monitoring](/observability/metrics-monitoring/) — Prometheus metrics | |
60 | +- [OpenTelemetry](/observability/opentelemetry/) — Distributed tracing | |
61 | +- [Health Checks](/observability/health-checks/) — Health endpoints | |
0 | 62 | |