Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

日志与追踪:syslog/printf → log + tracing

你将学到: Rust 的双层日志架构(门面 + 后端)、logtracing crate、带 span 的结构化日志,以及如何替代 printf/syslog 调试。

C++ 诊断代码通常使用 printfsyslog 或自定义日志框架。 Rust 有标准化的双层日志架构:门面 crate(logtracing)与后端(实际日志实现)。

log 门面 — Rust 通用日志 API

log crate 提供与 syslog 严重级别对应的宏。库使用 log 宏;可执行文件选择后端:

// Cargo.toml
// [dependencies]
// log = "0.4"
// env_logger = "0.11"    # One of many backends

use log::{info, warn, error, debug, trace};

fn check_sensor(id: u32, temp: f64) {
    trace!("Reading sensor {id}");           // Finest granularity
    debug!("Sensor {id} raw value: {temp}"); // Development-time detail

    if temp > 85.0 {
        warn!("Sensor {id} high temperature: {temp}°C");
    }
    if temp > 95.0 {
        error!("Sensor {id} CRITICAL: {temp}°C — initiating shutdown");
    }
    info!("Sensor {id} check complete");     // Normal operation
}

fn main() {
    // Initialize the backend — typically done once in main()
    env_logger::init();  // Controlled by RUST_LOG env var

    check_sensor(0, 72.5);
    check_sensor(1, 91.0);
}
# Control log level via environment variable
RUST_LOG=debug cargo run          # Show debug and above
RUST_LOG=warn cargo run           # Show only warn and error
RUST_LOG=my_crate=trace cargo run # Per-module filtering
RUST_LOG=my_crate::gpu=debug,warn cargo run  # Mix levels

C++ 对比

C++Rust(log说明
printf("DEBUG: %s\n", msg)debug!("{msg}")编译期检查格式
syslog(LOG_ERR, "...")error!("...")由后端决定输出位置
日志调用外包 #ifdef DEBUG在 max_level 下 trace! / debug! 被编译掉禁用时零成本
自定义 Logger::log(level, msg)log::info!("...") — 所有 crate 同一 API通用门面,可换后端
按文件控制日志详细度RUST_LOG=crate::module=level基于环境变量,无需重编译

tracing crate — 带 span 的结构化日志

tracinglog 基础上扩展了结构化字段span(带时间的作用域)。 对需要跟踪上下文的诊断代码尤其有用:

// Cargo.toml
// [dependencies]
// tracing = "0.1"
// tracing-subscriber = { version = "0.3", features = ["env-filter"] }

use tracing::{info, warn, error, instrument, info_span};

#[instrument(skip(data), fields(gpu_id = gpu_id, data_len = data.len()))]
fn run_gpu_test(gpu_id: u32, data: &[u8]) -> Result<(), String> {
    info!("Starting GPU test");

    let span = info_span!("ecc_check", gpu_id);
    let _guard = span.enter();  // All logs inside this scope include gpu_id

    if data.is_empty() {
        error!(gpu_id, "No test data provided");
        return Err("empty data".to_string());
    }

    // Structured fields — machine-parseable, not just string interpolation
    info!(
        gpu_id,
        temp_celsius = 72.5,
        ecc_errors = 0,
        "ECC check passed"
    );

    Ok(())
}

fn main() {
    // Initialize tracing subscriber
    tracing_subscriber::fmt()
        .with_env_filter("debug")  // Or use RUST_LOG env var
        .with_target(true)          // Show module path
        .with_thread_ids(true)      // Show thread IDs
        .init();

    let _ = run_gpu_test(0, &[1, 2, 3]);
}

使用 tracing-subscriber 时的输出:

#![allow(unused)]
fn main() {
2026-02-15T10:30:00.123Z DEBUG ThreadId(01) run_gpu_test{gpu_id=0 data_len=3}: my_crate: Starting GPU test
2026-02-15T10:30:00.124Z  INFO ThreadId(01) run_gpu_test{gpu_id=0 data_len=3}:ecc_check{gpu_id=0}: my_crate: ECC check passed gpu_id=0 temp_celsius=72.5 ecc_errors=0
}

#[instrument] — 自动创建 span

#[instrument] 属性自动以函数名及其参数创建 span:

#![allow(unused)]
fn main() {
use tracing::instrument;

#[instrument]
fn parse_sel_record(record_id: u16, sensor_type: u8, data: &[u8]) -> Result<(), String> {
    // Every log inside this function automatically includes:
    // record_id, sensor_type, and data (if Debug)
    tracing::debug!("Parsing SEL record");
    Ok(())
}

// skip: exclude large/sensitive args from the span
// fields: add computed fields
#[instrument(skip(raw_buffer), fields(buf_len = raw_buffer.len()))]
fn decode_ipmi_response(raw_buffer: &[u8]) -> Result<Vec<u8>, String> {
    tracing::trace!("Decoding {} bytes", raw_buffer.len());
    Ok(raw_buffer.to_vec())
}
}

logtracing — 选哪个

方面logtracing
复杂度简单 — 5 个宏更丰富 — span、字段、instrument
结构化数据仅字符串插值键值字段:info!(gpu_id = 0, "msg")
计时 / span有 — #[instrument]span.enter()
异步支持基础一等公民 — span 跨 .await 传播
兼容性通用门面log 兼容(有 log 桥接)
适用场景简单应用、库诊断工具、异步代码、可观测性

建议:生产级诊断类项目(带结构化输出的诊断工具)用 tracing。 依赖要少的简单库用 logtracing 含兼容层,使用 log 宏的库在 tracing subscriber 下仍可工作。

后端选项

后端 Crate输出适用场景
env_loggerstderr,彩色开发、简单 CLI 工具
tracing-subscriberstderr,格式化生产环境配合 tracing
syslog系统 syslogLinux 系统服务
tracing-journaldsystemd journalsystemd 管理的服务
tracing-appender轮转日志文件长期运行的守护进程
tracing-opentelemetryOpenTelemetry 收集器分布式追踪