postfix_log_parser/components/
cleanup.rs

1//! 清理(cleanup)组件解析器
2
3use crate::components::ComponentParser;
4use crate::error::ParseError;
5use crate::events::cleanup::CleanupEvent;
6use crate::events::ComponentEvent;
7use crate::utils::queue_id::create_queue_id_pattern;
8use lazy_static::lazy_static;
9use regex::Regex;
10
11/// Cleanup组件解析器
12/// 基于896,788个真实生产数据分析,cleanup组件占4.5%的日志
13/// 解析顺序按照真实数据频率优化
14pub struct CleanupParser;
15
16lazy_static! {
17    // 1. Message-ID事件 - 最高频事件,优先解析
18    // 示例: "4C79F1C801AD: message-id=<1833e987d3ecefa9.6d2e4a5d94f9a46d.99a741ed1cc424f9@m01>"
19    static ref MESSAGE_ID_REGEX: Regex = Regex::new(
20        &create_queue_id_pattern(r"^{QUEUE_ID}: message-id=<([^>]+)>$")
21    ).unwrap();
22
23    // 2. 队列文件警告 - 第二高频,系统问题相关
24    // 示例: "mail_queue_enter: create file incoming/635139.92: Permission denied"
25    static ref QUEUE_FILE_WARNING_REGEX: Regex = Regex::new(
26        r"^([^:]+): create file ([^:]+): (.+)$"
27    ).unwrap();
28
29    // 3. 邮件大小信息
30    // 示例: "4C79F1C801AD: size=1024"
31    static ref MESSAGE_SIZE_REGEX: Regex = Regex::new(
32        &create_queue_id_pattern(r"^{QUEUE_ID}: size=(\d+)$")
33    ).unwrap();
34
35    // 4. 邮件头处理
36    // 示例: "4C79F1C801AD: header Subject: Test Message"
37    static ref HEADER_PROCESSING_REGEX: Regex = Regex::new(
38        &create_queue_id_pattern(r"^{QUEUE_ID}: header ([^:]+): (.+)$")
39    ).unwrap();
40
41    // 5. 地址重写
42    // 示例: "4C79F1C801AD: from=<old@example.com> -> <new@example.com>"
43    static ref ADDRESS_REWRITE_REGEX: Regex = Regex::new(
44        &create_queue_id_pattern(r"^{QUEUE_ID}: (from|to)=<([^>]+)> -> <([^>]+)>$")
45    ).unwrap();
46
47    // 6. 邮件内容重写
48    // 示例: "4C79F1C801AD: rewrite: replaced header"
49    static ref MESSAGE_REWRITE_REGEX: Regex = Regex::new(
50        &create_queue_id_pattern(r"^{QUEUE_ID}: rewrite: (.+)$")
51    ).unwrap();
52
53    // 7. 过滤器动作
54    // 示例: "4C79F1C801AD: filter action: ACCEPT"
55    static ref FILTER_ACTION_REGEX: Regex = Regex::new(
56        &create_queue_id_pattern(r"^{QUEUE_ID}: filter ([^:]+): (.+)$")
57    ).unwrap();
58
59    // 8. Milter交互
60    // 示例: "4C79F1C801AD: milter spamassassin: ACCEPT"
61    static ref MILTER_INTERACTION_REGEX: Regex = Regex::new(
62        &create_queue_id_pattern(r"^{QUEUE_ID}: milter ([^:]+): (.+)$")
63    ).unwrap();
64
65    // 9. 邮件拒绝
66    // 示例: "4C79F1C801AD: reject: spam detected"
67    static ref MESSAGE_REJECT_REGEX: Regex = Regex::new(
68        &create_queue_id_pattern(r"^{QUEUE_ID}: reject: (.+)$")
69    ).unwrap();
70
71    // 10. 配置警告
72    // 示例: "warning: configuration error in main.cf"
73    static ref CONFIG_WARNING_REGEX: Regex = Regex::new(
74        r"^warning: (.+)$"
75    ).unwrap();
76
77    // 11. 资源限制
78    // 示例: "warning: disk space low: 95% full"
79    static ref RESOURCE_LIMIT_REGEX: Regex = Regex::new(
80        r"^warning: ([^:]+): (.+)$"
81    ).unwrap();
82
83    // 12. 统计信息
84    // 示例: "statistics: processed=1000 rejected=10"
85    static ref STATISTICS_REGEX: Regex = Regex::new(
86        r"^statistics: processed=(\d+) rejected=(\d+)(?:\s+errors=(\d+))?$"
87    ).unwrap();
88}
89
90impl CleanupParser {
91    pub fn new() -> Self {
92        CleanupParser
93    }
94
95    /// 解析Message-ID事件 - 最高频事件
96    fn parse_message_id(&self, message: &str) -> Option<CleanupEvent> {
97        if let Some(captures) = MESSAGE_ID_REGEX.captures(message) {
98            return Some(CleanupEvent::MessageId {
99                queue_id: captures.get(1)?.as_str().to_string(),
100                message_id: captures.get(2)?.as_str().to_string(),
101            });
102        }
103        None
104    }
105
106    /// 解析队列文件警告
107    fn parse_queue_file_warning(&self, message: &str) -> Option<CleanupEvent> {
108        if let Some(captures) = QUEUE_FILE_WARNING_REGEX.captures(message) {
109            return Some(CleanupEvent::QueueFileWarning {
110                operation: captures.get(1)?.as_str().to_string(),
111                file_path: captures.get(2)?.as_str().to_string(),
112                error_reason: captures.get(3)?.as_str().to_string(),
113            });
114        }
115        None
116    }
117
118    /// 解析邮件大小信息
119    fn parse_message_size(&self, message: &str) -> Option<CleanupEvent> {
120        if let Some(captures) = MESSAGE_SIZE_REGEX.captures(message) {
121            if let Ok(size) = captures.get(2)?.as_str().parse::<u64>() {
122                return Some(CleanupEvent::MessageSize {
123                    queue_id: captures.get(1)?.as_str().to_string(),
124                    size,
125                });
126            }
127        }
128        None
129    }
130
131    /// 解析邮件头处理
132    fn parse_header_processing(&self, message: &str) -> Option<CleanupEvent> {
133        if let Some(captures) = HEADER_PROCESSING_REGEX.captures(message) {
134            return Some(CleanupEvent::HeaderProcessing {
135                queue_id: captures.get(1)?.as_str().to_string(),
136                header_name: captures.get(2)?.as_str().to_string(),
137                header_value: captures.get(3)?.as_str().to_string(),
138                action: "process".to_string(), // 默认动作
139            });
140        }
141        None
142    }
143
144    /// 解析地址重写
145    fn parse_address_rewrite(&self, message: &str) -> Option<CleanupEvent> {
146        if let Some(captures) = ADDRESS_REWRITE_REGEX.captures(message) {
147            return Some(CleanupEvent::AddressRewrite {
148                queue_id: captures.get(1)?.as_str().to_string(),
149                address_type: captures.get(2)?.as_str().to_string(),
150                original_address: captures.get(3)?.as_str().to_string(),
151                rewritten_address: captures.get(4)?.as_str().to_string(),
152            });
153        }
154        None
155    }
156
157    /// 解析邮件内容重写
158    fn parse_message_rewrite(&self, message: &str) -> Option<CleanupEvent> {
159        if let Some(captures) = MESSAGE_REWRITE_REGEX.captures(message) {
160            return Some(CleanupEvent::MessageRewrite {
161                queue_id: captures.get(1)?.as_str().to_string(),
162                rewrite_type: "content".to_string(),
163                original: "".to_string(), // 需要更详细的解析
164                rewritten: captures.get(2)?.as_str().to_string(),
165            });
166        }
167        None
168    }
169
170    /// 解析过滤器动作
171    fn parse_filter_action(&self, message: &str) -> Option<CleanupEvent> {
172        if let Some(captures) = FILTER_ACTION_REGEX.captures(message) {
173            return Some(CleanupEvent::FilterAction {
174                queue_id: captures.get(1)?.as_str().to_string(),
175                filter_name: captures.get(2)?.as_str().to_string(),
176                action: captures.get(3)?.as_str().to_string(),
177                details: None,
178            });
179        }
180        None
181    }
182
183    /// 解析Milter交互
184    fn parse_milter_interaction(&self, message: &str) -> Option<CleanupEvent> {
185        if let Some(captures) = MILTER_INTERACTION_REGEX.captures(message) {
186            return Some(CleanupEvent::MilterInteraction {
187                queue_id: captures.get(1)?.as_str().to_string(),
188                milter_name: captures.get(2)?.as_str().to_string(),
189                command: "interaction".to_string(),
190                response: Some(captures.get(3)?.as_str().to_string()),
191            });
192        }
193        None
194    }
195
196    /// 解析邮件拒绝
197    fn parse_message_reject(&self, message: &str) -> Option<CleanupEvent> {
198        if let Some(captures) = MESSAGE_REJECT_REGEX.captures(message) {
199            return Some(CleanupEvent::MessageReject {
200                queue_id: captures.get(1)?.as_str().to_string(),
201                reason: captures.get(2)?.as_str().to_string(),
202                action: "reject".to_string(),
203            });
204        }
205        None
206    }
207
208    /// 解析配置警告
209    fn parse_config_warning(&self, message: &str) -> Option<CleanupEvent> {
210        if let Some(captures) = CONFIG_WARNING_REGEX.captures(message) {
211            let warning_msg = captures.get(1)?.as_str();
212
213            // 先检查是否是资源限制类警告
214            if warning_msg.contains("disk")
215                || warning_msg.contains("memory")
216                || warning_msg.contains("queue")
217                || warning_msg.contains("limit")
218            {
219                return Some(CleanupEvent::ResourceLimit {
220                    resource_type: "unknown".to_string(),
221                    limit_details: warning_msg.to_string(),
222                    current_value: None,
223                    limit_value: None,
224                });
225            }
226
227            return Some(CleanupEvent::ConfigurationWarning {
228                warning_type: "cleanup_config".to_string(),
229                message: warning_msg.to_string(),
230            });
231        }
232        None
233    }
234
235    /// 解析统计信息
236    fn parse_statistics(&self, message: &str) -> Option<CleanupEvent> {
237        if let Some(captures) = STATISTICS_REGEX.captures(message) {
238            let processed = captures.get(1)?.as_str().parse::<u32>().ok();
239            let rejected = captures.get(2)?.as_str().parse::<u32>().ok();
240            let errors = captures.get(3).and_then(|m| m.as_str().parse::<u32>().ok());
241
242            return Some(CleanupEvent::Statistics {
243                processed,
244                rejected,
245                errors,
246            });
247        }
248        None
249    }
250}
251
252impl ComponentParser for CleanupParser {
253    fn parse(&self, message: &str) -> Result<ComponentEvent, ParseError> {
254        // 按照真实数据频率优化的解析顺序
255        // 1. Message-ID事件 - 最高频,约占90%的cleanup日志
256        if let Some(event) = self.parse_message_id(message) {
257            return Ok(ComponentEvent::Cleanup(event));
258        }
259
260        // 2. 队列文件警告 - 系统问题,重要性高
261        if let Some(event) = self.parse_queue_file_warning(message) {
262            return Ok(ComponentEvent::Cleanup(event));
263        }
264
265        // 3. 邮件大小信息
266        if let Some(event) = self.parse_message_size(message) {
267            return Ok(ComponentEvent::Cleanup(event));
268        }
269
270        // 4. 邮件头处理
271        if let Some(event) = self.parse_header_processing(message) {
272            return Ok(ComponentEvent::Cleanup(event));
273        }
274
275        // 5. 地址重写
276        if let Some(event) = self.parse_address_rewrite(message) {
277            return Ok(ComponentEvent::Cleanup(event));
278        }
279
280        // 6. 邮件内容重写
281        if let Some(event) = self.parse_message_rewrite(message) {
282            return Ok(ComponentEvent::Cleanup(event));
283        }
284
285        // 7. 过滤器动作
286        if let Some(event) = self.parse_filter_action(message) {
287            return Ok(ComponentEvent::Cleanup(event));
288        }
289
290        // 8. Milter交互
291        if let Some(event) = self.parse_milter_interaction(message) {
292            return Ok(ComponentEvent::Cleanup(event));
293        }
294
295        // 9. 邮件拒绝
296        if let Some(event) = self.parse_message_reject(message) {
297            return Ok(ComponentEvent::Cleanup(event));
298        }
299
300        // 10. 配置警告和资源限制
301        if let Some(event) = self.parse_config_warning(message) {
302            return Ok(ComponentEvent::Cleanup(event));
303        }
304
305        // 11. 统计信息
306        if let Some(event) = self.parse_statistics(message) {
307            return Ok(ComponentEvent::Cleanup(event));
308        }
309
310        // 12. 未识别的事件归类为Other
311        Ok(ComponentEvent::Cleanup(CleanupEvent::Other {
312            event_type: "unknown".to_string(),
313            message: message.to_string(),
314            queue_id: None, // 尝试从消息中提取队列ID
315        }))
316    }
317
318    fn component_name(&self) -> &'static str {
319        "cleanup"
320    }
321}
322
323impl Default for CleanupParser {
324    fn default() -> Self {
325        Self::new()
326    }
327}