From 628481be7cb186a4964528d596e6adf42dc35159 Mon Sep 17 00:00:00 2001 From: houseme Date: Sat, 21 Mar 2026 11:07:35 +0800 Subject: [PATCH] fix(tracing): Fix distributed tracing context linking (#2247) --- crates/obs/src/telemetry/otel.rs | 20 ++++++++++++++------ rustfs/src/server/http.rs | 17 +++++++++++++++-- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/crates/obs/src/telemetry/otel.rs b/crates/obs/src/telemetry/otel.rs index 048e990ac..3d4320356 100644 --- a/crates/obs/src/telemetry/otel.rs +++ b/crates/obs/src/telemetry/otel.rs @@ -41,17 +41,17 @@ use crate::config::OtelConfig; use crate::global::set_observability_metric_enabled; use crate::telemetry::filter::build_env_filter; use crate::telemetry::guard::OtelGuard; -// Import helper functions from local.rs (sibling module) -use crate::TelemetryError; use crate::telemetry::local::spawn_cleanup_task; use crate::telemetry::recorder::Recorder; use crate::telemetry::resource::build_resource; use crate::telemetry::rolling::{RollingAppender, Rotation}; +// Import helper functions from local.rs (sibling module) +use crate::TelemetryError; use metrics::counter; -use opentelemetry::{global, trace::TracerProvider}; +use opentelemetry::{global, propagation::TextMapCompositePropagator, trace::TracerProvider}; use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge; use opentelemetry_otlp::{Compression, Protocol, WithExportConfig, WithHttpConfig}; -use opentelemetry_sdk::propagation::TraceContextPropagator; +use opentelemetry_sdk::propagation::{BaggagePropagator, TraceContextPropagator}; use opentelemetry_sdk::{ logs::SdkLoggerProvider, metrics::{PeriodicReader, SdkMeterProvider}, @@ -326,7 +326,8 @@ pub(super) fn init_observability_http( /// /// Returns `None` when the endpoint is empty or trace export is disabled. /// When enabled, the provider is also registered as the global tracer provider -/// and installs the W3C trace-context propagator. +/// and installs a composite propagator supporting both W3C TraceContext +/// (traceparent header) and W3C Baggage (baggage header) propagation. fn build_tracer_provider( trace_ep: &str, config: &OtelConfig, @@ -358,7 +359,14 @@ fn build_tracer_provider( let provider = builder.build(); global::set_tracer_provider(provider.clone()); - global::set_text_map_propagator(TraceContextPropagator::new()); + + // Configure composite propagator to support multiple trace context formats: + // - W3C TraceContext (traceparent header) - standard format for distributed tracing + // - W3C Baggage (baggage header) - for propagating user-defined key-value pairs + let propagator = + TextMapCompositePropagator::new(vec![Box::new(TraceContextPropagator::new()), Box::new(BaggagePropagator::new())]); + global::set_text_map_propagator(propagator); + Ok(Some(provider)) } diff --git a/rustfs/src/server/http.rs b/rustfs/src/server/http.rs index cb3b62d19..edb0aec15 100644 --- a/rustfs/src/server/http.rs +++ b/rustfs/src/server/http.rs @@ -35,6 +35,7 @@ use hyper_util::{ }; use metrics::{counter, histogram}; use opentelemetry::global; +use opentelemetry::trace::TraceContextExt; use rustfs_common::GlobalReadiness; use rustfs_config::{RUSTFS_TLS_CERT, RUSTFS_TLS_KEY}; use rustfs_ecstore::rpc::{TONIC_RPC_PREFIX, verify_rpc_signature}; @@ -636,7 +637,7 @@ fn process_connection( .layer( TraceLayer::new_for_http() .make_span_with(|request: &HttpRequest<_>| { - let trace_id = request + let request_id = request .headers() .get(http::header::HeaderName::from_static("x-request-id")) .and_then(|v| v.to_str().ok()) @@ -646,6 +647,18 @@ fn process_connection( propagator.extract(&HeaderMapCarrier::new(request.headers())) }); + // Log trace context extraction for debugging distributed tracing + if parent_context.has_active_span() { + let span_ref = parent_context.span(); + debug!( + otel_trace_id = %span_ref.span_context().trace_id(), + otel_parent_span_id = %span_ref.span_context().span_id(), + sampled = span_ref.span_context().is_sampled(), + "Extracted trace context from incoming request headers" + ); + } else { + debug!("No trace context found in request headers, will create root span"); + } // Extract real client IP from trusted proxy middleware if available let client_info = request.extensions().get::(); let real_ip = client_info @@ -653,7 +666,7 @@ fn process_connection( .unwrap_or_else(|| "unknown".to_string()); let span = tracing::info_span!("http-request", - trace_id = %trace_id, + request_id = %request_id, status_code = tracing::field::Empty, method = %request.method(), real_ip = %real_ip,