Skip to main content

jerrycan_observe/
lib.rs

1//! Observability for jerrycan: request IDs, structured JSON access logs,
2//! /healthz, and a Prometheus /metrics endpoint. #![forbid(unsafe_code)].
3#![forbid(unsafe_code)]
4
5use jerrycan_core::{App, Extension, IntoResponse, Response, get};
6use std::sync::Arc;
7
8pub mod access_log;
9pub mod metrics;
10
11pub use metrics::Metrics;
12
13/// The observability extension: app-wide access-log middleware + health/metrics
14/// routes sharing one metrics registry.
15pub struct Observe {
16    metrics: Arc<Metrics>,
17}
18
19impl Observe {
20    pub fn new() -> Self {
21        Self {
22            metrics: Arc::new(Metrics::new()),
23        }
24    }
25}
26
27impl Default for Observe {
28    fn default() -> Self {
29        Self::new()
30    }
31}
32
33impl Extension for Observe {
34    fn register(self, app: App) -> App {
35        let metrics_for_mw = self.metrics.clone();
36        let metrics_for_route = self.metrics.clone();
37        app.middleware(access_log::AccessLog {
38            metrics: metrics_for_mw,
39        })
40        .route("/healthz", get(|| async { "ok" }))
41        .route(
42            "/metrics",
43            get(move || {
44                let metrics = metrics_for_route.clone();
45                async move { prometheus_response(metrics.render()) }
46            }),
47        )
48    }
49}
50
51/// Initialize JSON logging once (call from `main` before serving). Idempotent;
52/// honors `RUST_LOG`. No-op if a global subscriber is already set.
53pub fn init_logging() {
54    use tracing_subscriber::{EnvFilter, fmt};
55    let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
56    let _ = fmt().json().with_env_filter(filter).try_init();
57}
58
59fn prometheus_response(body: String) -> Response {
60    let mut response = body.into_response();
61    response.headers_mut().insert(
62        jerrycan_core::http::header::CONTENT_TYPE,
63        jerrycan_core::http::HeaderValue::from_static("text/plain; version=0.0.4"),
64    );
65    response
66}