postfix-log-parser 0.2.0

高性能模块化Postfix日志解析器,经3.2GB生产数据验证,SMTPD事件100%准确率
Documentation
//! Postlogd内部日志服务模块
//!
//! 处理Postfix postlogd守护进程的事件,负责内部日志处理和配置管理

use serde::{Deserialize, Serialize};
use std::fmt;

/// POSTLOGD events - Postfix internal log server
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PostlogdEvent {
    pub timestamp: String,
    pub process_id: String,
    pub event_type: PostlogdEventType,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum PostlogdEventType {
    /// Configuration override warning
    ConfigOverrideWarning {
        file_path: String,
        line_number: u32,
        parameter: String,
        value: String,
    },
}

impl fmt::Display for PostlogdEvent {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "[{}] postlogd[{}]: {}",
            self.timestamp, self.process_id, self.event_type
        )
    }
}

impl fmt::Display for PostlogdEventType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            PostlogdEventType::ConfigOverrideWarning {
                file_path,
                line_number,
                parameter,
                value,
            } => {
                write!(
                    f,
                    "Config override warning: {}, line {}: overriding earlier entry: {}={}",
                    file_path, line_number, parameter, value
                )
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_config_override_warning_event() {
        let event = PostlogdEvent {
            timestamp: "Apr 08 17:54:30".to_string(),
            process_id: "78".to_string(),
            event_type: PostlogdEventType::ConfigOverrideWarning {
                file_path: "/etc/postfix/main.cf".to_string(),
                line_number: 820,
                parameter: "smtpd_recipient_restrictions".to_string(),
                value: "check_client_access pcre:/etc/postfix/filter_trusted".to_string(),
            },
        };

        assert_eq!(event.process_id, "78");

        let PostlogdEventType::ConfigOverrideWarning {
            file_path,
            line_number,
            parameter,
            ..
        } = &event.event_type;

        assert_eq!(file_path, "/etc/postfix/main.cf");
        assert_eq!(*line_number, 820);
        assert_eq!(parameter, "smtpd_recipient_restrictions");
    }

    #[test]
    fn test_event_serialization() {
        let event = PostlogdEvent {
            timestamp: "Apr 08 17:54:30".to_string(),
            process_id: "78".to_string(),
            event_type: PostlogdEventType::ConfigOverrideWarning {
                file_path: "/etc/postfix/main.cf".to_string(),
                line_number: 806,
                parameter: "smtpd_client_message_rate_limit".to_string(),
                value: "0".to_string(),
            },
        };

        let serialized = serde_json::to_string(&event).unwrap();
        let deserialized: PostlogdEvent = serde_json::from_str(&serialized).unwrap();
        assert_eq!(event, deserialized);
    }

    #[test]
    fn test_event_display() {
        let event = PostlogdEvent {
            timestamp: "Apr 08 17:54:30".to_string(),
            process_id: "78".to_string(),
            event_type: PostlogdEventType::ConfigOverrideWarning {
                file_path: "/etc/postfix/main.cf".to_string(),
                line_number: 826,
                parameter: "smtpd_discard_ehlo_keywords".to_string(),
                value: "silent-discard,dsn,etrn".to_string(),
            },
        };

        let display = format!("{}", event);
        assert!(display.contains("postlogd[78]"));
        assert!(display.contains("Config override warning"));
        assert!(display.contains("main.cf"));
        assert!(display.contains("line 826"));
    }

    #[test]
    fn test_complex_parameter_value() {
        let event = PostlogdEvent {
            timestamp: "Apr 08 17:54:30".to_string(),
            process_id: "78".to_string(),
            event_type: PostlogdEventType::ConfigOverrideWarning {
                file_path: "/etc/postfix/main.cf".to_string(),
                line_number: 820,
                parameter: "smtpd_recipient_restrictions".to_string(),
                value: "check_client_access pcre:/etc/postfix/filter_trusted, permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, pcre:/etc/postfix/filter_default".to_string(),
            },
        };

        let PostlogdEventType::ConfigOverrideWarning { value, .. } = &event.event_type;
        assert!(value.contains("check_client_access"));
        assert!(value.contains("permit_sasl_authenticated"));
        assert!(value.contains("reject_unauth_destination"));
    }

    #[test]
    fn test_different_parameters() {
        let events = vec![
            (
                "smtpd_recipient_restrictions",
                "check_client_access pcre:/etc/postfix/filter_trusted",
            ),
            ("smtpd_client_message_rate_limit", "0"),
            ("smtpd_discard_ehlo_keywords", "silent-discard,dsn,etrn"),
        ];

        for (param, val) in events {
            let event = PostlogdEvent {
                timestamp: "Apr 10 11:17:10".to_string(),
                process_id: "78".to_string(),
                event_type: PostlogdEventType::ConfigOverrideWarning {
                    file_path: "/etc/postfix/main.cf".to_string(),
                    line_number: 806,
                    parameter: param.to_string(),
                    value: val.to_string(),
                },
            };

            let PostlogdEventType::ConfigOverrideWarning {
                parameter, value, ..
            } = &event.event_type;

            assert_eq!(parameter, param);
            assert_eq!(value, val);
        }
    }
}