You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用opentelemetry与tracing实现trace_id和span_id的传播?

问题根源与解决方案

核心问题在于:直接调用opentelemetry::Context::current()获取的是全局OpenTelemetry上下文,而非当前tracing Span关联的上下文。tracing-opentelemetry会将OpenTelemetry Context附加到每个tracing Span的扩展数据中,而非替换全局上下文,因此直接取全局上下文会得到未初始化的无效SpanContext。

1. 正确获取有效SpanContext

首先导入tracing-opentelemetry提供的OpenTelemetrySpanExt trait,它为tracing::Span提供了提取关联OpenTelemetry Context的方法:

use tracing_opentelemetry::OpenTelemetrySpanExt;

修改你的bb函数,从当前tracing Span中提取对应上下文:

#[tracing::instrument]
fn bb() {
    // 获取当前活跃的tracing Span
    let current_tracing_span = tracing::Span::current();
    // 提取绑定的OpenTelemetry Context
    let otel_context = current_tracing_span.otel_context();
    // 获取有效的SpanContext
    let span_context = otel_context.span().span_context();
    
    dbg!(span_context);
}

执行后就能拿到正确的trace_id和span_id了。

2. 客户端实现Trace上下文传播(以tonic为例)

要将客户端的Trace上下文传递给服务端,需要遵循W3C Trace Context标准,将SpanContext序列化为HTTP头并附加到请求中:

步骤1:设置全局传播器

在初始化追踪的函数中添加全局传播器配置(确保客户端和服务端使用相同的传播器):

fn init_tracing_subscriber(service_name: &str, endpoint: &str) -> OtelGuard {
    eprintln!("Initializing tracing with endpoint {}", endpoint);
    let resource = Resource::from_schema_url(
        [KeyValue::new(SERVICE_NAME, service_name.to_owned())],
        SCHEMA_URL,
    );

    // 设置W3C Trace Context传播器
    opentelemetry::global::set_text_map_propagator(TraceContextPropagator::new());

    tracing_subscriber::registry()
        .with(tracing_subscriber::filter::EnvFilter::from_default_env())
        .with(tracing_subscriber::fmt::layer().compact())
        .with(OpenTelemetryLayer::new(init_tracer(resource, endpoint)))
        .try_init()
        .unwrap();
    OtelGuard {}
}

步骤2:将上下文注入请求头

在发起tonic请求时,提取当前tracing Span的上下文并注入到请求头:

use opentelemetry::propagation::TextMapPropagator;
use tonic::metadata::{MetadataMap, MetadataMapInjector};

#[tracing::instrument]
async fn call_server() -> Result<(), Box<dyn std::error::Error>> {
    // 建立tonic客户端连接
    let channel = tonic::transport::Channel::from_static("http://your-service:50051")
        .connect().await?;
    let mut client = YourServiceClient::new(channel);

    // 获取当前tracing Span对应的OpenTelemetry上下文
    let otel_ctx = tracing::Span::current().otel_context();

    // 创建请求头载体,注入Trace上下文
    let mut headers = MetadataMap::new();
    opentelemetry::global::get_text_map_propagator(|propagator| {
        propagator.inject_context(&otel_ctx, &mut MetadataMapInjector(&mut headers));
    });

    // 构造带追踪头的请求
    let request = tonic::Request::new(YourRequest { /* 请求数据 */ })
        .into_metadata(headers);

    // 发起请求
    let response = client.your_service_method(request).await?;
    Ok(())
}

3. 服务端实现Trace上下文注入

服务端需要从请求头中提取Trace上下文,创建与客户端关联的子Span:

步骤1:同样设置全局传播器

服务端初始化时必须和客户端使用相同的传播器:

// 服务端初始化函数中添加
opentelemetry::global::set_text_map_propagator(TraceContextPropagator::new());

步骤2:提取上下文并绑定到当前Span

在tonic服务处理函数中,提取请求头的Trace上下文并附加到当前tracing Span:

use tonic::{Request, Response, Status};
use opentelemetry::propagation::TextMapPropagator;
use tracing_opentelemetry::OpenTelemetrySpanExt;

#[tonic::async_trait]
impl YourService for YourServiceImpl {
    #[tracing::instrument(name = "your_service_method", skip(self, request))]
    async fn your_service_method(&self, request: Request<YourRequest>) -> Result<Response<YourResponse>, Status> {
        // 从请求头中提取Trace上下文
        let parent_ctx = opentelemetry::global::get_text_map_propagator(|propagator| {
            propagator.extract(&tonic::metadata::MetadataMapExtractor(request.metadata()))
        });

        // 将提取的父上下文绑定到当前tracing Span
        tracing::Span::current().set_parent(parent_ctx);

        // 业务逻辑处理
        Ok(Response::new(YourResponse { /* 响应数据 */ }))
    }
}

关键注意事项

  • 所有需要参与链路追踪的函数必须添加#[tracing::instrument]宏,确保tracing能创建Span并与OpenTelemetry上下文绑定。
  • 客户端和服务端必须使用相同的传播器(这里使用的W3C TraceContextPropagator是OpenTelemetry官方推荐的标准实现)。
  • 不要直接依赖opentelemetry::Context::current(),始终从当前tracing Span中提取关联的OpenTelemetry上下文。

内容的提问来源于stack exchange,提问作者cdecker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 04:57:03