gitdataai/libs/observability/src/tracing_fmt.rs
ZhenYi b4024aa690 feat(observability): Phase 6 OTLP tracing + Prometheus /metrics endpoint
- Add HTTP OTLP exporter (opentelemetry-otlp 0.31) via SdkTracerProvider +
  BatchSpanProcessor + tracing_opentelemetry layer
- Add Prometheus /metrics handler via metrics-exporter-prometheus 0.13
- Replace slog with tracing throughout: HttpMetrics, TracingSpanMiddleware
- Replace .init() with .try_init() to allow OTLP layer registration after
  init_tracing_subscriber()
- otlp.rs: SpanExporter::builder().with_http().with_endpoint(),
  Resource::builder().with_service_name(), .with_attribute(KeyValue::new(...))
- prometheus_exporter.rs: install_recorder(), prometheus_handler(),
  spawn_http_metrics_poller()
2026-04-21 22:28:15 +08:00

64 lines
1.9 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.
pub fn init_tracing_subscriber(level: &str) {
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);
// try_init only fails if a global is already set — this is safe when
// init_otlp() is also called (it rebuilds the subscriber with OTLP layers).
let _ = registry.try_init();
}