Rust日志追踪库tracing-gum的使用:高效灵活的日志记录与诊断工具

Rust日志追踪库tracing-gum的使用:高效灵活的日志记录与诊断工具

tracing-gum简介

tracing-gum是一个将tracing::{warn,info,..}mick-jaeger粘合在一起的库,可以零代码修改地在Grafana中实现交叉引用。

使用场景

在Grafana Loki和Tempo中交叉引用span和日志时,需要一个共享的traceIDTraceIdentifier。所有日志都必须标注这样的元信息。

在大多数情况下,CandidateHashjaeger::Span的主要标识符,因此也是traceID的来源。对于它不是主要标识符的情况,会添加一个名为traceID的辅助标签(已作为便利措施存在)。

决策原因

为了避免在每个包含candidate_hash的tracing行上添加大约2行代码来从中派生TraceIdentifier,并将其作为tracing::*宏的键值部分打印出来,引入了一个proc-macro来抽象化这个过程。

使用方式

在使用日志宏(warn!info!等)时,只需将CandidateHash命名为candidate_hash即可。为了区分,使用前缀gum::

示例代码

use tracing_gum::{gum, info};

fn main() {
    // 初始化tracing subscriber
    tracing_subscriber::fmt().init();
    
    let candidate_hash = "abc123".to_string();
    
    // 使用gum宏记录日志
    gum::info!(
        candidate_hash = candidate_hash,
        "Processing candidate with hash"
    );
    
    // 也可以使用标准tracing语法
    info!(
        candidate_hash = candidate_hash,
        "Another log message with candidate hash"
    );
    
    // 带额外字段的日志
    gum::warn!(
        candidate_hash = candidate_hash,
        duration_ms = 42,
        "Operation took longer than expected"
    );
}

完整示例

use tracing_gum::{gum, info, warn, debug};
use tracing_subscriber::{fmt, EnvFilter};

fn process_data(candidate_hash: String) {
    // 模拟数据处理
    gum::debug!(
        candidate_hash = candidate_hash,
        "Starting data processing"
    );
    
    // 模拟耗时操作
    std::thread::sleep(std::time::Duration::from_millis(50));
    
    // 记录处理结果
    gum::info!(
        candidate_hash = candidate_hash,
        processed_items = 42,
        "Successfully processed data"
    );
}

fn main() {
    // 设置日志过滤器,只显示info及以上级别的日志
    fmt()
        .with_env_filter(EnvFilter::from_default_env()
            .add_directive("info".parse().unwrap()))
        .init();
    
    let candidates = vec![
        "hash1".to_string(),
        "hash2".to_string(),
        "hash3".to_string()
    ];
    
    for hash in candidates {
        process_data(hash);
    }
    
    // 模拟错误情况
    gum::error!(
        candidate_hash = "special_hash".to_string(),
        error_code = 500,
        "Failed to process special candidate"
    );
}

注意事项

  1. 需要在Cargo.toml中添加依赖:
[dependencies]
tracing-gum = "21.0.0"
tracing-subscriber = "0.3"
  1. 该库需要在整个代码库中一致使用才能正常工作

  2. 不追求与tracing::{warn!,..}完全相同的功能,更注重一致性

  3. 所有当前使用的功能在gum中也都支持


1 回复

Rust日志追踪库tracing-gum的使用:高效灵活的日志记录与诊断工具

介绍

tracing-gum是一个基于Rust tracing生态系统的日志记录库,它提供了更简洁、更灵活的API来记录结构化日志。这个库特别适合需要详细诊断信息的应用程序,同时保持了高性能和低开销的特点。

完整示例demo

下面是一个结合了tracing-gum各个功能的完整示例:

// 引入必要的模块
use tracing_gum::{Gum, info, warn, error, debug, trace, instrument};
use std::error::Error;

// 定义一个用户结构体
#[derive(Debug)]
struct User {
    id: u64,
    name: String,
    email: String,
}

// 带有instrument宏的函数
#[instrument]
fn register_user(name: &str, email: &str) -> Result<User, Box<dyn Error>> {
    debug!("开始注册用户");
    
    if name.is_empty() {
        warn!("用户名不能为空");
        return Err("无效用户名".into());
    }
    
    if !email.contains('@') {
        warn!("无效的邮箱格式");
        return Err("无效邮箱".into());
    }
    
    let user = User {
        id: 1,
        name: name.to_string(),
        email: email.to_string(),
    };
    
    info!(
        user_id = user.id,
        user_name = %user.name,
        "用户注册成功",
        details = ?user
    );
    
    Ok(user)
}

// 敏感操作函数
#[instrument(skip(password))]
fn login(username: &str, password: &str) -> bool {
    debug!("尝试登录");
    username == "admin" && password == "123456"
}

fn main() -> Result<(), Box<dyn Error>> {
    // 初始化日志收集器
    Gum::builder()
        .with_level(tracing::Level::DEBUG)
        .with_ansi(true)
        .init();
    
    // 处理用户注册
    match register_user("张三", "zhangsan@example.com") {
        Ok(user) => {
            info!("主函数中收到用户: {:?}", user);
        }
        Err(e) => {
            error!(error = %e, "用户注册失败");
        }
    }
    
    // 模拟登录
    if login("admin", "123456") {
        info!("管理员登录成功");
    } else {
        warn!("登录失败");
    }
    
    // 性能敏感场景
    debug!(
        result = %tracing::field::debug(expensive_operation()), 
        "调试信息"
    );
    
    Ok(())
}

// 耗时的操作
fn expensive_operation() -> String {
    std::thread::sleep(std::time::Duration::from_millis(100));
    "expensive result".to_string()
}

#[cfg(test)]
mod tests {
    use super::*;
    use tracing_gum::Gum;
    use tracing_gum::test_helpers::*;

    #[test]
    fn test_register_user() {
        let _guard = Gum::test_init();
        
        let result = register_user("test", "test@example.com");
        
        assert!(result.is_ok());
        assert_logged!("开始注册用户");
        assert_logged_with_level!(tracing::Level::INFO, "用户注册成功");
    }
}

示例说明

  1. 初始化配置:

    • 使用Gum::builder()自定义日志级别和ANSI彩色输出
  2. 结构化日志:

    • User结构体使用Debug派生,可以方便地记录整个结构体
    • 使用info!宏记录结构化的用户信息
  3. 跨函数追踪:

    • register_user函数使用#[instrument]宏自动记录调用信息
    • login函数使用skip参数跳过敏感字段的记录
  4. 错误处理:

    • 使用error!宏记录错误信息
    • 结合Rust的错误处理机制
  5. 性能优化:

    • expensive_operation使用延迟评估,避免不必要的计算
  6. 测试集成:

    • 测试中使用Gum::test_init()捕获日志
    • 使用assert_logged!宏验证日志输出

这个示例展示了tracing-gum在真实场景中的典型用法,包括初始化、结构化日志、跨函数追踪、错误处理和测试集成等功能。

回到顶部