Rust日志限流库tor-log-ratelim的使用,提供高效的日志记录速率控制与管理功能

Rust日志限流库tor-log-ratelim的使用,提供高效的日志记录速率控制与管理功能

高频事件的限速日志记录

我们经常需要告诉用户发生了不希望发生的事件,但这些事件可能非常频繁。在这种情况下,我们不想每小时记录上千次"出现问题!"。相反,我们希望日志看起来更像: "连接到守卫X: 错误(过去一小时发生了1310/2000次问题)"

这个crate是arti的一部分,没有为外部使用进行适配: 它假设你的日志系统是tracing,并且你使用tor_rtcompat作为异步运行时。

设置

在使用这个crate之前,你需要调用install_runtime,否则消息不会被收集。

示例

use tor_log_ratelim::log_ratelim;
# use std::num::ParseIntError;
pub fn parse_u8(source: &str, s: &str) -> u8 {
    let r: Result<u8, ParseIntError> = s.parse();
    log_ratelim(
        // 我们正在执行的活动
        "parsing an integer from {}", source;
        // 用来决定是否成功的Result
        r; 
        // 失败时报告的错误信息(有限速)
        // 经过一段时间后
        // 错误本身总是会被报告
        Err(_) => WARN, "Had to use default";
        // 每次成功时报告的成功信息(无限制)
        Ok(v) => TRACE, "Got {}", v;
    );
    r.unwrap_or(0)
}

上面的例子可能会产生类似这样的WARN输出:

WARN: Parsing an integer from cache: error (Problem occurred 7/10 times in the last minute): Had to use default: invalid digit found in string"
WARN: Parsing an integer from cache: error (Problem occurred 81/92 times in the last 5 minutes): Had to use default: number too large to fit in target type"
WARN: Parsing an integer from cache: now working (Problem occurred 0/106 times in the last 5 minutes)

和这样的TRACE输出:

TRACE: Parsing an integer from cache: Got 7
TRACE: Parsing an integer from cache: Got 14
TRACE: Parsing an integer from the network: Got 2

完整示例代码

use tor_log_ratelim::log_ratelim;
use std::num::ParseIntError;

// 初始化运行时 (必须在调用log_ratelim之前执行)
tor_log_ratelim::install_runtime();

pub fn parse_u8(source: &str, s: &str) -> u8 {
    let r: Result<u8, ParseIntError> = s.parse();
    
    // 使用log_ratelim宏记录日志
    log_ratelim!(
        // 活动描述
        "parsing an integer from {}", source;
        
        // 结果值
        r;
        
        // 错误情况处理
        Err(_) => WARN, "Had to use default";
        
        // 成功情况处理
        Ok(v) => TRACE, "Got {}", v;
    );
    
    // 返回解析结果或默认值0
    r.unwrap_or(0)
}

fn main() {
    // 测试解析函数
    let _ = parse_u8("cache", "123");  // 应该成功
    let _ = parse_u8("cache", "abc");  // 应该失败
    let _ = parse_u8("network", "256"); // 应该失败(超出u8范围)
    let _ = parse_u8("network", "42");  // 应该成功
}

安装

在项目目录中运行以下Cargo命令:

cargo add tor-log-ratelim

或者在Cargo.toml中添加以下行:

tor-log-ratelim = "0.32.0"

许可证

MIT OR Apache-2.0


1 回复

Rust日志限流库tor-log-ratelim使用指南

简介

tor-log-ratelim是一个Rust日志限流库,专门用于控制和管理日志记录的速率。它特别适合需要限制日志输出频率的场景,防止日志过载或重复日志淹没日志系统。

主要特性

  • 精确控制日志记录的速率
  • 防止重复日志淹没日志系统
  • 可配置的时间窗口和最大消息数
  • 与标准日志框架无缝集成

安装

在Cargo.toml中添加依赖:

[dependencies]
tor-log-ratelim = "0.1"
log = "0.4"

基本使用方法

1. 简单限流示例

use log::{info, warn};
use tor_log_ratelim::log_ratelim;

fn main() {
    // 初始化日志系统
    env_logger::init();

    // 每5秒最多记录2条相同内容的警告日志
    for _ in 0..10 {
        log_ratelim!(warn, 5, 2, "This is a rate-limited warning");
    }
    
    // 普通日志不受限流影响
    info!("This is a normal info message");
}

2. 带参数的限流日志

use log::error;
use tor_log-ratelim::log_ratelim;

fn process_data(data: &str) {
    if data.is_empty() {
        // 每分钟最多记录3次空数据错误
        log_ratelim!(error, 60, 3, "Empty data received: {}", data);
    }
    // ...处理数据
}

3. 自定义限流配置

use log::debug;
use tor_log_ratelim::{RateLimiter, LogRateLimit};

fn main() {
    // 创建自定义限流器: 每10秒最多5条
    let limiter = RateLimiter::new(10, 5);
    
    for i in 0..20 {
        // 使用自定义限流器记录调试日志
        limiter.log(
            LogRateLimit::Debug,
            &format!("Processing item {} with custom rate limit", i)
        );
    }
}

高级用法

1. 不同日志级别使用不同限流策略

use tor_log_ratelim::{RateLimiter, LogRateLimit};

fn handle_network_event(event: &str) {
    let error_limiter = RateLimiter::new(60, 5); // 每分钟最多5个错误
    let warn_limiter = RateLimiter::new(10, 3);  // 每10秒最多3个警告
    
    if event == "timeout" {
        warn_limiter.log(LogRateLimit::Warn, "Network timeout occurred");
    } else if event == "disconnect" {
        error_limiter.log(LogRateLimit::Error, "Network disconnected");
    }
}

2. 结构化日志限流

use serde::Serialize;
use tor_log_ratelim::log_ratelim;

#[derive(Serialize)]
struct UserAction {
    user_id: u64,
    action: String,
    timestamp: i64,
}

fn log_user_action(action: UserAction) {
    // 每个用户每分钟最多记录10次相同操作
    log_ratelim!(
        info, 
        60, 
        10, 
        "User action: {}", 
        serde_json::to_string(&action).unwrap()
    );
}

完整示例代码

use log::{error, warn, info};
use tor_log_ratelim::{log_ratelim, RateLimiter, LogRateLimit};
use serde::Serialize;
use std::thread;
use std::time::Duration;

#[derive(Serialize)]
struct ApiRequest {
    endpoint: String,
    status: u16,
    user_id: u64,
}

fn main() {
    // 初始化日志系统
    env_logger::init();
    
    // 示例1: 简单限流日志
    println!("--- 简单限流日志示例 ---");
    for i in 0..10 {
        log_ratelim!(warn, 2, 3, "重复的警告消息 {}", i); // 每2秒最多3条
        thread::sleep(Duration::from_millis(500));
    }
    
    // 示例2: 带错误处理的限流日志
    println!("\n--- 错误处理限流示例 ---");
    let results = vec![Ok("data"), Err("超时"), Err("超时"), Ok("more data"), Err("连接失败")];
    for result in results {
        if let Err(e) = result {
            // 每分钟最多记录2次相同错误
            log_ratelim!(error, 60, 2, "处理请求失败: {}", e);
        }
    }
    
    // 示例3: 自定义限流器
    println!("\n--- 自定义限流器示例 ---");
    let api_limiter = RateLimiter::new(30, 5); // 每30秒最多5条API日志
    for i in 0..15 {
        api_limiter.log(
            LogRateLimit::Info,
            &format!("API调用 #{}, 状态: {}", i, if i % 2 == 0 { "成功" } else { "失败" })
        );
        thread::sleep(Duration::from_millis(200));
    }
    
    // 示例4: 结构化日志限流
    println!("\n--- 结构化日志限流示例 ---");
    let request = ApiRequest {
        endpoint: "/user/profile".to_string(),
        status: 200,
        user_id: 12345,
    };
    for _ in 0..5 {
        log_ratelim!(
            info,
            10, // 10秒窗口
            2,  // 最多2条
            "API请求: {}",
            serde_json::to_string(&request).unwrap()
        );
    }
}

最佳实践

  1. 合理设置限流参数:根据日志的重要性和系统负载能力设置合适的时间窗口和最大消息数

  2. 区分日志级别:通常错误日志比调试日志需要更宽松的限流设置

  3. 关键日志不使用限流:对于必须记录的严重错误,考虑不使用限流或设置非常宽松的限流

  4. 监控限流效果:定期检查哪些日志被限流,这可能是系统问题的信号

注意事项

  • 限流是基于日志消息内容的精确匹配,所以参数变化会被视为不同消息
  • 时间窗口是滑动窗口,不是固定间隔
  • 在多线程环境中,限流是线程安全的

tor-log-ratelim为Rust应用程序提供了简单而强大的日志限流功能,帮助开发者保持日志系统的可用性和可管理性。

回到顶部