Skip to content

Rust+Axum高性能web系列(2)-集成日志功能Tracing

哈喽,大家好,我是呼噜噜。在上一篇文章Rust+Axum高性能web系列-从入门到速通教程 我们了解什么事axum和axum为web提供的服务,本文接着给web项目引入日志监控功能!

Tracing 是什么?

我们这里选用Tracing 这个库,它是一个用于检测Rust程序以收集结构化、基于事件的诊断信息的框架。它允许开发者跟踪异步操作的执行流,以更好地理解、监控和调试应用程序。而对于web项目来说,日志监控非常重要

在像 Tokio 这样的异步系统中,由于异步编程模型复杂,解释传统的日志消息通常非常具有挑战性(效率低下,难以掌握整个执行流程)。Tracing扩展了日志记录样式的诊断,允许库和应用程序记录结构化事件,可以按区间span记录日志,并提供有关时间性和因果关系的附加信息,并收集关键的上下文信息,极大地提高了应用程序的可观测性

添加依赖

这里选用tracing这个库的当前最新版本

toml
tracing = { version = "0.1.41", features = ["async-await"] } #日志,追踪诊断信息;同时开启支持异步的特性
tracing-subscriber = { version = "0.3.19", features = ["env-filter", "chrono"] } #日志订阅器,更好地处理日志

这里把axum也升到最新版本,笔者发文此时现在最新版本:0.8.4

简单日志记录

  1. 编写logger.rs
rust
use tracing_subscriber::EnvFilter;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;

pub fn init() {
    // 注册一个全局的日志记录器
    tracing_subscriber::registry() 
        .with(::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"))) // 设置日志级别
        .with(
            tracing_subscriber::fmt::layer()
            .with_file(true) //打印文件名
            .with_line_number(true) //打印行号
            .with_thread_ids(true) //打印线程ID
            .with_thread_names(true) //打印线程名称
            .with_target(false) //不打印target
        )
        .init();
}

如果从环境变量RUST_LOG未获取到日志等级,就设置默认等级为info

  1. main.rs

tracingTRACE、DEBUG、INFO、WARN、ERROR 共5个日志级别,其中TRACE是最详细的级别。

rust
...
mod logger;  
    
#[tokio::main]
async fn main() {
    logger::init();// 初始化!

    let app = Router::new()
        .route("/test", get(handle_test))// 测试接口
        //.layer(middleware::from_fn(logging_middleware));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    tracing::info!("rust_demo listening on {}", listener.local_addr().unwrap());
    axum::serve(listener, app).await.unwrap();
}


...
    
#[debug_handler]
async fn handle_test() -> Json<Value> {

    tracing::trace!("trace");
    tracing::debug!("debug"); 
    tracing::info!("info,test方法打印");
    tracing::warn!("warn");
    tracing::error!("error");

    Json(json!({
        "code": 0,
        "msg": "请求成功",
        "data": "手动构造 返回请求体"
    }))
}

logger::init()初始化日志后,就可以像tracing::debug去使用,非常方便。我们在上面设置的日志等级为:INFO,那么TRACE、DEBUG级别的日志就不会显示

Spans与Events

  1. Spans区间,Span表示具有开始、结束的时间段 及其他元数据,当 程序开始在上下文中执行或执行工作单元,则 输入该上下文的 span,当它停止在该上下文中执行时,它将退出 span。线程当前正在执行的 span 称为该线程的当前 span
rust
use tracing::{span, Level};
let span = span!(Level::TRACE, "my_span");
// `enter` returns a RAII guard which, when dropped, exits the span. this
// indicates that we are in the span for the current lexical scope.
let _enter = span.enter();
// perform some work in the context of `my_span`...
  1. <font style="color:rgb(0, 0, 0);">Events</font>事件,表示某个时刻 。它表示在记录跟踪时发生的事情。Event 可与非结构化日志记录代码发出的日志记录相媲美,但与典型的 log 行不同,Event 可能在 span 的上下文中发生
rust
use tracing::{event, span, Level};

// records an event outside of any span context:
event!(Level::INFO, "something happened");

let span = span!(Level::INFO, "my_span");
let _guard = span.enter();

// records an event within "my_span".
event!(Level::DEBUG, "something happened inside my_span");

小结

其实还有log这个库来实现日志功能,但涉及到异步程序就会比较复杂,不太好用。而tracing则能很好地解决异步日志追踪这个问题,使用起来也非常方便。

参考:

https://docs.rs/tracing/latest/tracing


作者:小牛呼噜噜

本文到这里就结束啦,感谢阅读,关注同名公众号:小牛呼噜噜,防失联+获取更多技术干货