postfix_log_parser/
master_parser.rs

1use chrono::{DateTime, Datelike, NaiveDateTime, Utc};
2use log::{debug, warn};
3use regex::Regex;
4use std::collections::HashMap;
5
6use crate::components::{master::MasterParser as MasterComponentParser, ComponentParser};
7
8use crate::error::{ParseError, ParseResult};
9use crate::events::base::ComponentEvent;
10use crate::events::base::{PostfixLogEvent, UnknownEvent};
11
12/// 主解析器
13///
14/// 对应Postfix的master进程,负责识别组件并分发到相应的解析器
15pub struct MasterParser {
16    /// 组件解析器注册表
17    component_parsers: HashMap<String, Box<dyn ComponentParser>>,
18    /// 用于解析基础日志格式的正则表达式
19    base_log_regex: Regex,
20}
21
22impl MasterParser {
23    /// 创建新的主解析器实例
24    pub fn new() -> Self {
25        let mut parser = Self {
26            component_parsers: HashMap::new(),
27            base_log_regex: Regex::new(
28                r"^(\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2})\s+(\S+)\s+postfix/([^\[\]]+)\[(\d+)\]:\s+(.+)$",
29            )
30            .expect("基础日志正则表达式编译失败"),
31        };
32
33        // 注册默认组件解析器
34        parser.register_default_parsers();
35        parser
36    }
37
38    /// 注册默认的组件解析器
39    fn register_default_parsers(&mut self) {
40        use crate::components::*;
41
42        self.register_parser("smtpd", Box::new(smtpd::SmtpdParser::new()));
43        self.register_parser("qmgr", Box::new(qmgr::QmgrParser::new()));
44        self.register_parser("smtp", Box::new(smtp::SmtpParser::new()));
45        self.register_parser("cleanup", Box::new(cleanup::CleanupParser::new()));
46        self.register_parser("error", Box::new(error::ErrorParser::new()));
47        self.register_parser("relay", Box::new(relay::RelayParser::new()));
48        self.register_parser("relay/smtp", Box::new(relay::RelayParser::new()));
49        self.register_parser("discard", Box::new(discard::DiscardParser::new()));
50        self.register_parser("bounce", Box::new(bounce::BounceParser::new()));
51        self.register_parser(
52            "postfix-script",
53            Box::new(postfix_script::PostfixScriptParser::new()),
54        );
55        self.register_parser("master", Box::new(MasterComponentParser::new()));
56        self.register_parser("local", Box::new(LocalParser::new()));
57        self.register_parser("postmap", Box::new(PostmapParser::new()));
58        self.register_parser("anvil", Box::new(anvil::AnvilParser::new()));
59        self.register_parser("pickup", Box::new(pickup::PickupParser::new()));
60        self.register_parser(
61            "trivial-rewrite",
62            Box::new(trivial_rewrite::TrivialRewriteParser::new()),
63        );
64        self.register_parser("postlogd", Box::new(postlogd::PostlogdParser::new()));
65        self.register_parser("proxymap", Box::new(proxymap::ProxymapParser::new()));
66        self.register_parser("sendmail", Box::new(sendmail::SendmailParser::new()));
67        self.register_parser("virtual", Box::new(virtual_parser::VirtualParser::new()));
68    }
69
70    /// 注册组件解析器
71    ///
72    /// 允许用户注册自定义组件解析器
73    pub fn register_parser(&mut self, component: &str, parser: Box<dyn ComponentParser>) {
74        debug!("注册组件解析器: {}", component);
75        self.component_parsers.insert(component.to_string(), parser);
76    }
77
78    /// 解析单行日志
79    ///
80    /// 这是主要的解析入口点
81    pub fn parse(&self, log_line: &str) -> ParseResult {
82        debug!("开始解析日志行: {}", log_line);
83
84        // 1. 解析基础格式
85        let base_info = match self.parse_base_format(log_line) {
86            Ok(info) => info,
87            Err(e) => return ParseResult::failure(e),
88        };
89
90        // 2. 查找对应的组件解析器
91        let parser = match self.component_parsers.get(&base_info.component) {
92            Some(parser) => parser,
93            None => {
94                warn!("未找到组件解析器: {}", base_info.component);
95                return self.create_unknown_event(log_line, base_info);
96            }
97        };
98
99        // 3. 使用组件解析器解析具体事件
100        match parser.parse(&base_info.message) {
101            Ok(event) => {
102                let postfix_event = PostfixLogEvent::new(
103                    log_line.to_string(),
104                    base_info.timestamp,
105                    base_info.hostname,
106                    base_info.component,
107                    base_info.process_id,
108                    base_info.log_level,
109                    event,
110                    None, // 队列ID将在具体解析器中提取
111                );
112
113                ParseResult::success(postfix_event, 1.0)
114            }
115            Err(e) => {
116                warn!(
117                    "组件解析失败: {} - {}, 创建Unknown事件",
118                    base_info.component, e
119                );
120                // 当组件解析失败时,创建Unknown事件但保留日志等级信息
121                self.create_unknown_event(log_line, base_info)
122            }
123        }
124    }
125
126    /// 解析日志的基础格式
127    fn parse_base_format(&self, log_line: &str) -> Result<BaseLogInfo, ParseError> {
128        let captures =
129            self.base_log_regex
130                .captures(log_line)
131                .ok_or_else(|| ParseError::InvalidLogFormat {
132                    reason: "不匹配标准Postfix日志格式".to_string(),
133                })?;
134
135        let timestamp_str = captures.get(1).unwrap().as_str();
136        let hostname = captures.get(2).unwrap().as_str().to_string();
137        let component = captures.get(3).unwrap().as_str().to_string();
138        let process_id = captures
139            .get(4)
140            .unwrap()
141            .as_str()
142            .parse::<u32>()
143            .map_err(|_| ParseError::InvalidLogFormat {
144                reason: "无效的进程ID".to_string(),
145            })?;
146        let raw_message = captures.get(5).unwrap().as_str().to_string();
147
148        // 解析时间戳(简单实用的实现,满足当前需求)
149        let timestamp = self.parse_timestamp(timestamp_str)?;
150
151        // 提取日志等级和消息内容
152        let (log_level, message) = self.extract_log_level_and_message(&raw_message);
153
154        Ok(BaseLogInfo {
155            timestamp,
156            hostname,
157            component,
158            process_id,
159            log_level,
160            message,
161            raw_message,
162        })
163    }
164
165    /// 解析时间戳
166    fn parse_timestamp(&self, timestamp_str: &str) -> Result<DateTime<Utc>, ParseError> {
167        // 简单实用的时间戳解析,使用当前年份
168        let current_year = chrono::Utc::now().year();
169        let datetime_str = format!("{} {}", current_year, timestamp_str);
170
171        let naive_dt =
172            NaiveDateTime::parse_from_str(&datetime_str, "%Y %b %d %H:%M:%S").map_err(|_e| {
173                ParseError::InvalidTimestamp {
174                    timestamp: timestamp_str.to_string(),
175                }
176            })?;
177
178        Ok(DateTime::from_naive_utc_and_offset(naive_dt, Utc))
179    }
180
181    /// 提取日志等级和消息内容
182    fn extract_log_level_and_message(
183        &self,
184        raw_message: &str,
185    ) -> (crate::events::base::PostfixLogLevel, String) {
186        // 检查消息是否以日志等级前缀开始
187        if let Some(first_word_end) = raw_message.find(' ') {
188            let first_word = &raw_message[..first_word_end + 1]; // 包含空格或冒号
189            if let Some(level) =
190                crate::events::base::PostfixLogLevel::from_prefix(first_word.trim())
191            {
192                let message = raw_message[first_word_end + 1..].to_string();
193                return (level, message);
194            }
195        }
196
197        // 如果没有明确的等级前缀,检查是否以 "warning:" 等开头
198        if raw_message.starts_with("warning:") {
199            let message = raw_message
200                .strip_prefix("warning:")
201                .unwrap_or(raw_message)
202                .trim()
203                .to_string();
204            return (crate::events::base::PostfixLogLevel::Warning, message);
205        }
206
207        // 默认为Info级别
208        (
209            crate::events::base::PostfixLogLevel::Info,
210            raw_message.to_string(),
211        )
212    }
213
214    /// 创建未知事件
215    fn create_unknown_event(&self, log_line: &str, base_info: BaseLogInfo) -> ParseResult {
216        let unknown_event = UnknownEvent {
217            component: base_info.component.clone(),
218            message: base_info.message.clone(),
219        };
220
221        let component_name = base_info.component.clone();
222        let postfix_event = PostfixLogEvent::new(
223            log_line.to_string(),
224            base_info.timestamp,
225            base_info.hostname,
226            base_info.component,
227            base_info.process_id,
228            base_info.log_level,
229            ComponentEvent::Unknown(unknown_event),
230            None,
231        );
232
233        ParseResult::partial(
234            postfix_event,
235            0.3,
236            vec![format!("未识别的组件: {}", component_name)],
237        )
238    }
239
240    /// 获取已注册的组件列表
241    pub fn registered_components(&self) -> Vec<&String> {
242        self.component_parsers.keys().collect()
243    }
244
245    /// 公共方法:解析基础日志格式(用于测试)
246    pub fn parse_base_info(&self, log_line: &str) -> Result<BaseLogInfo, ParseError> {
247        self.parse_base_format(log_line)
248    }
249}
250
251impl Default for MasterParser {
252    fn default() -> Self {
253        Self::new()
254    }
255}
256
257/// 基础日志信息
258#[derive(Debug, Clone, PartialEq)]
259pub struct BaseLogInfo {
260    pub timestamp: DateTime<Utc>,
261    pub hostname: String,
262    pub component: String,
263    pub process_id: u32,
264    pub log_level: crate::events::base::PostfixLogLevel,
265    pub message: String,
266    pub raw_message: String,
267}