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
12pub struct MasterParser {
16 component_parsers: HashMap<String, Box<dyn ComponentParser>>,
18 base_log_regex: Regex,
20}
21
22impl MasterParser {
23 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 parser.register_default_parsers();
35 parser
36 }
37
38 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 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 pub fn parse(&self, log_line: &str) -> ParseResult {
82 debug!("开始解析日志行: {}", log_line);
83
84 let base_info = match self.parse_base_format(log_line) {
86 Ok(info) => info,
87 Err(e) => return ParseResult::failure(e),
88 };
89
90 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 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, );
112
113 ParseResult::success(postfix_event, 1.0)
114 }
115 Err(e) => {
116 warn!(
117 "组件解析失败: {} - {}, 创建Unknown事件",
118 base_info.component, e
119 );
120 self.create_unknown_event(log_line, base_info)
122 }
123 }
124 }
125
126 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 let timestamp = self.parse_timestamp(timestamp_str)?;
150
151 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 fn parse_timestamp(&self, timestamp_str: &str) -> Result<DateTime<Utc>, ParseError> {
167 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 fn extract_log_level_and_message(
183 &self,
184 raw_message: &str,
185 ) -> (crate::events::base::PostfixLogLevel, String) {
186 if let Some(first_word_end) = raw_message.find(' ') {
188 let first_word = &raw_message[..first_word_end + 1]; 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 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 (
209 crate::events::base::PostfixLogLevel::Info,
210 raw_message.to_string(),
211 )
212 }
213
214 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 pub fn registered_components(&self) -> Vec<&String> {
242 self.component_parsers.keys().collect()
243 }
244
245 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#[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}