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

使用OpenTelemetry埋点递归异步Rust函数触发Panic问题求助

问题解决:递归异步Rust函数的OpenTelemetry链路埋点Panic修复

核心问题分析

触发Panic的根本原因有两个:

  1. 阻塞调用破坏异步调度:递归函数中使用了thread::sleep,这是同步阻塞调用,会占用Tokio runtime的工作线程,导致runtime调度异常,最终引发上下文冲突。
  2. Tracer Provider关闭时机不当:在异步函数main内部调用global::shutdown_tracer_provider()时,该操作会执行同步阻塞的清理逻辑,而此时Tokio runtime仍处于异步上下文运行状态,触发了"不能在异步上下文里drop runtime"的限制。

修复后的完整代码

use futures::future::{BoxFuture, FutureExt};
use opentelemetry::global;
use opentelemetry::trace::Tracer;
use tokio;
use tracing::{info, instrument};
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;

#[derive(Debug)]
struct Foo {
    number: usize,
}

#[instrument]
fn recursive<'a>(foo: &'a mut Foo) -> BoxFuture<'a, usize> {
    async move {
        // 替换同步sleep为Tokio异步sleep,避免阻塞工作线程
        tokio::time::sleep(tokio::time::Duration::from_millis(333)).await;
        
        if foo.number == 0 {
            return 0;
        }
        foo.number -= 1;
        recursive(foo).await
    }
    .boxed()
}

#[tokio::main]
async fn main() {
    // 初始化tracing与OpenTelemetry的集成
    let tracer = opentelemetry_zipkin::new_pipeline()
        .install_simple()
        .unwrap();
    
    let telemetry_layer = tracing_opentelemetry::layer().with_tracer(tracer);
    tracing_subscriber::registry()
        .with(telemetry_layer)
        .with(tracing_subscriber::fmt::layer())
        .init();

    global::set_text_map_propagator(opentelemetry_zipkin::Propagator::new());

    let mut start = Foo { number: 3 };
    let _ = recursive(&mut start).await;

    // 确保异步逻辑完全执行完毕后,再执行Tracer Provider清理
    global::shutdown_tracer_provider();
    dbg!(&start);
}

关键修复点说明

  • 替换阻塞sleep为异步sleep:用tokio::time::sleep(...).await替代thread::sleep,保证异步调度正常运行,不会阻塞Tokio的工作线程。
  • 正确集成tracing与OpenTelemetry:添加tracing_subscriber配置,将OpenTelemetry的tracer作为layer注册到tracing订阅器中,确保#[instrument]生成的链路能被OpenTelemetry捕获。
  • 调整清理时机:等待所有异步递归任务完全执行完毕后,再调用global::shutdown_tracer_provider(),避免异步上下文与同步清理操作的冲突。

额外注意事项

  • 递归异步函数的生命周期标注'a需与引用的生命周期一致,避免悬垂引用。
  • 若需要更精细的链路控制,可以手动创建Span并管理其生命周期,而非仅依赖#[instrument]宏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 03:42:50