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
为log
crate 提供了一个兼容层,使得日志消息可以作为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
为actix
actor 框架提供了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 许可,不附加任何额外条款或条件。