postfix-log-parser 0.2.0

高性能模块化Postfix日志解析器,经3.2GB生产数据验证,SMTPD事件100%准确率
Documentation
//! # Error 错误处理组件解析器
//!
//! Error 是 Postfix 的错误处理组件,负责:
//! - 处理投递失败和延迟投递错误
//! - 记录 DNS 解析错误和连接失败
//! - 管理系统配置错误和资源限制
//! - 提供详细的错误分类和原因分析
//!
//! ## 重要统计
//!
//! 基于 676 万+条真实生产数据分析,ERROR 组件占 34.0% 的日志量,
//! 是 Postfix 系统中最重要的监控组件之一。
//!
//! ## 支持的事件类型
//!
//! - **投递延迟**: 临时投递失败,包含重试机制
//! - **投递失败**: 永久投递失败,生成退信
//! - **连接错误**: 网络连接问题和超时
//! - **系统错误**: 配置错误、权限问题、资源不足
//! - **其他错误**: 未分类的错误事件
//!
//! ## 示例日志格式
//!
//! ```text
//! # 投递延迟错误
//! queue_id: to=<user@example.com>, relay=smtp.example.com, delay=300, delays=1/2/3/294, dsn=4.4.1, status=deferred (Connection timed out)
//!
//! # 投递失败错误
//! queue_id: to=<user@example.com>, relay=none, delay=86400, delays=1/2/3/86394, dsn=5.1.1, status=bounced (User unknown)
//!
//! # 系统错误
//! warning: configuration parameter 'virtual_alias_maps' is deprecated
//! ```

use crate::components::ComponentParser;
use crate::error::ParseError;
use crate::events::error::{ErrorEvent, ErrorType};
use crate::events::ComponentEvent;
use crate::utils::queue_id::{create_queue_id_pattern, extract_queue_id};
use lazy_static::lazy_static;
use regex::Regex;

/// ERROR组件解析器
///
/// 解析Postfix error组件产生的各种错误日志
/// 按照真实数据频率优化解析顺序
pub struct ErrorParser;

lazy_static! {
    // 1. 投递延迟错误 - 最常见的ERROR类型 (占95%+)
    static ref DELIVERY_DEFERRED_REGEX: Regex = Regex::new(
        &create_queue_id_pattern(r"^{QUEUE_ID}: to=<([^>]+)>, relay=([^,]+), delay=([\d.]+), delays=([\d./]+), dsn=([\d.]+), status=deferred \((.+)\)$")
    ).unwrap();

    // 2. 投递永久失败错误
    static ref DELIVERY_FAILED_REGEX: Regex = Regex::new(
        &create_queue_id_pattern(r"^{QUEUE_ID}: to=<([^>]+)>, relay=([^,]+), delay=([\d.]+), delays=([\d./]+), dsn=([\d.]+), status=bounced \((.+)\)$")
    ).unwrap();

    // 3. 连接错误(不包含完整投递信息)
    static ref CONNECTION_ERROR_REGEX: Regex = Regex::new(
        &create_queue_id_pattern(r"^{QUEUE_ID}: (.+)$")
    ).unwrap();

    // 4. 系统配置错误(无队列ID)
    static ref SYSTEM_ERROR_REGEX: Regex = Regex::new(
        r"^(warning|error|fatal|panic):\s+(.+)$"
    ).unwrap();

    // 5. 无队列ID的错误消息
    static ref NO_QUEUE_ERROR_REGEX: Regex = Regex::new(
        r"^(.+)$"
    ).unwrap();
}

impl ErrorParser {
    pub fn new() -> Self {
        Self
    }

    /// 解析投递延迟错误
    fn parse_delivery_deferred(&self, message: &str) -> Option<ErrorEvent> {
        if let Some(captures) = DELIVERY_DEFERRED_REGEX.captures(message) {
            let queue_id = captures.get(1)?.as_str().to_string();
            let to = captures.get(2)?.as_str().to_string();
            let relay = captures.get(3)?.as_str().to_string();
            let delay = captures.get(4)?.as_str().parse::<f64>().ok()?;
            let delays = captures.get(5)?.as_str().to_string();
            let dsn = captures.get(6)?.as_str().to_string();
            let reason = captures.get(7)?.as_str().to_string();

            let error_type = ErrorType::from_reason(&reason);

            return Some(ErrorEvent::DeliveryDeferred {
                queue_id,
                to,
                relay,
                delay,
                delays,
                dsn,
                status: "deferred".to_string(),
                reason,
                error_type,
            });
        }
        None
    }

    /// 解析投递永久失败错误
    fn parse_delivery_failed(&self, message: &str) -> Option<ErrorEvent> {
        if let Some(captures) = DELIVERY_FAILED_REGEX.captures(message) {
            let queue_id = captures.get(1)?.as_str().to_string();
            let to = captures.get(2)?.as_str().to_string();
            let relay = captures.get(3)?.as_str().to_string();
            let delay = captures.get(4)?.as_str().parse::<f64>().ok()?;
            let delays = captures.get(5)?.as_str().to_string();
            let dsn = captures.get(6)?.as_str().to_string();
            let reason = captures.get(7)?.as_str().to_string();

            let error_type = ErrorType::from_reason(&reason);

            return Some(ErrorEvent::DeliveryFailed {
                queue_id,
                to,
                relay,
                delay,
                delays,
                dsn,
                status: "bounced".to_string(),
                reason,
                error_type,
            });
        }
        None
    }

    /// 解析连接错误
    fn parse_connection_error(&self, message: &str) -> Option<ErrorEvent> {
        // 跳过已经被其他方法处理的消息格式
        if message.contains("status=deferred") || message.contains("status=bounced") {
            return None;
        }

        if let Some(captures) = CONNECTION_ERROR_REGEX.captures(message) {
            let queue_id = captures.get(1)?.as_str().to_string();
            let error_message = captures.get(2)?.as_str().to_string();

            // 解析连接错误的详细信息
            if error_message.contains("connect to") || error_message.contains("lost connection") {
                let error_type = ErrorType::from_reason(&error_message);

                return Some(ErrorEvent::ConnectionError {
                    queue_id,
                    to: "".to_string(), // 连接错误通常不包含收件人信息
                    relay: "".to_string(),
                    delay: 0.0,
                    delays: "".to_string(),
                    dsn: "".to_string(),
                    reason: error_message,
                    error_type,
                });
            }
        }
        None
    }

    /// 解析系统错误
    fn parse_system_error(&self, message: &str) -> Option<ErrorEvent> {
        if let Some(captures) = SYSTEM_ERROR_REGEX.captures(message) {
            let _level = captures.get(1)?.as_str();
            let error_message = captures.get(2)?.as_str().to_string();

            let error_type = ErrorType::from_reason(&error_message);

            return Some(ErrorEvent::SystemError {
                queue_id: None,
                error_type,
                message: error_message,
            });
        }
        None
    }

    /// 解析其他错误类型
    fn parse_other_error(&self, message: &str) -> Option<ErrorEvent> {
        // 提取队列ID(如果存在)
        let queue_id = extract_queue_id(message);

        Some(ErrorEvent::Other {
            queue_id,
            error_type: "unclassified".to_string(),
            message: message.to_string(),
        })
    }
}

impl ComponentParser for ErrorParser {
    fn parse(&self, message: &str) -> Result<ComponentEvent, ParseError> {
        // 按照真实数据频率优化的解析顺序

        // 1. 投递延迟错误 (最高频 - 约95%+)
        if let Some(event) = self.parse_delivery_deferred(message) {
            return Ok(ComponentEvent::Error(event));
        }

        // 2. 投递永久失败错误 (第二高频)
        if let Some(event) = self.parse_delivery_failed(message) {
            return Ok(ComponentEvent::Error(event));
        }

        // 3. 连接错误
        if let Some(event) = self.parse_connection_error(message) {
            return Ok(ComponentEvent::Error(event));
        }

        // 4. 系统错误
        if let Some(event) = self.parse_system_error(message) {
            return Ok(ComponentEvent::Error(event));
        }

        // 5. 其他未分类错误
        if let Some(event) = self.parse_other_error(message) {
            return Ok(ComponentEvent::Error(event));
        }

        // 理论上不应该到达这里,因为parse_other_error总是返回Some
        Err(ParseError::ComponentParseError {
            component: "error".to_string(),
            reason: format!("无法解析消息: {}", message),
        })
    }

    fn component_name(&self) -> &'static str {
        "error"
    }
}

impl Default for ErrorParser {
    fn default() -> Self {
        Self::new()
    }
}