# Observability

rs-zero 通过 `observability` feature 提供统一 Prometheus registry、自动指标记录和 tracing helper；真实 OTLP trace exporter 通过 `otlp` feature 启用。

## Metrics

`MetricsRegistry` 是 rs-zero 内置的 in-process Prometheus text registry。推荐每个服务创建一个 registry，并传入 REST、RPC、SQL、Redis、cache、resilience adapter。它适合默认 feature 和本地开发；极高并发或大量 label churn 场景应运行 `tests/metrics_stress.rs`，并评估是否启用 `observability-prometheus-client`。

```rust
use axum::{Router, routing::get};
use rs_zero::observability::{MetricsRegistry, metrics_router};
use rs_zero::rest::{ApiResponse, RestConfig, RestMetricsConfig, RestMiddlewareConfig, RestServer};

let metrics = MetricsRegistry::new();
let router = Router::new()
    .route("/ready", get(|| async { ApiResponse::success("ok") }))
    .merge(metrics_router(metrics.clone()));

let config = RestConfig {
    metrics_registry: Some(metrics),
    middlewares: RestMiddlewareConfig {
        metrics: RestMetricsConfig { enabled: true },
        ..RestMiddlewareConfig::default()
    },
    ..RestConfig::default()
};

let app = RestServer::new(config, router).into_router();
```

`metrics_router` 暴露 Prometheus text at `/metrics`。当前 registry 覆盖：

- HTTP request count、duration、in-flight。
- gRPC unary count、duration。
- SQL query count、duration、error count。
- Redis command count、duration、error count。
- cache hit/miss/set/delete/loader/backfill events。
- resilience breaker/shedder/concurrency/timeout events。

标签只使用低基数字段，禁止 raw path、SQL 文本、Redis key、cache key、请求体或用户 id。

### 成熟 Prometheus client adapter

`observability-prometheus-client` feature 提供 `PrometheusClientMetricsRegistry`，底层使用 `prometheus-client` crate，默认不启用：

```toml
rs-zero = { version = "0.1", features = ["observability-prometheus-client"] }
```

该 adapter 适合需要对接成熟 OpenMetrics/Prometheus client 生态的服务。当前它覆盖 HTTP、RPC、SQL、Redis 的核心 counter 与 duration histogram；cache/resilience 仍由内置 registry 覆盖。两种 registry 不应在同一条请求路径重复记录同一指标。

### 自动覆盖边界

- REST metrics middleware 自动记录 matched route pattern。
- Redis cache adapter 内部通过 `RedisCommandRecorder` 记录 pool、command、degradation、breaker 事件，不记录 Redis key 或 URL。
- SQL 生成 repository 和 `health_check_with_metrics` 会调用 `observe_sql_query`；手写 SQL 仍应通过 repository 封装或显式 helper 接入。
- RPC `RpcResilienceLayer`、`RpcUnaryResilienceLayer` 和生成 skeleton 默认记录 unary/streaming 边界；手写 tonic stack 未挂 layer 时不会自动记录。

## Tracing 与日志关联

REST 使用 `x-request-id` header，gRPC 使用同名 metadata。HTTP/RPC span 会带上统一 `CorrelationContext` 字段：`request_id`、合法入站 `traceparent`、`trace_id/span_id`、service、transport、route/method、status/code。REST 只记录 matched route pattern；缺少 pattern 时写 `unknown`，不会把 raw path、query、用户 id 或 token 写入日志关联字段。

`core::init_tracing` 会输出 span close event，便于从日志关联到 request id 与 span 字段。`LogConfig` 默认在 subscriber 最终输出前脱敏，覆盖 text/JSON、span fields、event fields 和错误文本；JSON 输出脱敏后仍保持可解析。`RollingFile` 支持运行期按 `max_bytes` 大小轮转并通过 `max_files` 控制保留数量；`max_age` 仅作为平台日志策略边界，按时间清理应交给 logrotate、journald、容器日志驱动或托管日志平台。

## OpenTelemetry

`OpenTelemetryConfig` models three exporter modes:

- `Disabled`: no global subscriber is installed.
- `Stdout`: installs a local `tracing-subscriber` pipeline for span inspection.
- `Otlp { endpoint }`: installs a real OTLP tracing pipeline when the `otlp` feature is enabled.

Use `init_opentelemetry_tracing_with_handle` when the application needs to flush or shut down exporters explicitly.

## OTLP traces

`OtlpTraceConfig` supports gRPC/HTTP protobuf protocol selection, headers, resource attributes, sampling ratio, timeout and `TraceShutdownHandle`. Default tests do not require a collector; external collector checks stay ignored/gated.
