postfix_log_parser/components/
error.rs1use 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
14pub struct ErrorParser;
19
20lazy_static! {
21 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 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 static ref CONNECTION_ERROR_REGEX: Regex = Regex::new(
36 &create_queue_id_pattern(r"^{QUEUE_ID}: (.+)$")
37 ).unwrap();
38
39 static ref SYSTEM_ERROR_REGEX: Regex = Regex::new(
42 r"^(warning|error|fatal|panic):\s+(.+)$"
43 ).unwrap();
44
45 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 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 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 fn parse_connection_error(&self, message: &str) -> Option<ErrorEvent> {
115 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 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(), 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 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 fn parse_other_error(&self, message: &str) -> Option<ErrorEvent> {
162 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 if let Some(event) = self.parse_delivery_deferred(message) {
179 return Ok(ComponentEvent::Error(event));
180 }
181
182 if let Some(event) = self.parse_delivery_failed(message) {
184 return Ok(ComponentEvent::Error(event));
185 }
186
187 if let Some(event) = self.parse_connection_error(message) {
189 return Ok(ComponentEvent::Error(event));
190 }
191
192 if let Some(event) = self.parse_system_error(message) {
194 return Ok(ComponentEvent::Error(event));
195 }
196
197 if let Some(event) = self.parse_other_error(message) {
199 return Ok(ComponentEvent::Error(event));
200 }
201
202 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}