//! Tracing subscriber initialisation with JSON output. //! //! Uses `tracing_subscriber::fmt` with a custom `Make` impl that injects //! `instance_id` into every log line. use once_cell::sync::Lazy; use std::str::FromStr; use tracing_subscriber::{ fmt::{self, format::FmtSpan}, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, }; /// Global instance identifier, resolved once at startup. /// Priority: `INSTANCE_ID` env var → system hostname → `"unknown"`. pub static INSTANCE_ID: Lazy = Lazy::new(|| { std::env::var("INSTANCE_ID") .ok() .filter(|s| !s.is_empty()) .or_else(|| { hostname::get() .ok() .and_then(|h| h.into_string().ok()) .filter(|s| !s.is_empty()) }) .unwrap_or_else(|| "unknown".to_string()) }); /// Returns the platform-wide instance identifier for this process. pub fn instance_id() -> String { INSTANCE_ID.clone() } /// Initialises the global tracing subscriber with JSON-formatted output to stderr. /// /// Each JSON line includes `ts`, `level`, `target` (module), `fields` (structured kv), /// `line`, `file`, and `instance_id`. /// `RUST_LOG` env var controls the log level filter. /// /// Pass `defer = true` when OTLP will be initialized afterwards via `init_otlp()`; /// in that case this function only builds the subscriber without calling `try_init()`, /// and the combined (fmt + OTLP) subscriber is installed by `init_otlp()` instead. pub fn init_tracing_subscriber(level: &str, defer: bool) { let env_filter = EnvFilter::try_from_default_env() .or_else(|_| EnvFilter::from_str(level)) .expect("invalid log level"); let fmt_layer = fmt::layer() .json() .with_target(true) .with_thread_ids(false) .with_file(true) .with_line_number(true) .with_span_events(FmtSpan::CLOSE) .flatten_event(true); let registry = tracing_subscriber::registry() .with(env_filter) .with(fmt_layer); if defer { // Caller will invoke init_otlp() which builds the full subscriber // including the OTLP layer, then calls try_init() once. return; } let _ = registry.try_init(); }