eyre 的 span trace 如何启用?

解读

在国内 Rust 后端或中间件面试中,“如何拿到报错时的完整调用链” 是高频考点。eyre 作为社区推荐的错误处理库,默认只打印单层错误;若想输出类似 Java 堆栈的 span trace,必须显式启用 spantrace 特性并配合 tracing 生态。面试官想确认两点:

  1. 你知道 eyre 与 anyhow 的差异(可扩展性 + span trace)。
  2. 你能在真实工程里把“编译期开关 + 运行期订阅”两步都踩对,而不是只说“加 feature”。

知识点

  1. eyre 的 feature flag 体系
    • spantrace —— 编译期打开后,eyre::Report 才会在内部记录 tracing::Span 回溯。
    • auto-install —— 自动调用 set_hook,适合 bin crate;lib crate 建议手动 install。
  2. tracing-subscriber 注册
    必须启用 env-filter + fmt 并设置 RUST_LIB_BACKTRACE=1RUST_BACKTRACE=1,否则即使收集到 span trace 也不会渲染。
  3. 与 anyhow 的对比
    anyhow 无 span trace 能力;eyre 通过 EyreHandler trait 允许自定义上下文,因此更适合观测型系统。
  4. 国内生产环境注意
    日志侧通常接 Loki/ELK,需把 span trace 格式化成 JSON;记得在 .cargo/config.toml 里给调试版加 rustflags = ["--cfg=tracing_unstable"],否则 tracing 0.1.x 在旧编译器下会丢字段。

答案

分三步:

  1. Cargo.toml 打开特性

    [dependencies]
    eyre = { version = "0.6", features = ["spantrace", "auto-install"] }
    tracing = "0.1"
    tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
    

    如果是 lib,去掉 auto-install,在初始化函数里手动调用
    eyre::set_hook(Box::new(eyre::DefaultHandler::default_with(spantrace)))

  2. main.rs 启动时注册 subscriber

    use tracing_subscriber::prelude::*;
    
    fn main() -> eyre::Result<()> {
        tracing_subscriber::registry()
            .with(tracing_subscriber::fmt::layer())
            .with(tracing_subscriber::EnvFilter::from_default_env())
            .init();
        // 触发错误观察效果
        run()?;
        Ok(())
    }
    
  3. 运行前加环境变量

    RUST_BACKTRACE=1 cargo run
    

    当错误发生时会同时打印 两层信息

    • Error: 行开始的标准错误链;
    • Span backtrace: 行开始的 span 层级,展示 #[instrument] 的函数调用顺序。

    至此 span trace 启用完成。

拓展思考

  1. 性能权衡
    span trace 记录的是 tracing::Span 引用,无 alloc,但每一次 with_span 都会走原子计数;对热路径(如 100 Mops 的网关)应使用 tracing::Level::ERROR 级别做条件采样,避免开销。
  2. color-eyre 混用
    color-eyre 已内置 spantrace,直接依赖它即可省去手动 set_hook;但注意其默认主题带颜色,国内阿里/腾讯生产日志收集器会转义 ANSI 码,需加 COLOR_BT=never 环境变量关闭。
  3. 错误上报链路
    Sentry 集成场景,可把 eyre::Report 的 span trace 序列化成 JSON,放入 extra 字段;结合 tracing-errorErrorLayer,能把同一 TraceId 下的所有 span 统一上报,方便排查微服务雪崩。