Skip to main content

faucet_cli/serve/
observability.rs

1//! serve-owned observability: install the Prometheus recorder (returning a
2//! render handle for the `/metrics` route) and a tracing subscriber whose fmt
3//! layer routes through the secret-redacting writer and whose `RunLogLayer`
4//! feeds the per-run SSE log buffers. Both are process-global and set-once; a
5//! second install in the same process is tolerated (returns no handle / leaves
6//! the existing subscriber). The returned [`LogHub`] is shared with
7//! `ServerState` so the `/logs` handler reads the same buffers the layer writes.
8
9use crate::serve::logs::LogHub;
10use metrics_exporter_prometheus::{PrometheusBuilder, PrometheusHandle};
11use std::sync::OnceLock;
12
13/// The tracing subscriber is process-global and set-once, so the [`LogHub`] wired
14/// into it is too. A second `serve` in the same process (e.g. multiple tests)
15/// reuses this hub rather than getting a detached one whose lines are never
16/// captured by the live subscriber.
17static LOG_HUB: OnceLock<LogHub> = OnceLock::new();
18
19/// Install the recorder + tracing subscriber. Returns the Prometheus render
20/// handle (when this call installed the recorder; `None` if one was already
21/// present) and the process-global [`LogHub`] wired into the subscriber's
22/// `RunLogLayer`.
23pub fn install(level: &str) -> (Option<PrometheusHandle>, LogHub) {
24    let handle = match PrometheusBuilder::new().install_recorder() {
25        Ok(h) => Some(h),
26        Err(e) => {
27            tracing::warn!("Prometheus recorder already installed or failed: {e}");
28            None
29        }
30    };
31    // Register faucet_build_info into whatever recorder is now global.
32    faucet_core::register_build_info();
33
34    let hub = LOG_HUB.get_or_init(LogHub::new).clone();
35    // Only the first call's `try_init` succeeds; subsequent calls leave the
36    // already-installed subscriber (which holds this same hub) in place.
37    install_subscriber(level, hub.clone());
38    (handle, hub)
39}
40
41#[cfg(feature = "observability")]
42fn install_subscriber(level: &str, hub: LogHub) {
43    use crate::secrets::registry::RedactingMakeWriter;
44    use crate::serve::logs::RunLogLayer;
45    use tracing_subscriber::EnvFilter;
46    use tracing_subscriber::layer::SubscriberExt;
47    use tracing_subscriber::util::SubscriberInitExt;
48
49    let filter = EnvFilter::try_new(level).unwrap_or_else(|_| EnvFilter::new("info"));
50    let registry = tracing_subscriber::registry()
51        .with(filter)
52        .with(tracing_subscriber::fmt::layer().with_writer(RedactingMakeWriter))
53        .with(RunLogLayer::new(hub));
54    if registry.try_init().is_err() {
55        tracing::warn!("tracing subscriber already installed; continuing");
56    }
57}
58
59#[cfg(not(feature = "observability"))]
60fn install_subscriber(_level: &str, _hub: LogHub) {}