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

如何正确为`Box<dyn Future<...>>`添加tracing instrumentation?

解决tracing异步函数父子span绑定问题

针对你遇到的返回Box<dyn Future<...>>函数的tracing span父子关系异常、span耗时不匹配的问题,核心原因是默认的#[tracing::instrument]属性作用在返回boxed future的同步函数上时,span仅在函数调用(创建并返回future)的短暂周期内存在,并未覆盖future实际执行的全过程,同时内部手动管理的span也没正确继承父上下文。

以下是具体的解决方案:

1. 修正返回boxed future函数的span生命周期

直接使用#[tracing::instrument]无法自动将span绑定到boxed future的执行阶段,需要手动将span与future绑定,确保span的生命周期覆盖整个异步任务:

方案一:手动创建并在future中进入span

use tracing::info_span;

fn blocks_matching_polling_intervals(/* 参数 */) -> Box<dyn Future<Output = Result<...>> + Send> {
    // 创建当前函数的span
    let span = info_span!("blocks_matching_polling_intervals");
    Box::pin(async move {
        // 在future执行时进入span,此时span的生命周期会跟随future执行
        let _enter = span.enter();
        // 后续调用会自动继承该span作为父上下文
        block_range_to_ptrs(/* 参数 */).await
    })
}

fn block_range_to_ptrs(/* 参数 */) -> Box<dyn Future<Output = Result<...>> + Send> {
    let span = info_span!("block_range_to_ptrs");
    Box::pin(async move {
        let _enter = span.enter();
        load_block_ptrs_rpc(/* 参数 */).await
    })
}

方案二:用Instrument组合器绑定span(需tracing-futures依赖)

如果想保留#[tracing::instrument]属性,可以借助tracing-futures的Instrument trait,将函数创建的span附加到返回的future上:

use tracing::instrument;
use tracing_futures::Instrument;

#[instrument(skip_all)]
fn blocks_matching_polling_intervals(/* 参数 */) -> Box<dyn Future<Output = Result<...>> + Send> {
    let future = async move {
        block_range_to_ptrs(/* 参数 */).await
    };
    // 将当前函数的span附加到future,确保执行时处于该span上下文
    Box::pin(future.instrument(tracing::Span::current()))
}

#[instrument(skip_all)]
fn block_range_to_ptrs(/* 参数 */) -> Box<dyn Future<Output = Result<...>> + Send> {
    let future = async move {
        load_block_ptrs_rpc(/* 参数 */).await
    };
    Box::pin(future.instrument(tracing::Span::current()))
}

2. 修正load_block_ptrs_rpc的span继承逻辑

如果load_block_ptrs_rpc需要手动管理span,必须显式继承调用方的span作为父span:

async fn load_block_ptrs_rpc(/* 参数 */) -> Result<...> {
    // 获取当前调用方的span作为父span
    let parent_span = tracing::Span::current();
    // 创建子span时指定parent
    let span = tracing::info_span!("load_block_ptrs_rpc", parent = parent_span.id());
    let _enter = span.enter();
    
    // 执行具体业务逻辑
    // ...
}

如果不需要手动管理,直接给load_block_ptrs_rpc加上#[tracing::instrument]属性即可,它会自动继承调用方的span作为父上下文,无需额外代码。

效果验证

完成上述修改后,三个函数的span会形成正确的父子层级:blocks_matching_polling_intervals → block_range_to_ptrs → load_block_ptrs_rpc,且前两个函数的span耗时会包含其内部future执行的完整时间,不再是仅60微秒的函数调用时间。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 01:53:16