postfix_log_parser/components/
error.rs

1//! ERROR组件解析器
2//!
3//! 基于676万+条真实生产数据分析,ERROR组件占34.0%的日志
4//! 主要处理邮件投递错误、DNS错误、连接错误等各种故障情况
5
6use crate::components::ComponentParser;
7use crate::error::ParseError;
8use crate::events::error::{ErrorEvent, ErrorType};
9use crate::events::ComponentEvent;
10use crate::utils::queue_id::{create_queue_id_pattern, extract_queue_id};
11use lazy_static::lazy_static;
12use regex::Regex;
13
14/// ERROR组件解析器
15///
16/// 解析Postfix error组件产生的各种错误日志
17/// 按照真实数据频率优化解析顺序
18pub struct ErrorParser;
19
20lazy_static! {
21    // 1. 投递延迟错误 - 最常见的ERROR类型 (占95%+)
22    // 示例: "3623D2A209AD: to=<gzq1@example.com>, relay=none, delay=14, delays=14/0.03/0/0, dsn=4.4.3, status=deferred (delivery temporarily suspended: Host or domain name not found. Name service error for name=example.com type=MX: Host not found, try again)"
23    static ref DELIVERY_DEFERRED_REGEX: Regex = Regex::new(
24        &create_queue_id_pattern(r"^{QUEUE_ID}: to=<([^>]+)>, relay=([^,]+), delay=([\d.]+), delays=([\d./]+), dsn=([\d.]+), status=deferred \((.+)\)$")
25    ).unwrap();
26
27    // 2. 投递永久失败错误
28    // 示例: "3623D2A209AD: to=<user@example.com>, relay=none, delay=14, delays=14/0.03/0/0, dsn=5.1.1, status=bounced (user unknown)"
29    static ref DELIVERY_FAILED_REGEX: Regex = Regex::new(
30        &create_queue_id_pattern(r"^{QUEUE_ID}: to=<([^>]+)>, relay=([^,]+), delay=([\d.]+), delays=([\d./]+), dsn=([\d.]+), status=bounced \((.+)\)$")
31    ).unwrap();
32
33    // 3. 连接错误(不包含完整投递信息)
34    // 示例: "3623D2A209AD: connect to mx.example.com[1.2.3.4]:25: Connection refused"
35    static ref CONNECTION_ERROR_REGEX: Regex = Regex::new(
36        &create_queue_id_pattern(r"^{QUEUE_ID}: (.+)$")
37    ).unwrap();
38
39    // 4. 系统配置错误(无队列ID)
40    // 示例: "warning: hash:/etc/postfix/transport is unavailable"
41    static ref SYSTEM_ERROR_REGEX: Regex = Regex::new(
42        r"^(warning|error|fatal|panic):\s+(.+)$"
43    ).unwrap();
44
45    // 5. 无队列ID的错误消息
46    // 示例: "fatal: configuration error"
47    static ref NO_QUEUE_ERROR_REGEX: Regex = Regex::new(
48        r"^(.+)$"
49    ).unwrap();
50}
51
52impl ErrorParser {
53    pub fn new() -> Self {
54        Self
55    }
56
57    /// 解析投递延迟错误
58    fn parse_delivery_deferred(&self, message: &str) -> Option<ErrorEvent> {
59        if let Some(captures) = DELIVERY_DEFERRED_REGEX.captures(message) {
60            let queue_id = captures.get(1)?.as_str().to_string();
61            let to = captures.get(2)?.as_str().to_string();
62            let relay = captures.get(3)?.as_str().to_string();
63            let delay = captures.get(4)?.as_str().parse::<f64>().ok()?;
64            let delays = captures.get(5)?.as_str().to_string();
65            let dsn = captures.get(6)?.as_str().to_string();
66            let reason = captures.get(7)?.as_str().to_string();
67
68            let error_type = ErrorType::from_reason(&reason);
69
70            return Some(ErrorEvent::DeliveryDeferred {
71                queue_id,
72                to,
73                relay,
74                delay,
75                delays,
76                dsn,
77                status: "deferred".to_string(),
78                reason,
79                error_type,
80            });
81        }
82        None
83    }
84
85    /// 解析投递永久失败错误
86    fn parse_delivery_failed(&self, message: &str) -> Option<ErrorEvent> {
87        if let Some(captures) = DELIVERY_FAILED_REGEX.captures(message) {
88            let queue_id = captures.get(1)?.as_str().to_string();
89            let to = captures.get(2)?.as_str().to_string();
90            let relay = captures.get(3)?.as_str().to_string();
91            let delay = captures.get(4)?.as_str().parse::<f64>().ok()?;
92            let delays = captures.get(5)?.as_str().to_string();
93            let dsn = captures.get(6)?.as_str().to_string();
94            let reason = captures.get(7)?.as_str().to_string();
95
96            let error_type = ErrorType::from_reason(&reason);
97
98            return Some(ErrorEvent::DeliveryFailed {
99                queue_id,
100                to,
101                relay,
102                delay,
103                delays,
104                dsn,
105                status: "bounced".to_string(),
106                reason,
107                error_type,
108            });
109        }
110        None
111    }
112
113    /// 解析连接错误
114    fn parse_connection_error(&self, message: &str) -> Option<ErrorEvent> {
115        // 跳过已经被其他方法处理的消息格式
116        if message.contains("status=deferred") || message.contains("status=bounced") {
117            return None;
118        }
119
120        if let Some(captures) = CONNECTION_ERROR_REGEX.captures(message) {
121            let queue_id = captures.get(1)?.as_str().to_string();
122            let error_message = captures.get(2)?.as_str().to_string();
123
124            // 解析连接错误的详细信息
125            if error_message.contains("connect to") || error_message.contains("lost connection") {
126                let error_type = ErrorType::from_reason(&error_message);
127
128                return Some(ErrorEvent::ConnectionError {
129                    queue_id,
130                    to: "".to_string(), // 连接错误通常不包含收件人信息
131                    relay: "".to_string(),
132                    delay: 0.0,
133                    delays: "".to_string(),
134                    dsn: "".to_string(),
135                    reason: error_message,
136                    error_type,
137                });
138            }
139        }
140        None
141    }
142
143    /// 解析系统错误
144    fn parse_system_error(&self, message: &str) -> Option<ErrorEvent> {
145        if let Some(captures) = SYSTEM_ERROR_REGEX.captures(message) {
146            let _level = captures.get(1)?.as_str();
147            let error_message = captures.get(2)?.as_str().to_string();
148
149            let error_type = ErrorType::from_reason(&error_message);
150
151            return Some(ErrorEvent::SystemError {
152                queue_id: None,
153                error_type,
154                message: error_message,
155            });
156        }
157        None
158    }
159
160    /// 解析其他错误类型
161    fn parse_other_error(&self, message: &str) -> Option<ErrorEvent> {
162        // 提取队列ID(如果存在)
163        let queue_id = extract_queue_id(message);
164
165        Some(ErrorEvent::Other {
166            queue_id,
167            error_type: "unclassified".to_string(),
168            message: message.to_string(),
169        })
170    }
171}
172
173impl ComponentParser for ErrorParser {
174    fn parse(&self, message: &str) -> Result<ComponentEvent, ParseError> {
175        // 按照真实数据频率优化的解析顺序
176
177        // 1. 投递延迟错误 (最高频 - 约95%+)
178        if let Some(event) = self.parse_delivery_deferred(message) {
179            return Ok(ComponentEvent::Error(event));
180        }
181
182        // 2. 投递永久失败错误 (第二高频)
183        if let Some(event) = self.parse_delivery_failed(message) {
184            return Ok(ComponentEvent::Error(event));
185        }
186
187        // 3. 连接错误
188        if let Some(event) = self.parse_connection_error(message) {
189            return Ok(ComponentEvent::Error(event));
190        }
191
192        // 4. 系统错误
193        if let Some(event) = self.parse_system_error(message) {
194            return Ok(ComponentEvent::Error(event));
195        }
196
197        // 5. 其他未分类错误
198        if let Some(event) = self.parse_other_error(message) {
199            return Ok(ComponentEvent::Error(event));
200        }
201
202        // 理论上不应该到达这里,因为parse_other_error总是返回Some
203        Err(ParseError::ComponentParseError {
204            component: "error".to_string(),
205            reason: format!("无法解析消息: {}", message),
206        })
207    }
208
209    fn component_name(&self) -> &'static str {
210        "error"
211    }
212}
213
214impl Default for ErrorParser {
215    fn default() -> Self {
216        Self::new()
217    }
218}