postfix_log_parser/components/
error.rs

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