postfix_log_parser/events/
error.rs1use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
12#[serde(tag = "event_type")]
13pub enum ErrorEvent {
14 DeliveryDeferred {
16 queue_id: String,
17 to: String,
18 relay: String,
19 delay: f64,
20 delays: String,
21 dsn: String,
22 status: String,
23 reason: String,
24 error_type: ErrorType,
25 },
26
27 DeliveryFailed {
29 queue_id: String,
30 to: String,
31 relay: String,
32 delay: f64,
33 delays: String,
34 dsn: String,
35 status: String,
36 reason: String,
37 error_type: ErrorType,
38 },
39
40 SystemError {
42 queue_id: Option<String>,
43 error_type: ErrorType,
44 message: String,
45 },
46
47 ConnectionError {
49 queue_id: String,
50 to: String,
51 relay: String,
52 delay: f64,
53 delays: String,
54 dsn: String,
55 reason: String,
56 error_type: ErrorType,
57 },
58
59 Other {
61 queue_id: Option<String>,
62 error_type: String,
63 message: String,
64 },
65}
66
67impl ErrorEvent {
68 pub fn event_type(&self) -> &'static str {
70 match self {
71 ErrorEvent::DeliveryDeferred { .. } => "delivery_deferred",
72 ErrorEvent::DeliveryFailed { .. } => "delivery_failed",
73 ErrorEvent::SystemError { .. } => "system_error",
74 ErrorEvent::ConnectionError { .. } => "connection_error",
75 ErrorEvent::Other { .. } => "error_other",
76 }
77 }
78
79 pub fn queue_id(&self) -> Option<&str> {
81 match self {
82 ErrorEvent::DeliveryDeferred { queue_id, .. } => Some(queue_id),
83 ErrorEvent::DeliveryFailed { queue_id, .. } => Some(queue_id),
84 ErrorEvent::SystemError { queue_id, .. } => queue_id.as_deref(),
85 ErrorEvent::ConnectionError { queue_id, .. } => Some(queue_id),
86 ErrorEvent::Other { queue_id, .. } => queue_id.as_deref(),
87 }
88 }
89
90 pub fn severity(&self) -> u8 {
92 match self {
93 ErrorEvent::DeliveryDeferred { error_type, .. } => error_type.severity().level(),
94 ErrorEvent::DeliveryFailed { error_type, .. } => error_type.severity().level() + 1, ErrorEvent::SystemError { error_type, .. } => error_type.severity().level(),
96 ErrorEvent::ConnectionError { error_type, .. } => error_type.severity().level(),
97 ErrorEvent::Other { .. } => 2, }
99 }
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
106pub enum ErrorType {
107 #[serde(rename = "dns_resolution")]
109 DnsResolution,
110
111 #[serde(rename = "connection_refused")]
113 ConnectionRefused,
114
115 #[serde(rename = "connection_lost")]
117 ConnectionLost,
118
119 #[serde(rename = "connection_timeout")]
121 ConnectionTimeout,
122
123 #[serde(rename = "host_unreachable")]
125 HostUnreachable,
126
127 #[serde(rename = "mailbox_not_found")]
129 MailboxNotFound,
130
131 #[serde(rename = "mailbox_full")]
133 MailboxFull,
134
135 #[serde(rename = "authentication_failed")]
137 AuthenticationFailed,
138
139 #[serde(rename = "rate_limited")]
141 RateLimited,
142
143 #[serde(rename = "spam_rejected")]
145 SpamRejected,
146
147 #[serde(rename = "policy_rejected")]
149 PolicyRejected,
150
151 #[serde(rename = "system_config")]
153 SystemConfig,
154
155 #[serde(rename = "resource_shortage")]
157 ResourceShortage,
158
159 #[serde(rename = "protocol_error")]
161 ProtocolError,
162
163 #[serde(rename = "tls_error")]
165 TlsError,
166
167 #[serde(rename = "other")]
169 Other,
170}
171
172impl ErrorType {
173 pub fn from_reason(reason: &str) -> Self {
175 let reason_lower = reason.to_lowercase();
176
177 if reason_lower.contains("host or domain name not found")
178 || reason_lower.contains("name service error")
179 || reason_lower.contains("host not found")
180 {
181 Self::DnsResolution
182 } else if reason_lower.contains("connection refused") {
183 Self::ConnectionRefused
184 } else if reason_lower.contains("lost connection") {
185 Self::ConnectionLost
186 } else if reason_lower.contains("connection timed out") || reason_lower.contains("timeout")
187 {
188 Self::ConnectionTimeout
189 } else if reason_lower.contains("host unreachable")
190 || reason_lower.contains("network unreachable")
191 {
192 Self::HostUnreachable
193 } else if reason_lower.contains("user unknown")
194 || reason_lower.contains("recipient address rejected")
195 || reason_lower.contains("mailbox unavailable")
196 {
197 Self::MailboxNotFound
198 } else if reason_lower.contains("mailbox full")
199 || reason_lower.contains("quota exceeded")
200 || reason_lower.contains("insufficient storage")
201 {
202 Self::MailboxFull
203 } else if reason_lower.contains("authentication") || reason_lower.contains("auth") {
204 Self::AuthenticationFailed
205 } else if reason_lower.contains("rate limit")
206 || reason_lower.contains("too many")
207 || reason_lower.contains("throttl")
208 {
209 Self::RateLimited
210 } else if reason_lower.contains("spam")
211 || reason_lower.contains("blacklist")
212 || reason_lower.contains("blocked")
213 {
214 Self::SpamRejected
215 } else if reason_lower.contains("policy") || reason_lower.contains("rejected") {
216 Self::PolicyRejected
217 } else if reason_lower.contains("tls")
218 || reason_lower.contains("ssl")
219 || reason_lower.contains("certificate")
220 {
221 Self::TlsError
222 } else if reason_lower.contains("protocol") {
223 Self::ProtocolError
224 } else if reason_lower.contains("config") || reason_lower.contains("permission") {
225 Self::SystemConfig
226 } else if reason_lower.contains("resource")
227 || reason_lower.contains("memory")
228 || reason_lower.contains("disk")
229 {
230 Self::ResourceShortage
231 } else {
232 Self::Other
233 }
234 }
235
236 pub fn description(&self) -> &'static str {
238 match self {
239 Self::DnsResolution => "DNS解析错误",
240 Self::ConnectionRefused => "连接被拒绝",
241 Self::ConnectionLost => "连接丢失",
242 Self::ConnectionTimeout => "连接超时",
243 Self::HostUnreachable => "主机不可达",
244 Self::MailboxNotFound => "邮箱不存在",
245 Self::MailboxFull => "邮箱已满",
246 Self::AuthenticationFailed => "认证失败",
247 Self::RateLimited => "速率限制",
248 Self::SpamRejected => "垃圾邮件拒绝",
249 Self::PolicyRejected => "策略拒绝",
250 Self::SystemConfig => "系统配置错误",
251 Self::ResourceShortage => "资源不足",
252 Self::ProtocolError => "协议错误",
253 Self::TlsError => "TLS/SSL错误",
254 Self::Other => "其他错误",
255 }
256 }
257
258 pub fn severity(&self) -> ErrorSeverity {
260 match self {
261 Self::DnsResolution | Self::ConnectionTimeout | Self::ConnectionLost => {
262 ErrorSeverity::Temporary
263 }
264 Self::ConnectionRefused | Self::HostUnreachable => ErrorSeverity::Network,
265 Self::MailboxNotFound | Self::MailboxFull => ErrorSeverity::RecipientIssue,
266 Self::AuthenticationFailed | Self::PolicyRejected | Self::SpamRejected => {
267 ErrorSeverity::PolicyIssue
268 }
269 Self::SystemConfig | Self::ResourceShortage => ErrorSeverity::SystemIssue,
270 Self::TlsError | Self::ProtocolError => ErrorSeverity::ProtocolIssue,
271 Self::RateLimited => ErrorSeverity::Temporary,
272 Self::Other => ErrorSeverity::Unknown,
273 }
274 }
275}
276
277#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
279pub enum ErrorSeverity {
280 #[serde(rename = "temporary")]
282 Temporary,
283
284 #[serde(rename = "network")]
286 Network,
287
288 #[serde(rename = "recipient_issue")]
290 RecipientIssue,
291
292 #[serde(rename = "policy_issue")]
294 PolicyIssue,
295
296 #[serde(rename = "system_issue")]
298 SystemIssue,
299
300 #[serde(rename = "protocol_issue")]
302 ProtocolIssue,
303
304 #[serde(rename = "unknown")]
306 Unknown,
307}
308
309impl ErrorSeverity {
310 pub fn level(&self) -> u8 {
312 match self {
313 Self::Temporary => 1,
314 Self::Network => 2,
315 Self::RecipientIssue => 3,
316 Self::PolicyIssue => 3,
317 Self::ProtocolIssue => 4,
318 Self::SystemIssue => 5,
319 Self::Unknown => 2,
320 }
321 }
322
323 pub fn description(&self) -> &'static str {
325 match self {
326 Self::Temporary => "临时性错误,通常可重试",
327 Self::Network => "网络连接问题",
328 Self::RecipientIssue => "收件人相关问题",
329 Self::PolicyIssue => "策略或安全问题",
330 Self::ProtocolIssue => "协议层面问题",
331 Self::SystemIssue => "系统级别问题",
332 Self::Unknown => "未知类型错误",
333 }
334 }
335}