40 个版本
| 0.1.40 | 2023 年 10 月 19 日 |
|---|---|
| 0.1.38 |
|
| 0.1.37 | 2022 年 10 月 6 日 |
| 0.1.36 | 2022 年 7 月 29 日 |
| 0.0.0 | 2017 年 11 月 27 日 |
在 调试 中排名 5
每月下载量 8,175,834
在 31,120 个 包中使用(直接使用 7,796 个)
440KB
6K SLoC
tracing
Rust 的应用级追踪。
概述
tracing 是一个框架,用于为 Rust 程序收集结构化、基于事件的诊断信息。
在异步系统(如 Tokio)中,解释传统的日志消息通常相当具有挑战性。由于单个任务在同一个线程上复用,相关的事件和日志行会混合在一起,使得难以追踪逻辑流程。 tracing 通过允许库和应用记录结构化事件并附带有关 时间性 和 因果性 的额外信息,扩展了日志式诊断的功能——与日志消息不同,tracing 中的跨度具有开始和结束时间,可以被执行流程进入和退出,并可能存在于类似跨度的嵌套树中。此外,tracing 跨度是 结构化 的,能够记录类型数据以及文本消息。
tracing 包提供必要的 API,以便库和应用能够发出追踪数据。
编译器支持:需要 rustc 1.56+
使用方法
(以下示例是从 log 包的 yak-shaving 示例 中借用的,已修改为惯用的 tracing 语法。)
在应用程序中
为了记录跟踪事件,可执行文件必须使用与 tracing 兼容的 Subscriber 实现方式。一个 Subscriber 实现了收集跟踪数据的方式,例如将其记录到标准输出。 tracing_subscriber 的 fmt 模块 提供了合理的默认设置。此外,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 相同的显式范围附加操作。
概念
本软件包提供用于创建Span和Event的宏,分别表示程序执行中的时间段和瞬时事件。
一般来说,应使用时间段来表示离散的工作单元(例如,服务器中某个请求的生命周期)或特定上下文中花费的时间段(例如,与外部系统实例(如数据库)交互所花费的时间)。相比之下,应使用事件来表示时间段内的时间点——具有特定状态码的请求返回、从队列中取出的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 破坏性更改。
生态系统
相关软件包
除了tracing和tracing-core之外,tokio-rs/tracing存储库还包含几个额外的软件包,旨在与tracing生态系统一起使用。这包括一组Subscriber实现,以及辅助和适配器软件包,以帮助编写Subscriber和为应用程序添加仪表。
特别是,以下软件包可能特别有用
tracing-futures提供了一个与futures软件包的兼容层,允许将时间段附加到Future、Stream和Executor上。tracing-subscriber提供了与Subscriber一起工作的Subscriber实现和实用工具。这包括一个FmtSubscriber,它将格式化的跟踪数据记录到 stdout,具有与env_logger软件包类似的过滤和格式化。tracing-log为logcrate 提供了一个兼容层,使得日志消息可以作为tracing的Event在跟踪树中记录。这对于一个使用tracing的项目,其依赖项使用了log的情况很有用。请注意,如果您正在使用tracing-subscriber的FmtSubscriber,则不需要直接依赖tracing-log。
此外,还有一些由 tokio 项目不维护的第三方 crate。这些包括
tracing-timing在tracing的基础上实现事件间的计时度量。它提供了一个订阅者,用于记录tracing事件之间的时间间隔并生成直方图。tracing-opentelemetry提供了一个订阅者,用于将跟踪发送到兼容 OpenTelemetry 的分布式跟踪系统。tracing-honeycomb提供了一个层,用于将跨多台机器的跟踪报告给 honeycomb.io。由tracing-distributed支持。tracing-distributed为将跨多台机器的跟踪报告到某个后端提供了一般实现。tracing-actix为actixactor 框架提供了tracing集成。axum-insights为axum网络框架提供了tracing集成和应用洞察导出。tracing-gelf实现了一个订阅者,用于以 Greylog GELF 格式导出跟踪。tracing-coz为与 coz 因果分析器(仅限 Linux)集成。test-log根据与env_logger兼容的语法的环境变量初始化测试中的tracing。tracing-unwrap提供了方便的方法来报告Result或Option类型上的失败 unwraps 到Subscriber。diesel-tracing为diesel数据库连接提供了集成。tracing-tracy为在受监控的应用程序中收集 Tracy 配置文件提供了一种方法。tracing-elastic-apm为向 Elastic APM 报告跟踪提供了一层。tracing-etw为发出 Windows ETW 事件提供了一层。tracing-fluent-assertions为验证tracing跨度的行为提供了一种流畅断言风格的测试框架。sentry-tracing为向 Sentry 报告事件和跟踪提供了一层。tracing-loki为将日志发送到 Grafana Loki 提供了一层。tracing-logfmt提供了一层,可以将事件和跨度格式化为 logfmt 格式。
如果您是上述未列出的 tracing 生态系统 crate 的维护者,请告诉我们!我们很乐意将您的项目添加到列表中!
注意:一些生态系统 crate 目前尚未发布,正在进行积极开发。它们的稳定性可能不如 tracing 和 tracing-core。
支持的 Rust 版本
Tracing 是针对最新的稳定版本构建的。最低支持版本是 1.56。当前 Tracing 版本不一定能在低于最低支持版本的 Rust 版本上构建。
Tracing 遵循 Tokio 项目中其余部分的编译器支持策略。当前稳定 Rust 编译器和之前的三个较小版本将始终得到支持。例如,如果当前稳定编译器版本是 1.69,则最低支持版本不会增加到 1.66,即三个较小版本之前。只要这样做符合此政策,增加最低支持编译器版本不被视为 semver 破坏性更改。
许可证
本项目采用 MIT 许可证。
贡献
除非您明确声明,否则您提交给 Tokio 的任何有意包含的贡献,均应按照 MIT 许可,不附加任何额外条款或条件。