postfix-log-parser 0.2.0

高性能模块化Postfix日志解析器,经3.2GB生产数据验证,SMTPD事件100%准确率
Documentation
//! 基础事件类型和通用数据结构
//!
//! 定义所有Postfix日志事件的基础类型,包括通用字段、日志等级和组件事件枚举

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

use super::{
    anvil::AnvilEvent, bounce::BounceEvent, discard::DiscardEvent, master::MasterEvent,
    pickup::PickupEvent, postfix_script::PostfixScriptEvent, postmap::PostmapEvent,
    postsuper::PostsuperEvent, relay::RelayEvent, trivial_rewrite::TrivialRewriteEvent,
    CleanupEvent, ErrorEvent, LocalEvent, QmgrEvent, SmtpEvent, SmtpdEvent, VirtualEvent,
};

/// 基础事件信息
/// 包含所有Postfix事件的通用字段
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct BaseEvent {
    /// 日志时间戳(UTC时间)
    /// 从日志行开头解析的时间信息,统一转换为UTC时间格式
    pub timestamp: DateTime<Utc>,

    /// 主机名(产生日志的服务器名称)
    /// 从日志行第二个字段提取,用于标识日志来源服务器
    pub hostname: String,

    /// 组件名称(Postfix组件标识)
    /// 如smtpd、qmgr、smtp等,对应Postfix架构中的不同模块
    pub component: String,

    /// 进程ID(组件进程的系统标识符)
    /// 从方括号内提取,用于区分同一组件的不同进程实例
    pub process_id: u32,

    /// 日志等级(消息的重要程度分类)
    /// 基于Postfix源码中的消息等级分类系统
    pub log_level: PostfixLogLevel,

    /// 原始消息内容(冒号后的完整消息文本)
    /// 保留原始格式,用于组件特定的解析和调试
    pub raw_message: String,
}

/// 统一的Postfix日志事件结构
///
/// 这是解析后的日志事件的完整表示,包含所有基础字段和具体的事件数据
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PostfixLogEvent {
    /// 原始日志行(完整的原始日志字符串)
    /// 用途:审计、调试、追溯原始数据
    /// 示例:"Jun 05 15:40:52 m01 postfix/smtpd\[89\]: connect from localhost\[127.0.0.1\]"
    pub raw_log: String,

    /// 日志时间戳(UTC时间)
    /// 从日志行提取的时间信息,自动转换为UTC格式
    /// 格式:ISO 8601标准时间格式
    pub timestamp: DateTime<Utc>,

    /// 主机名(产生日志的服务器主机名)
    /// 从日志行中的第二个字段提取
    /// 示例:"m01", "mail.example.com"
    pub hostname: String,

    /// Postfix组件名称(产生此日志的Postfix组件)
    /// 可能的值:smtpd, qmgr, smtp, cleanup, local, virtual等
    /// 对应Postfix架构中的不同功能模块
    pub component: String,

    /// 进程ID(产生日志的进程标识符)
    /// 从日志行中方括号内提取的数字
    /// 用于区分同一组件的不同进程实例
    pub process_id: u32,

    /// 日志等级(日志的重要性和严重程度)
    /// 基于Postfix源码中的msg_*函数分类
    /// 影响日志的过滤和告警策略
    pub log_level: PostfixLogLevel,

    /// 具体的业务事件数据(解析后的结构化事件信息)
    /// 根据不同组件类型包含不同的事件结构
    /// 这是日志解析的核心价值所在
    pub event: ComponentEvent,

    /// 队列ID(邮件在Postfix队列系统中的唯一标识符)
    /// 用于跟踪单个邮件在系统中的完整生命周期
    /// 格式:通常为十六进制字符串,如"5BC302B027ED"
    pub queue_id: Option<String>,
}

impl PostfixLogEvent {
    /// 创建新的日志事件
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        raw_log: String,
        timestamp: DateTime<Utc>,
        hostname: String,
        component: String,
        process_id: u32,
        log_level: PostfixLogLevel,
        event: ComponentEvent,
        queue_id: Option<String>,
    ) -> Self {
        Self {
            raw_log,
            timestamp,
            hostname,
            component,
            process_id,
            log_level,
            event,
            queue_id,
        }
    }

    /// 获取事件类型描述
    pub fn event_type(&self) -> &'static str {
        self.event.event_type()
    }

    /// 检查是否为错误级别的日志
    pub fn is_error_level(&self) -> bool {
        self.log_level.is_error_level()
    }

    /// 检查是否为警告级别的日志
    pub fn is_warning_level(&self) -> bool {
        self.log_level.is_warning_level()
    }

    /// 检查是否为调试级别的日志
    pub fn is_debug_level(&self) -> bool {
        self.log_level.is_debug_level()
    }

    /// 获取日志等级的严重程度数值
    pub fn severity_level(&self) -> u8 {
        self.log_level.severity_level()
    }
}

/// 组件事件枚举
///
/// 每个Postfix组件都有对应的事件类型
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ComponentEvent {
    /// SMTPD组件事件
    Smtpd(SmtpdEvent),
    /// 队列管理器组件事件
    Qmgr(QmgrEvent),
    /// SMTP投递组件事件
    Smtp(SmtpEvent),
    /// 清理组件事件
    Cleanup(CleanupEvent),
    /// 错误处理组件事件
    Error(ErrorEvent),
    /// 中继组件事件
    Relay(RelayEvent),
    /// 丢弃组件事件
    Discard(DiscardEvent),
    /// 退信处理组件事件
    Bounce(BounceEvent),
    /// Postfix脚本组件事件
    PostfixScript(PostfixScriptEvent),
    /// Master守护进程组件事件
    Master(MasterEvent),
    /// 本地投递组件事件
    Local(LocalEvent),
    /// 邮件拾取组件事件
    Pickup(PickupEvent),
    /// Postmap工具组件事件
    Postmap(PostmapEvent),
    /// Postsuper邮件队列管理工具组件事件
    Postsuper(PostsuperEvent),
    /// Anvil连接计数组件事件
    Anvil(AnvilEvent),
    /// 地址重写和解析组件事件
    TrivialRewrite(TrivialRewriteEvent),
    /// 虚拟投递组件事件
    Virtual(VirtualEvent),
    /// Postlogd组件事件
    Postlogd(crate::events::postlogd::PostlogdEvent),
    /// Proxymap组件事件  
    Proxymap(crate::events::proxymap::ProxymapEvent),
    Sendmail(crate::events::sendmail::SendmailEvent),
    /// 未知或不支持的组件事件
    Unknown(UnknownEvent),
}

impl ComponentEvent {
    /// 获取事件类型描述
    pub fn event_type(&self) -> &'static str {
        match self {
            ComponentEvent::Smtpd(event) => event.event_type(),
            ComponentEvent::Qmgr(event) => event.event_type(),
            ComponentEvent::Smtp(event) => event.event_type(),
            ComponentEvent::Cleanup(event) => event.event_type(),
            ComponentEvent::Error(event) => event.event_type(),
            ComponentEvent::Relay(_) => "relay",
            ComponentEvent::Discard(_) => "discard",
            ComponentEvent::Bounce(event) => event.event_type(),
            ComponentEvent::PostfixScript(_) => "postfix-script",
            ComponentEvent::Master(event) => event.event_type(),
            ComponentEvent::Local(event) => event.event_type(),
            ComponentEvent::Pickup(_) => "pickup",
            ComponentEvent::Postmap(_) => "postmap",
            ComponentEvent::Postsuper(_) => "postsuper",
            ComponentEvent::Anvil(_) => "anvil",
            ComponentEvent::TrivialRewrite(_) => "trivial-rewrite",
            ComponentEvent::Virtual(event) => event.event_type(),
            ComponentEvent::Postlogd(_) => "postlogd",
            ComponentEvent::Proxymap(_) => "proxymap",
            ComponentEvent::Sendmail(_) => "sendmail",
            ComponentEvent::Unknown(_) => "unknown",
        }
    }
}

/// 未知组件事件(无法识别的组件产生的事件)
/// 当遇到未知组件或解析失败时使用此结构
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UnknownEvent {
    /// 组件名称(即使未知也保留原始组件名)
    pub component: String,
    /// 消息内容(去除前缀后的原始消息内容)
    pub message: String,
}

/// Postfix日志等级,对应Postfix源码中的msg_*函数
/// 基于Postfix源码:msg_debug, msg_info, msg_warn, msg_error, msg_fatal, msg_panic
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum PostfixLogLevel {
    /// msg_debug() - 调试信息(默认关闭)
    Debug,
    /// msg_info() - 正常业务操作(最常见,默认等级)
    Info,
    /// msg_warn() - 警告,可恢复问题
    Warning,
    /// msg_error() - 错误,操作失败但系统可继续
    Error,
    /// msg_fatal() - 致命错误,导致进程退出
    Fatal,
    /// msg_panic() - 系统崩溃级别
    Panic,
}

impl PostfixLogLevel {
    /// 获取日志等级的严重程度数值(1-6,数值越高越严重)
    pub fn severity_level(&self) -> u8 {
        match self {
            PostfixLogLevel::Debug => 1,
            PostfixLogLevel::Info => 2,
            PostfixLogLevel::Warning => 3,
            PostfixLogLevel::Error => 4,
            PostfixLogLevel::Fatal => 5,
            PostfixLogLevel::Panic => 6,
        }
    }

    /// 检查是否为错误级别(Error/Fatal/Panic)
    pub fn is_error_level(&self) -> bool {
        matches!(
            self,
            PostfixLogLevel::Error | PostfixLogLevel::Fatal | PostfixLogLevel::Panic
        )
    }

    /// 检查是否为警告级别
    pub fn is_warning_level(&self) -> bool {
        matches!(self, PostfixLogLevel::Warning)
    }

    /// 检查是否为调试级别
    pub fn is_debug_level(&self) -> bool {
        matches!(self, PostfixLogLevel::Debug)
    }

    /// 获取日志等级的字符串表示
    pub fn as_str(&self) -> &'static str {
        match self {
            PostfixLogLevel::Debug => "debug",
            PostfixLogLevel::Info => "info",
            PostfixLogLevel::Warning => "warning",
            PostfixLogLevel::Error => "error",
            PostfixLogLevel::Fatal => "fatal",
            PostfixLogLevel::Panic => "panic",
        }
    }

    /// 从字符串解析日志等级(用于解析日志中的等级前缀)
    pub fn from_prefix(prefix: &str) -> Option<Self> {
        match prefix.to_lowercase().as_str() {
            "debug:" => Some(PostfixLogLevel::Debug),
            "warning:" => Some(PostfixLogLevel::Warning),
            "error:" => Some(PostfixLogLevel::Error),
            "fatal:" => Some(PostfixLogLevel::Fatal),
            "panic:" => Some(PostfixLogLevel::Panic),
            _ => None,
        }
    }
}

impl Default for PostfixLogLevel {
    /// 默认为Info级别(Postfix大部分日志都是Info级别)
    fn default() -> Self {
        PostfixLogLevel::Info
    }
}

impl std::fmt::Display for PostfixLogLevel {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.as_str())
    }
}