40 个版本

0.1.40 2023 年 10 月 19 日
0.1.38 2023 年 4 月 25 日
0.1.37 2022 年 10 月 6 日
0.1.36 2022 年 7 月 29 日
0.0.0 2017 年 11 月 27 日

调试 中排名 5

Download history 1695222/week @ 2024-04-27 1697077/week @ 2024-05-04 1766740/week @ 2024-05-11 1734208/week @ 2024-05-18 1713091/week @ 2024-05-25 1909187/week @ 2024-06-01 1847057/week @ 2024-06-08 1835323/week @ 2024-06-15 1857067/week @ 2024-06-22 1757022/week @ 2024-06-29 1913337/week @ 2024-07-06 1936786/week @ 2024-07-13 2041848/week @ 2024-07-20 2034504/week @ 2024-07-27 1990626/week @ 2024-08-03 1791407/week @ 2024-08-10

每月下载量 8,175,834
31,120 包中使用(直接使用 7,796 个)

MIT 许可证

440KB
6K SLoC

Tracing — Structured, application-level diagnostics

tracing

Rust 的应用级追踪。

Crates.io Documentation Documentation (master) MIT licensed Build Status Discord chat

文档 | 聊天

概述

tracing 是一个框架,用于为 Rust 程序收集结构化、基于事件的诊断信息。

在异步系统(如 Tokio)中,解释传统的日志消息通常相当具有挑战性。由于单个任务在同一个线程上复用,相关的事件和日志行会混合在一起,使得难以追踪逻辑流程。 tracing 通过允许库和应用记录结构化事件并附带有关 时间性因果性 的额外信息,扩展了日志式诊断的功能——与日志消息不同,tracing 中的跨度具有开始和结束时间,可以被执行流程进入和退出,并可能存在于类似跨度的嵌套树中。此外,tracing 跨度是 结构化 的,能够记录类型数据以及文本消息。

tracing 包提供必要的 API,以便库和应用能够发出追踪数据。

编译器支持:需要 rustc 1.56+

使用方法

(以下示例是从 log 包的 yak-shaving 示例 中借用的,已修改为惯用的 tracing 语法。)

在应用程序中

为了记录跟踪事件,可执行文件必须使用与 tracing 兼容的 Subscriber 实现方式。一个 Subscriber 实现了收集跟踪数据的方式,例如将其记录到标准输出。 tracing_subscriberfmt 模块 提供了合理的默认设置。此外,tracing-subscriber 能够消费由 log-instrumented 库和模块发出的消息。

使用订阅者的最简单方法是调用 set_global_default 函数。

use tracing::{info, Level};
use tracing_subscriber::FmtSubscriber;

fn main() {
    // a builder for `FmtSubscriber`.
    let subscriber = FmtSubscriber::builder()
        // all spans/events with a level higher than TRACE (e.g, debug, info, warn, etc.)
        // will be written to stdout.
        .with_max_level(Level::TRACE)
        // completes the builder.
        .finish();

    tracing::subscriber::set_global_default(subscriber)
        .expect("setting default subscriber failed");

    let number_of_yaks = 3;
    // this creates a new event, outside of any spans.
    info!(number_of_yaks, "preparing to shave yaks");

    let number_shaved = yak_shave::shave_all(number_of_yaks);
    info!(
        all_yaks_shaved = number_shaved == number_of_yaks,
        "yak shaving completed."
    );
}
[dependencies]
tracing = "0.1"
tracing-subscriber = "0.3.0"

此订阅者将作为程序剩余运行期间所有线程的默认设置,类似于在 log crate 中日志记录器的工作方式。

此外,您可以局部覆盖默认订阅者。例如

use tracing::{info, Level};
use tracing_subscriber::FmtSubscriber;

fn main() {
    let subscriber = tracing_subscriber::FmtSubscriber::builder()
        // all spans/events with a level higher than TRACE (e.g, debug, info, warn, etc.)
        // will be written to stdout.
        .with_max_level(Level::TRACE)
        // builds the subscriber.
        .finish();

    tracing::subscriber::with_default(subscriber, || {
        info!("This will be logged to stdout");
    });
    info!("This will _not_ be logged to stdout");
}

这种方法允许跟踪数据在程序的不同上下文中由多个订阅者收集。请注意,覆盖仅适用于当前正在执行的线程;其他线程不会看到 中的更改。

在订阅者上下文之外生成的任何跟踪事件都不会被收集。

一旦设置了一个订阅者,就可以使用 tracing crate 的宏将检测点添加到可执行文件中。

在库中

库应仅依赖于 tracing crate,并使用提供的宏和类型收集可能对下游消费者有用的任何信息。

use std::{error::Error, io};
use tracing::{debug, error, info, span, warn, Level};

// the `#[tracing::instrument]` attribute creates and enters a span
// every time the instrumented function is called. The span is named after the
// the function or method. Paramaters passed to the function are recorded as fields.
#[tracing::instrument]
pub fn shave(yak: usize) -> Result<(), Box<dyn Error + 'static>> {
    // this creates an event at the DEBUG level with two fields:
    // - `excitement`, with the key "excitement" and the value "yay!"
    // - `message`, with the key "message" and the value "hello! I'm gonna shave a yak."
    //
    // unlike other fields, `message`'s shorthand initialization is just the string itself.
    debug!(excitement = "yay!", "hello! I'm gonna shave a yak.");
    if yak == 3 {
        warn!("could not locate yak!");
        // note that this is intended to demonstrate `tracing`'s features, not idiomatic
        // error handling! in a library or application, you should consider returning
        // a dedicated `YakError`. libraries like snafu or thiserror make this easy.
        return Err(io::Error::new(io::ErrorKind::Other, "shaving yak failed!").into());
    } else {
        debug!("yak shaved successfully");
    }
    Ok(())
}

pub fn shave_all(yaks: usize) -> usize {
    // Constructs a new span named "shaving_yaks" at the TRACE level,
    // and a field whose key is "yaks". This is equivalent to writing:
    //
    // let span = span!(Level::TRACE, "shaving_yaks", yaks = yaks);
    //
    // local variables (`yaks`) can be used as field values
    // without an assignment, similar to struct initializers.
    let _span_ = span!(Level::TRACE, "shaving_yaks", yaks).entered();

    info!("shaving yaks");

    let mut yaks_shaved = 0;
    for yak in 1..=yaks {
        let res = shave(yak);
        debug!(yak, shaved = res.is_ok());

        if let Err(ref error) = res {
            // Like spans, events can also use the field initialization shorthand.
            // In this instance, `yak` is the field being initalized.
            error!(yak, error = error.as_ref(), "failed to shave yak!");
        } else {
            yaks_shaved += 1;
        }
        debug!(yaks_shaved);
    }

    yaks_shaved
}
[dependencies]
tracing = "0.1"

注意:库不应调用 set_global_default,因为这将在可执行文件稍后尝试设置默认值时引起冲突。

在异步代码中

如果您正在检测使用 std::future::Future 或 async/await 的代码,请避免使用 Span::enter 方法。以下示例 不会 工作如下

async {
    let _s = span.enter();
    // ...
}
async {
    let _s = tracing::span!(...).entered();
    // ...
}

范围保护器 _s 将不会退出,直到由 async 块生成的未来完成。由于未来和范围可以 多次 进入和退出而不会完成,范围保持进入状态,直到未来存在,而不是仅在它被轮询时进入,导致非常混乱和不正确的结果。有关详细信息,请参阅 关于关闭范围的文档

有两种方式可以检测异步代码。第一种是通过 Future::instrument 组合器

use tracing::Instrument;

let my_future = async {
    // ...
};

my_future
    .instrument(tracing::info_span!("my_future"))
    .await

Future::instrument 将范围附加到未来,确保范围的生存期与未来的生存期相同。

第二种,也是首选的方法,是通过 #[instrument] 属性

use tracing::{info, instrument};
use tokio::{io::AsyncWriteExt, net::TcpStream};
use std::io;

#[instrument]
async fn write(stream: &mut TcpStream) -> io::Result<usize> {
    let result = stream.write(b"hello world\n").await;
    info!("wrote to stream; success={:?}", result.is_ok());
    result
}

在内部,#[instrument] 宏执行与 Future::instrument 相同的显式范围附加操作。

概念

本软件包提供用于创建SpanEvent的宏,分别表示程序执行中的时间段和瞬时事件。

一般来说,应使用时间段来表示离散的工作单元(例如,服务器中某个请求的生命周期)或特定上下文中花费的时间段(例如,与外部系统实例(如数据库)交互所花费的时间)。相比之下,应使用事件来表示时间段内的时间点——具有特定状态码的请求返回、从队列中取出的n个新项目,等等。

Span使用span!宏构建,然后进入以表示某些代码位于该Span的上下文中。

use tracing::{span, Level};

// Construct a new span named "my span".
let mut span = span!(Level::INFO, "my span");
span.in_scope(|| {
    // Any trace events in this closure or code called by it will occur within
    // the span.
});
// Dropping the span will close it, indicating that it has ended.

#[instrument]属性宏可以减少一些模板代码。

use tracing::{instrument};

#[instrument]
pub fn my_function(my_arg: usize) {
    // This event will be recorded inside a span named `my_function` with the
    // field `my_arg`.
    tracing::info!("inside my_function!");
    // ...
}

Event类型表示瞬时发生的事件,本质上是一个无法进入的Span。它们使用event!宏创建。

use tracing::{event, Level};

event!(Level::INFO, "something has happened!");

使用log软件包的用户应注意,tracing公开了一组用于创建Event的宏(trace!debug!info!warn!error!),这些宏可以与log软件包中同名宏相同的语法调用。通常,将项目转换为使用tracing的过程可以从简单的替换开始。

支持的 Rust 版本

跟踪是基于最新的稳定版本构建的。最低支持版本是 1.42。当前跟踪版本不保证在低于最低支持版本的 Rust 版本上构建。

跟踪遵循与 Tokio 项目其余部分相同的编译器支持策略。当前稳定 Rust 编译器和它之前的三个最近的小版本将始终得到支持。例如,如果当前稳定编译器版本是 1.45,则最低支持版本不会提高到 1.42,即三个小版本之前。只要这样做符合此政策,提高最低支持编译器版本不被视为 semver 破坏性更改。

生态系统

除了tracingtracing-core之外,tokio-rs/tracing存储库还包含几个额外的软件包,旨在与tracing生态系统一起使用。这包括一组Subscriber实现,以及辅助和适配器软件包,以帮助编写Subscriber和为应用程序添加仪表。

特别是,以下软件包可能特别有用

  • tracing-futures提供了一个与futures软件包的兼容层,允许将时间段附加到FutureStreamExecutor上。
  • tracing-subscriber提供了与Subscriber一起工作的Subscriber实现和实用工具。这包括一个FmtSubscriber,它将格式化的跟踪数据记录到 stdout,具有与env_logger软件包类似的过滤和格式化。
  • tracing-loglog crate 提供了一个兼容层,使得日志消息可以作为 tracingEvent 在跟踪树中记录。这对于一个使用 tracing 的项目,其依赖项使用了 log 的情况很有用。请注意,如果您正在使用 tracing-subscriberFmtSubscriber,则不需要直接依赖 tracing-log

此外,还有一些由 tokio 项目不维护的第三方 crate。这些包括

如果您是上述未列出的 tracing 生态系统 crate 的维护者,请告诉我们!我们很乐意将您的项目添加到列表中!

注意:一些生态系统 crate 目前尚未发布,正在进行积极开发。它们的稳定性可能不如 tracingtracing-core

支持的 Rust 版本

Tracing 是针对最新的稳定版本构建的。最低支持版本是 1.56。当前 Tracing 版本不一定能在低于最低支持版本的 Rust 版本上构建。

Tracing 遵循 Tokio 项目中其余部分的编译器支持策略。当前稳定 Rust 编译器和之前的三个较小版本将始终得到支持。例如,如果当前稳定编译器版本是 1.69,则最低支持版本不会增加到 1.66,即三个较小版本之前。只要这样做符合此政策,增加最低支持编译器版本不被视为 semver 破坏性更改。

许可证

本项目采用 MIT 许可证

贡献

除非您明确声明,否则您提交给 Tokio 的任何有意包含的贡献,均应按照 MIT 许可,不附加任何额外条款或条件。

依赖项