1个不稳定版本
0.0.0 | 2019年6月27日 |
---|
#92 in #logging-tracing
2KB
网站 | 聊天 | 文档(master分支)
master分支是tracing
的预发布、开发版本。请参阅v0.1.x分支以获取发布到crates.io的tracing
版本。
概述
tracing
是一个用于收集结构化、基于事件的诊断信息的框架。虽然tracing
由Tokio项目维护,但使用时并不需要tokio
运行时。
使用方法
在应用程序中
为了记录跟踪事件,可执行文件必须使用与tracing
兼容的收集器实现。收集器实现了一种收集跟踪数据的方式,例如将其记录到标准输出。例如,tracing-subscriber
的fmt
模块提供了一个具有合理默认值的收集器,用于记录跟踪信息。此外,tracing-subscriber
能够消费由log
工具库和模块发出的消息。
要使用tracing-subscriber
,请将以下内容添加到您的Cargo.toml
[dependencies]
tracing = "0.1"
tracing-subscriber = "0.3"
然后创建并安装一个收集器,例如使用init()
use tracing::info;
use tracing_subscriber;
fn main() {
// install global collector configured based on RUST_LOG env var.
tracing_subscriber::fmt::init();
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."
);
}
使用init()
会调用set_global_default()
,因此该收集器将被用作程序剩余运行时间内所有线程的默认收集器,类似于log
包中的日志记录器。
为了获得更多控制,可以分阶段构建收集器而不全局设置,而是用来局部覆盖默认收集器。例如
use tracing::{info, Level};
use tracing_subscriber;
fn main() {
let collector = tracing_subscriber::fmt()
// filter spans/events with level TRACE or higher.
.with_max_level(Level::TRACE)
// build but do not install the subscriber.
.finish();
tracing::collect::with_default(collector, || {
info!("This will be logged to stdout");
});
info!("This will _not_ be logged to stdout");
}
在收集器上下文之外生成的任何跟踪事件都不会被收集。
这种方法允许在不同上下文中由多个收集器收集跟踪数据。请注意,覆盖操作仅适用于当前正在执行的线程;其他线程不会看到from_with_default的变化。
一旦设置了一个收集器,就可以使用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 function or method. Parameters 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);
let _enter = span.enter();
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 initialized.
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()
的方法安装收集器,因为这会在可执行文件尝试稍后设置默认值时导致冲突。
在异步代码中
要跟踪async
函数,首选方法是使用#[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
}
对于使用std::future::Future
或带有async
/await
的代码块的一般情况,需要特殊处理,因为以下示例不会正常工作
async {
let _s = span.enter();
// ...
}
范围守护程序_s
将不会退出,直到由async
代码块生成的future完成。由于future和范围可以多次进入和退出而无需它们完成,范围将保持进入状态,直到future存在,而不是只在它被轮询时进入,这会导致非常混乱和不正确的输出。有关更多详细信息,请参阅关闭范围的文档。
可以使用Future::instrument
组合器解决这个问题
use tracing::Instrument;
let my_future = async {
// ...
};
my_future
.instrument(tracing::info_span!("my_future"))
.await
Future::instrument
将范围附加到future,确保范围的生命周期与future相同。
在底层,#[instrument]
宏执行与Future::instrument
相同的显式范围附加操作。
支持的Rust版本
跟踪构建在最新的稳定版本上。最低支持的版本是1.63。当前跟踪版本不保证在低于最低支持版本的Rust版本上构建。
跟踪遵循与Tokio项目其他部分相同的编译器支持策略。当前稳定Rust编译器和其之前的三个最近的小版本将始终得到支持。例如,如果当前稳定编译器版本是1.69,最低支持版本将不会超过1.66,即三个小版本之前。只要这样做符合此政策,增加最低支持编译器版本不被视为semver破坏性更改。
获取帮助
首先,查看您的问题是否可以在API文档中找到答案。如果答案不在那里,Tracing Discord频道(点击访问)中有一个活跃的社区。我们很乐意尝试回答您的问题。最后,如果上述方法都不奏效,请尝试通过提交问题的方式提出您的问题。
贡献
🎈 感谢您的帮助,共同改进项目!我们非常高兴有您加入!我们有一个贡献指南,帮助您参与到Tracing项目中。
项目布局
tracing
包包含主要的 instrumentation API,用于为库和应用程序提供跟踪数据。 tracing-core
包包含 core API 原语,其余的 tracing
都是在其基础上进行跟踪的。跟踪订阅者作者可以依赖于 tracing-core
,这保证了更高的稳定性。
此外,这个存储库还包含了一些基于 tracing
构建的兼容性和实用库。其中一些包处于预发布状态,不如 tracing
和 tracing-core
稳定。
Tracing 包含的包有
-
tracing-futures
:用于跟踪futures
的工具。 (crates.io|文档) -
tracing-macros
:用于发出跟踪事件的实验性宏(不稳定)。 -
tracing-attributes
:用于自动跟踪函数的过程宏属性。 (crates.io|文档) -
tracing-log
:与log
包的兼容性(不稳定)。 -
tracing-serde
:与serde
的兼容层,用于序列化跟踪数据(不稳定)。 -
tracing-subscriber
:收集器实现,以及实现和组合Collector
的工具。 (crates.io|文档) -
tracing-tower
:与tower
生态系统的兼容性(不稳定)。 -
tracing-appender
:用于输出跟踪数据的工具,包括文件追加和非阻塞写入器。 (crates.io|文档) -
tracing-error
:提供SpanTrace
类型,用于使用跟踪跨度跟踪错误。 -
tracing-flame
;提供基于跟踪跨度进入/退出事件的火焰图生成层。 -
tracing-journald
:为记录事件到Linuxjournald
服务提供一个层,保留结构化数据。
相关包
除了这个存储库,还有一些由 tokio
项目不维护的第三方包。这些包括
tracing-timing
在tracing
的基础上实现了事件间的时间度量。它提供了一个订阅者,记录一对tracing
事件之间经过的时间并生成直方图。tracing-honeycomb
提供了一个层,将跨多台机器的跟踪信息报告到 honeycomb.io。由tracing-distributed
支持。tracing-distributed
提供了一个通用的层实现,该层将跨多台机器的跟踪信息报告到某些后端。tracing-actix-web
为actix-web
网络框架提供tracing
集成。tracing-actix
为actix
实例框架提供tracing
集成。axum-insights
为axum
网络框架提供tracing
集成和应用程序洞察导出。tracing-gelf
实现了一个用于导出跟踪信息的 Greylog GELF 格式订阅者。tracing-coz
提供与 coz 因果分析器(仅适用于Linux)的集成。tracing-bunyan-formatter
提供了一个层实现,以 bunyan 格式报告事件和跨度,并增加了时间信息。tide-tracing
为 tide 中间件提供了一种方法,用于跟踪所有传入请求和响应。color-spantrace
提供了一个格式化程序,用于以color-backtrace
风格渲染跨度跟踪。color-eyre
为eyre::Report
提供自定义的恐慌和 eyre 报告处理器,以捕获跨度跟踪和回溯以及用新错误以漂亮的格式打印。spandoc
提供了一个过程宏,用于从函数内部的文档注释构建跨度。tracing-wasm
提供了一个Collector
/Subscriber
实现来报告事件和跨度,通过浏览器console.log
和 用户时间 API (window.performance
)。tracing-web
提供了一个层实现,用于将事件以不同级别记录到网络浏览器的console.*
,并将跨度事件记录到 用户时间 API (window.performance
)。test-log
通过与env_logger
兼容的语法,负责初始化测试环境下的tracing
。tracing-unwrap
提供了便利的方法,用于将Result
或Option
类型的失败 unwraps 报告给Collector
。diesel-tracing
为diesel
数据库连接提供集成。tracing-tracy
为在已配置的应用程序中收集 Tracy 配置文件提供了一种方法。tracing-elastic-apm
为将跟踪信息报告给 Elastic APM 提供了一层。tracing-etw
为在 Windows 系统上发出 ETW 事件提供了一层。sentry-tracing
为将事件和跟踪信息报告给 Sentry 提供了一层。tracing-forest
提供了一个订阅者,通过在写入过程中将同一跨度(span)的日志分组在一起来保留上下文一致性。tracing-loki
为将日志发送到 Grafana Loki 提供了一层。tracing-logfmt
为将事件和跨度格式化为 logfmt 格式提供了一层。tracing-chrome
为导出可在chrome://tracing
中查看的跟踪数据提供了一层。reqwest-tracing
为跟踪reqwest
HTTP 请求提供了一个中间件。tracing-cloudwatch
为将事件发送到 AWS CloudWatch Logs 提供了一层。clippy-tracing
提供了一个工具,用于添加、删除和检查tracing::instrument
。
(如果您维护的 tracing
生态系统 crate 不在此列表中,请告知我们!)
注意: 其中一些生态系统 crate 目前尚未发布,正在进行积极开发。它们可能比 tracing
和 tracing-core
稳定性低。
外部资源
这是一份链接到关于跟踪的博客文章、会议演讲和教程的列表。
博客文章
- 在 Tokio 博客上关于跟踪的诊断,2019 年 8 月
- 在 Rust 应用程序中使用生产级日志记录,2020 年 11 月
- 使用
tracing
和tracing-subscriber
在 Rust 中进行自定义日志记录,第一部分 和 第二部分,2021 年 10 月 - 配置 Axum 项目,2023 年 8 月
演讲
- 湾区 Rust Meetup 讲座和问答,2019 年 3 月
- RustConf 2019 讲座 和 讲义,2019 年 8 月
- 我们在 RustyDays 上还能观察吗? 和 讲义,2020 年 8 月
- 带着仪器的螃蟹!,2021 年 9 月
帮助我们扩展这个列表!如果你写过关于 Tracing 的内容,或者知道尚未列出的资源,请创建一个 pull request 添加它们。
许可
本项目采用 MIT 许可协议。
贡献
除非你明确声明,否则你提交给 Tracing 的任何有意包含的贡献,都应按照 MIT 许可协议进行许可,不附加任何额外条款或条件。