postfix-log-parser 0.2.0

高性能模块化Postfix日志解析器,经3.2GB生产数据验证,SMTPD事件100%准确率
Documentation
use postfix_log_parser::events::ComponentEvent;
use postfix_log_parser::MasterParser;
use std::fs;
use std::path::Path;

#[test]
fn test_anvil_real_logs() {
    let master_parser = MasterParser::new();

    // 读取真实的anvil日志文件
    let log_path = Path::new("logs/components/anvil.log");
    assert!(log_path.exists(), "anvil.log文件不存在: {:?}", log_path);

    let log_content = fs::read_to_string(log_path).expect("无法读取anvil.log文件");

    let lines: Vec<&str> = log_content.lines().collect();
    assert!(!lines.is_empty(), "anvil.log文件为空");

    println!("总计{}行anvil日志", lines.len());

    let mut success_count = 0;
    let mut config_warning_count = 0;
    let mut statistics_count = 0;
    let mut failed_lines: Vec<(usize, &str, Option<String>)> = Vec::new();

    for (line_num, line) in lines.iter().enumerate() {
        if line.trim().is_empty() {
            continue;
        }

        let result = master_parser.parse(line);

        if result.is_success() {
            success_count += 1;

            if let Some(event) = &result.event {
                if let ComponentEvent::Anvil(anvil_event) = &event.event {
                    match &anvil_event.event_type {
                        postfix_log_parser::events::anvil::AnvilEventType::ConfigWarning {
                            ..
                        } => {
                            config_warning_count += 1;
                        }
                        postfix_log_parser::events::anvil::AnvilEventType::Statistics {
                            ..
                        } => {
                            statistics_count += 1;
                        }
                    }
                }
            }
        } else {
            let error_msg = result.main_error().map(|e| e.clone());
            failed_lines.push((line_num + 1, *line, error_msg));
        }
    }

    println!(
        "解析成功: {}/{} ({:.1}%)",
        success_count,
        lines.len(),
        success_count as f32 / lines.len() as f32 * 100.0
    );
    println!("配置警告事件: {}", config_warning_count);
    println!("统计信息事件: {}", statistics_count);

    if !failed_lines.is_empty() {
        println!("\n解析失败的行:");
        for (line_num, line, error) in failed_lines.iter().take(5) {
            println!("{}行: {}", line_num, line);
            if let Some(err) = error {
                println!("  错误: {}", err);
            }
        }
        if failed_lines.len() > 5 {
            println!("... 还有{}行失败", failed_lines.len() - 5);
        }
    }

    // 验证解析成功率
    let success_rate = success_count as f32 / lines.len() as f32;
    assert!(
        success_rate > 0.95,
        "ANVIL组件解析成功率太低: {:.1}%",
        success_rate * 100.0
    );

    // 验证事件类型分布
    assert!(config_warning_count > 0, "应该有配置警告事件");
    assert!(statistics_count > 0, "应该有统计信息事件");

    println!("✅ ANVIL组件真实日志测试通过!");
}

#[test]
fn test_anvil_specific_patterns() {
    let master_parser = MasterParser::new();

    // 测试配置警告
    let config_warning = "Apr 10 11:17:43 m01 postfix/anvil[81]: warning: /etc/postfix/main.cf, line 806: overriding earlier entry: smtpd_client_message_rate_limit=0";
    let result = master_parser.parse(config_warning);
    assert!(result.is_success());

    if let Some(event) = &result.event {
        if let ComponentEvent::Anvil(anvil_event) = &event.event {
            match &anvil_event.event_type {
                postfix_log_parser::events::anvil::AnvilEventType::ConfigWarning {
                    file_path,
                    line_number,
                    parameter_name,
                    ..
                } => {
                    assert_eq!(file_path, "/etc/postfix/main.cf");
                    assert_eq!(*line_number, 806);
                    assert_eq!(parameter_name, "smtpd_client_message_rate_limit");
                }
                _ => panic!("期望配置警告事件"),
            }
        }
    }

    // 测试连接速率统计
    let connection_rate = "Apr 10 11:48:30 m01 postfix/anvil[81]: statistics: max connection rate 2/60s for (smtp:192.168.2.127) at Apr 10 11:47:05";
    let result = master_parser.parse(connection_rate);
    assert!(result.is_success());

    if let Some(event) = &result.event {
        if let ComponentEvent::Anvil(anvil_event) = &event.event {
            match &anvil_event.event_type {
                postfix_log_parser::events::anvil::AnvilEventType::Statistics {
                    metric_type,
                    value,
                    rate_window,
                    service_client,
                    ..
                } => {
                    assert!(matches!(
                        metric_type,
                        postfix_log_parser::events::anvil::StatisticType::MaxConnectionRate
                    ));
                    assert_eq!(*value, 2);
                    assert_eq!(rate_window, &Some("/60s".to_string()));
                    assert_eq!(service_client, "smtp:192.168.2.127");
                }
                _ => panic!("期望统计信息事件"),
            }
        }
    }

    // 测试缓存大小统计
    let cache_size =
        "Apr 10 11:48:30 m01 postfix/anvil[81]: statistics: max cache size 1 at Apr 10 11:46:30";
    let result = master_parser.parse(cache_size);
    assert!(result.is_success());

    if let Some(event) = &result.event {
        if let ComponentEvent::Anvil(anvil_event) = &event.event {
            match &anvil_event.event_type {
                postfix_log_parser::events::anvil::AnvilEventType::Statistics {
                    metric_type,
                    value,
                    rate_window,
                    service_client,
                    ..
                } => {
                    assert!(matches!(
                        metric_type,
                        postfix_log_parser::events::anvil::StatisticType::MaxCacheSize
                    ));
                    assert_eq!(*value, 1);
                    assert_eq!(rate_window, &None);
                    assert_eq!(service_client, "system");
                }
                _ => panic!("期望统计信息事件"),
            }
        }
    }

    println!("✅ ANVIL特定模式测试通过!");
}