postfix_log_parser/components/
error.rs1use 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
43pub struct ErrorParser;
48
49lazy_static! {
50 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 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 static ref CONNECTION_ERROR_REGEX: Regex = Regex::new(
62 &create_queue_id_pattern(r"^{QUEUE_ID}: (.+)$")
63 ).unwrap();
64
65 static ref SYSTEM_ERROR_REGEX: Regex = Regex::new(
67 r"^(warning|error|fatal|panic):\s+(.+)$"
68 ).unwrap();
69
70 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 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 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 fn parse_connection_error(&self, message: &str) -> Option<ErrorEvent> {
139 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 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(), 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 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 fn parse_other_error(&self, message: &str) -> Option<ErrorEvent> {
186 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 if let Some(event) = self.parse_delivery_deferred(message) {
203 return Ok(ComponentEvent::Error(event));
204 }
205
206 if let Some(event) = self.parse_delivery_failed(message) {
208 return Ok(ComponentEvent::Error(event));
209 }
210
211 if let Some(event) = self.parse_connection_error(message) {
213 return Ok(ComponentEvent::Error(event));
214 }
215
216 if let Some(event) = self.parse_system_error(message) {
218 return Ok(ComponentEvent::Error(event));
219 }
220
221 if let Some(event) = self.parse_other_error(message) {
223 return Ok(ComponentEvent::Error(event));
224 }
225
226 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}