跳转至

日志使用规范

本项目使用 tracing crate 进行日志记录,遵循统一的日志级别规范。

目录


日志级别定义

debug - 详细的调试信息

适用场景: - 请求体/响应体内容 - 中间计算结果 - 循环迭代细节 - 函数参数和返回值

使用场景示例

debug!("发送私信请求体: {:?}", body);
debug!("响应状态码: {}", status);
debug!("响应结果: {}", text);
debug!("计算 MD5: {}", md5_hash);

info - 关键操作成功的通知

适用场景: - 服务启动/停止 - 重要操作成功(如上传完成、私信发送成功) - 阶段性进度通知(如批量操作总结)

使用场景示例

info!("开始批量发送私信,目标用户数量: {}", user_ids.len());
info!("批量上传完成!成功: {}, 失败: {}", success_count, failure_count);
info!("图片上传成功!Media ID: {}", media_id);

warn - 可恢复的异常情况

适用场景: - 重试操作 - 降级方案 - 非致命错误

使用场景示例

warn!("FINALIZE 失败 (状态码 {}), 准备重试 (第 {} 次)", status, retry_count);
warn!("发现重复的 media_id: {}", duplicate_id);
warn!("未找到 User ID,使用默认值");

error - 错误和失败

适用场景: - 请求失败 - 解析错误 - 系统级错误

使用场景示例

error!("发送私信到用户 {} 失败: {}", user_id, error_msg);
error!("HTTP {} 响应内容: {}", status, text);
error!("认证失败:无法提取 CSRF token");


API 操作日志规范

推荐模式

// ✅ 正确:详细信息使用 debug
debug!("发送私信请求体: {:?}", body);
debug!("响应状态码: {}", status);
debug!("响应结果: {}", text);

// ✅ 正确:关键操作使用 info
info!("开始批量发送私信,目标用户数量: {}", user_ids.len());
info!("批量上传完成!成功: {}, 失败: {}", success_count, failure_count);

// ✅ 正确:重试使用 warn
warn!("FINALIZE 失败 (状态码 {}), 准备重试 (第 {} 次)", status, retry_count);

// ✅ 正确:错误使用 error
error!("发送私信到用户 {} 失败: {}", user_id, error_msg);
error!("HTTP {} 响应内容: {}", status, text);

禁止模式

// ❌ 错误:详细调试信息使用 info
info!("发送私信请求体: {:?}", body);  // 应该用 debug

// ❌ 错误:中间状态使用 info
info!("准备发送 {} 个并发请求", tasks.len());  // 应该用 debug

// ❌ 错误:响应内容使用 info
info!("响应结果: {}", text);  // 应该用 debug

日志初始化

推荐的日志格式配置

本项目统一使用以下清晰简洁的日志格式

use tracing_subscriber;

fn main() {
    // 推荐的日志配置 - 清晰简洁的格式
    tracing_subscriber::fmt()
        .with_max_level(tracing::Level::INFO)
        .with_target(false)          // 隐藏冗长的模块路径
        .with_thread_ids(false)      // 隐藏线程 ID
        .with_thread_names(false)    // 隐藏线程名
        .with_file(true)             // 显示文件名
        .with_line_number(true)      // 显示行号
        .compact()                   // 使用紧凑格式
        .init();

    // 应用代码...
}

日志输出效果对比

优化前(默认格式)

[2m2025-10-15T05:52:56.735883Z  INFO x_api::x::upload::client: 图片上传成功: media_id=1978338269223059456

优化后(推荐格式)

2025-10-15T13:52:56.735Z  INFO 图片上传成功: media_id=1978338269223059456 client.rs:316

配置说明

配置项 说明 效果
.with_target(false) 隐藏模块路径 不显示 x_api::x::upload::client
.with_thread_ids(false) 隐藏线程 ID 不显示 ThreadId(1)
.with_thread_names(false) 隐藏线程名 不显示线程名称
.with_file(true) 显示文件名 显示 client.rs
.with_line_number(true) 显示行号 显示 :316
.compact() 紧凑格式 简化时间戳,移除 ANSI 转义序列

生产环境配置

use tracing_subscriber;

fn main() {
    // 生产环境 - INFO 级别日志
    tracing_subscriber::fmt()
        .with_max_level(tracing::Level::INFO)
        .with_target(false)
        .with_thread_ids(false)
        .with_thread_names(false)
        .with_file(true)
        .with_line_number(true)
        .compact()
        .init();

    // 应用代码...
}

开发/调试环境配置

use tracing_subscriber;

fn main() {
    // 开发环境 - DEBUG 级别日志
    tracing_subscriber::fmt()
        .with_max_level(tracing::Level::DEBUG)
        .with_target(false)          // 仍然隐藏冗长的模块路径
        .with_thread_ids(false)
        .with_thread_names(false)
        .with_file(true)
        .with_line_number(true)
        .compact()
        .init();

    // 应用代码...
}

通过环境变量控制

# 设置日志级别为 DEBUG
RUST_LOG=debug cargo run

# 设置日志级别为 INFO
RUST_LOG=info cargo run

# 只显示特定模块的日志
RUST_LOG=x_api::dm=debug cargo run

日志输出示例

图片上传流程

// INIT 阶段
info!("开始上传图片,大小: {} bytes", image_size);
debug!("INIT 请求 URL: {}", init_url);
debug!("INIT 响应: {:?}", init_response);

// APPEND 阶段
debug!("APPEND 请求 media_id: {}", media_id);
debug!("APPEND 响应状态: {}", status);

// FINALIZE 阶段
debug!("FINALIZE 请求 MD5: {}", md5);
if retry_count > 0 {
    warn!("FINALIZE 失败,重试第 {} 次", retry_count);
}
info!("图片上传成功!Media ID: {}", media_id_string);

批量私信发送

info!("开始批量发送私信,目标用户数量: {}", user_ids.len());

for (index, user_id) in user_ids.iter().enumerate() {
    debug!("发送第 {}/{} 条私信到用户 {}", index + 1, total, user_id);
    debug!("请求体: {:?}", request_body);

    match result {
        Ok(_) => {
            info!("私信发送成功!用户: {}", user_id);
        }
        Err(e) => {
            error!("私信发送失败!用户: {}, 错误: {}", user_id, e);
        }
    }
}

info!("批量发送完成!成功: {}, 失败: {}", success_count, failure_count);

禁止的做法

❌ 在日志中输出敏感信息

// ❌ 禁止:完整的 cookies 或 token
error!("认证失败,cookies: {}", cookies);  // 禁止

// ✅ 正确:脱敏处理
error!("认证失败,cookies 长度: {} bytes", cookies.len());

❌ 过度使用 info 级别

// ❌ 禁止:中间状态使用 info
info!("循环第 {} 次", i);
info!("临时变量值: {}", temp);
info!("准备调用函数 foo()");

// ✅ 正确:中间状态使用 debug
debug!("循环第 {} 次", i);
debug!("临时变量值: {}", temp);
debug!("准备调用函数 foo()");

❌ 使用 println! 进行日志输出

// ❌ 禁止:使用 println!
println!("开始上传图片");  // 禁止

// ✅ 正确:使用 tracing 宏
info!("开始上传图片");

性能考虑

避免昂贵的字符串格式化

// ❌ 避免:总是执行格式化
debug!("大数据结构: {:?}", expensive_data_structure);

// ✅ 推荐:条件日志
if tracing::enabled!(tracing::Level::DEBUG) {
    debug!("大数据结构: {:?}", expensive_data_structure);
}

使用结构化日志

use tracing::{info, instrument};

#[instrument]
async fn send_dm(user_id: &str, message: &str) -> Result<DMResult, TwitterError> {
    info!(user_id = %user_id, message_len = message.len(), "发送私信");
    // ...
}

日志格式化建议

统一使用中文

// ✅ 正确:使用中文
info!("批量上传完成!成功: {}, 失败: {}", success_count, failure_count);

// ❌ 避免:混用中英文
info!("Batch upload completed! 成功: {}, 失败: {}", success_count, failure_count);

使用 Emoji 增强可读性(可选)

info!("✅ 图片上传成功!Media ID: {}", media_id);
error!("❌ 发送私信失败!用户: {}", user_id);
warn!("⚠️ FINALIZE 重试第 {} 次", retry_count);
debug!("🔍 响应状态码: {}", status);

相关文档