1 个不稳定版本

0.0.0 2019年6月27日

#96#日志跟踪

MIT 许可证

2KB

Tracing — Structured, application-level diagnostics

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

网站 | 聊天 | 文档(master分支)

master分支是tracing的预发布和开发版本。请参阅v0.1.x分支以获取发布到crates.io的tracing版本。

概述

tracing是一个用于对Rust程序进行跟踪以收集结构化、基于事件的诊断信息的框架。tracing由Tokio项目维护,但要求使用tokio运行时。

使用方法

在应用程序中

为了记录跟踪事件,可执行文件必须使用与tracing兼容的收集器实现。收集器实现了一种收集跟踪数据的方式,例如将其记录到标准输出。 tracing-subscriberfmt模块提供了一个默认合理的收集器来记录跟踪信息。此外,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_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 fn,首选方法是使用#[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::instrument组合器解决这个问题

use tracing::Instrument;

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

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

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

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

支持的Rust版本

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

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

寻求帮助

首先,看看你的问题的答案是否可以在API文档中找到。如果答案不在那里,Tracing Discord频道中有活跃的社区:Tracing Discord频道。我们很乐意尝试回答你的问题。最后,如果这还不行,请尝试打开一个问题

贡献

🎈 感谢您为改进项目提供帮助!我们非常高兴有你!我们有一个贡献指南,帮助你参与到Tracing项目中。

项目布局

tracing包包含主要的instrumentation API,用于对库和应用程序进行标记以发出跟踪数据。 tracing-core 包包含 core API 基本库,其余的 tracing 都在它之上进行标记。跟踪订阅者作者可能依赖于 tracing-core,这保证了更高的稳定性。

此外,此存储库还包含构建在 tracing 之上的几个兼容性和实用库。其中一些包处于预发布状态,稳定性不如 tracingtracing-core 包。

包含在Tracing中的包有

除了这个仓库,还有一些第三方Crate,它们不由 tokio 项目维护。这些包括

  • tracing-timingtracing 上实现事件间的时间度量指标。它提供了一个订阅者,记录 tracing 事件之间的时间差并生成直方图。
  • tracing-honeycomb 提供了一层,将跨多台机器的跟踪信息报告给 honeycomb.io。由 tracing-distributed 支持。
  • tracing-distributed 提供了一个通用的层实现,将跨多台机器的跟踪信息报告到某个后端。
  • tracing-actix-webactix-web 网络框架提供 tracing 集成。
  • tracing-actixactix Actor 框架提供 tracing 集成。
  • axum-insightsaxum 网络框架提供 tracing 集成和应用洞察导出。
  • tracing-gelf 实现了一个用于导出以 Greylog GELF 格式跟踪的订阅者。
  • tracing-coz 提供了与 Linux 仅有的 coz 因果分析器(coz)的集成。
  • tracing-bunyan-formatter 提供了一个报告事件和跨度在 bunyan 格式中的层实现,并丰富了时间信息。
  • tide-tracing 提供了一个 tide 中间件,用于跟踪所有传入的请求和响应。
  • color-spantrace 提供了一个用于以 color-backtrace 风格渲染跨度跟踪的格式化器。
  • color-eyreeyre::Report 提供了自定义的恐慌和 eyre 报告处理程序,用于捕获带有新错误的跨度跟踪和回溯,并以美观的方式打印它们。
  • spandoc 提供了一个 proc 宏,用于从函数中的文档注释 内部 构造跨度。
  • tracing-wasm 提供了一个 Collector/Subscriber 实现,通过浏览器 console.log用户时间 API (window.performance) 报告事件和跨度。
  • tracing-web 提供了一个层实现,用于将事件以感知级别的日志记录到网络浏览器的 console.* 和跨度事件到 用户时间 API (window.performance)
  • test-log 负责根据具有 env_logger 兼容语法的环境变量初始化测试中的 tracing
  • tracing-unwrap 提供了方便的方法来报告在 ResultOption 类型上的失败 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 提供了一个将事件和 span 格式化为 logfmt 格式的层。
  • tracing-chrome 提供了一个将跟踪数据导出到可以在 chrome://tracing 中查看的层的功能。
  • reqwest-tracing 提供了一个中间件来跟踪 reqwest HTTP 请求。
  • tracing-cloudwatch 提供了一个将事件发送到 AWS CloudWatch Logs 的层。
  • clippy-tracing 提供了一个工具,用于添加、删除和检查 tracing::instrument

(如果你是这个列表中未列出的 tracing 生态系统 crate 的维护者,请告知我们!)

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

外部资源

这是一个关于 Tracing 的博客文章、会议演讲和教程的链接列表。

博客文章

演讲

帮助我们扩展这个列表!如果您编写或讨论过跟踪(Tracing),或知道未列出的资源,请提交一个pull request添加它们。

许可证

本项目采用MIT许可证

贡献

除非您明确声明,否则您提交的任何有意包含在跟踪(Tracing)中的贡献,均应按MIT许可证授权,不附加任何额外条款或条件。

无运行时依赖