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

如何在Rust tracing/test-log单元测试中临时提升日志级别

问题

我在为使用tracing crate实现日志、test-log crate启用测试日志的Rust程序编写测试时遇到了需求冲突:

  • 准备步骤操作较多,希望日志级别为info;
  • 执行步骤需要debug级别的日志以获取有效测试输出,但仅在该步骤生效。

我尝试了两种方法,但都有问题:

  1. 使用reload Layer全局修改日志级别:虽然生效,但会影响全局日志配置;
  2. 使用with_default切换订阅者:同步版本在test-log中可行,但异步版本无法正常输出debug日志。

尝试的代码示例:

// 全局修改生效的代码
#[tokio::test]
async fn test_dynamic_logging1() {
    use tracing_subscriber::{ filter, fmt, reload, prelude::* };
    let filter = filter::LevelFilter::INFO;
    let (filter, reload_handle) = reload::Layer::new(filter);
    tracing_subscriber::registry().with(filter).with(fmt::Layer::default()).init();
    info!("This will be logged");
    debug!("This will be ignored");
    reload_handle
        .modify(|filter| {
            *filter = filter::LevelFilter::DEBUG;
        })
        .unwrap();
    debug!("This will be logged");
}

// 同步版本可行但异步失效的test-log代码
#[test_log::test(tokio::test)]
async fn test_dynamic_logging3() {
    debug!("This will be ignored");

    let debug_subscriber = tracing_subscriber::fmt::Subscriber
        ::builder()
        .with_max_level(tracing::Level::DEBUG)
        .with_test_writer()
        .finish();
    // 同步闭包有效
    tracing::subscriber::with_default(debug_subscriber, || {
        debug!("This will be logged");
    });
    // 异步闭包失效
    // tracing::subscriber::with_default(debug_subscriber, || async {
    //     debug!("This will be logged");
    // }).await;
    debug!("This will be ignored");
}
解决方案

要解决异步场景下with_default失效的问题,核心是使用异步版本的订阅者切换API,并正确结合test-log的配置:

1. 使用tracing::subscriber::with_default_async

tracing提供了异步版本的with_default_async函数,专门适配异步闭包场景。需要注意:

  • 该函数要求订阅者实现Clone(异步任务可能被调度到不同线程),创建订阅者时需确保支持克隆;
  • 配合test-log时,订阅者必须使用with_test_writer()适配测试环境输出。

2. 修复后的代码示例

#[test_log::test(tokio::test)]
async fn test_dynamic_logging_fixed() {
    debug!("This will be ignored (global info level)");

    // 创建可克隆的debug级订阅者
    let debug_subscriber = tracing_subscriber::fmt::Subscriber
        ::builder()
        .with_max_level(tracing::Level::DEBUG)
        .with_test_writer()
        .finish()
        .boxed(); // 装箱满足类型要求,也可用Arc包裹

    // 异步闭包内切换订阅者
    tracing::subscriber::with_default_async(debug_subscriber, async {
        debug!("This will be logged (debug level in async scope)");
        // 此处执行需要debug日志的异步操作
        info!("Info log also works here");
    }).await;

    debug!("This will be ignored again (back to global info level)");
}

3. 备选方案:用Span实现局部日志级别控制

如果不想切换整个订阅者,可通过tracing的Span配合局部过滤实现需求:

#[test_log::test(tokio::test)]
async fn test_local_debug_log() {
    info!("Preparation step (info level)");
    debug!("Preparation debug will be ignored");

    // 创建带debug级过滤的Span
    let debug_span = tracing::span!(
        tracing::Level::DEBUG,
        "execution_step"
    );
    let _enter = debug_span.enter();

    // Span范围内的debug日志会被输出
    debug!("Execution step debug log");
    info!("Execution step info log");

    // 退出Span后恢复原级别
    drop(_enter);
    debug!("Post-execution debug will be ignored");
}

关键注意事项

  • 确保tracing和tracing-subscriber使用最新稳定版,with_default_async是较新的API;
  • 全局默认日志级别可通过环境变量TEST_LOG=info设置,匹配准备步骤需求;
  • 异步测试中避免全局修改日志级别,防止测试间的配置污染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 17:33:20