Both init_tracing_subscriber() and init_otlp() were calling try_init() on the global tracing dispatcher, causing "global default trace dispatcher has already been set" at runtime when APP_OTEL_ENABLED=true. Fix: simplify the API so init_tracing_subscriber() never installs the subscriber — it either calls try_init() immediately (non-OTLP mode) or returns without installing (OTLP mode, defer=true). init_otlp() now builds the complete subscriber stack (registry + env_filter + fmt_layer + otel_layer) and calls try_init() once. init_tracing_subscriber() signature: (level, defer) → () init_otlp() signature: (endpoint, service_name, _, log_level) → Result The fmt layer is replicated inside init_otlp() for the OTLP path.
70 lines
2.2 KiB
Rust
70 lines
2.2 KiB
Rust
//! 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<String> = 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();
|
|
}
|