use opentelemetry::trace::TracerProvider as _; use opentelemetry_appender_tracing::layer::{OpenTelemetryTracingBridge, TracingSpanAttributes}; use tracing_subscriber::EnvFilter; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; use crate::Telemetry; use crate::config::TelemetryConfig; pub fn install(config: &TelemetryConfig, telemetry: &Telemetry) { let base = std::env::var(EnvFilter::DEFAULT_ENV) .ok() .filter(|value| !value.trim().is_empty()) .unwrap_or_else(|| config.log_filter.clone()); // 连接池与查询执行器自身不产出业务日志,慢查询告警也与请求日志重复。 // 默认静默,但配置里显式写了 sqlx 目标时以用户的选择为准,方便临时排查。 let mut spec = base.clone(); if !base.contains("sqlx") { spec.push_str(",sqlx=off"); } // RocketMQ 客户端每 5s 打印一次定时任务、拉取与路由刷新,属于库内部心跳。 // 默认只保留 warn 及以上:路由缺失、连接异常照样可见,周期性噪音不再进日志。 if !base.contains("rocketmq") { spec.push_str(",rocketmq_client_rust=warn,rocketmq_transport=warn"); } let filter = EnvFilter::try_new(&spec).unwrap_or_else(|_| EnvFilter::new(config.log_filter.as_str())); let tracer = telemetry .tracer_provider() .tracer(config.service_name.clone()); let trace_layer = tracing_opentelemetry::layer().with_tracer(tracer); let log_layer = OpenTelemetryTracingBridge::builder(telemetry.logger_provider()) .with_tracing_span_attributes(TracingSpanAttributes::allowlist(["request_id"])) .build(); let format_layer = tracing_subscriber::fmt::layer() .with_target(true) .with_writer(OutputWriter { enabled: config.stdout, }); let result = tracing_subscriber::registry() .with(filter) .with(format_layer) .with(trace_layer) .with(log_layer) .try_init(); if let Err(error) = result { tracing::warn!(%error, "tracing subscriber already installed"); } for warning in telemetry.warnings() { tracing::warn!("{warning}"); } } #[derive(Debug, Clone, Copy)] struct OutputWriter { enabled: bool, } impl<'a> tracing_subscriber::fmt::MakeWriter<'a> for OutputWriter { type Writer = Box; fn make_writer(&'a self) -> Self::Writer { if self.enabled { Box::new(std::io::stdout()) } else { Box::new(std::io::sink()) } } }