postfix_log_parser/formatters/json/
relay.rs

1use super::common::add_troubleshooting_info;
2use crate::events::relay::{ConnectionIssueType, DeliveryStatus, RelayConfigType, RelayEvent};
3use serde_json::{json, Map, Value};
4
5/// RELAY组件JSON格式化器
6pub struct RelayJsonFormatter;
7
8impl RelayJsonFormatter {
9    pub fn new() -> Self {
10        Self
11    }
12
13    /// 格式化RELAY事件为JSON
14    pub fn format_event(&self, event: &RelayEvent) -> Value {
15        let mut base_object = Map::new();
16
17        // 添加组件通用信息
18        base_object.insert("component".to_string(), json!("relay"));
19
20        match event {
21            RelayEvent::DeliveryStatus {
22                base: _base,
23                queue_id,
24                recipient,
25                relay_host,
26                relay_ip,
27                relay_port,
28                delay,
29                delays,
30                dsn,
31                status,
32                status_description,
33            } => {
34                base_object.insert("event_type".to_string(), json!("delivery_status"));
35                base_object.insert("queue_id".to_string(), json!(queue_id));
36                base_object.insert("recipient".to_string(), json!(recipient));
37                base_object.insert("relay_host".to_string(), json!(relay_host));
38                base_object.insert("base".to_string(), json!(_base));
39                if let Some(ip) = relay_ip {
40                    base_object.insert("relay_ip".to_string(), json!(ip.to_string()));
41                }
42
43                if let Some(port) = relay_port {
44                    base_object.insert("relay_port".to_string(), json!(port));
45                }
46
47                base_object.insert("delay_total_seconds".to_string(), json!(delay));
48
49                // 详细的延迟分析
50                let delay_details = json!({
51                    "queue_wait_seconds": delays.queue_wait,
52                    "connection_setup_seconds": delays.connection_setup,
53                    "connection_time_seconds": delays.connection_time,
54                    "transmission_time_seconds": delays.transmission_time,
55                    "total_seconds": delays.total_delay(),
56                    "performance_analysis": self.analyze_delays(delays)
57                });
58                base_object.insert("delay_breakdown".to_string(), delay_details);
59
60                base_object.insert("dsn_code".to_string(), json!(dsn));
61                base_object.insert(
62                    "dsn_classification".to_string(),
63                    json!(self.classify_dsn(dsn)),
64                );
65                base_object.insert("delivery_status".to_string(), json!(status));
66                base_object.insert("status_description".to_string(), json!(status_description));
67
68                // 添加状态分析
69                base_object.insert(
70                    "delivery_analysis".to_string(),
71                    self.analyze_delivery_status(status, dsn, status_description),
72                );
73
74                // 添加中继健康评估
75                base_object.insert(
76                    "relay_health".to_string(),
77                    self.assess_relay_health(relay_host, delay, status),
78                );
79
80                // 添加故障排除建议
81                if matches!(
82                    status,
83                    DeliveryStatus::Deferred | DeliveryStatus::Failed | DeliveryStatus::Bounced
84                ) {
85                    add_troubleshooting_info(
86                        &mut base_object,
87                        &format!("relay delivery {} for {}", status, relay_host),
88                    );
89                }
90            }
91
92            RelayEvent::ConnectionIssue {
93                base: _base,
94                queue_id,
95                recipient,
96                relay_host,
97                relay_ip,
98                issue_type,
99                error_message,
100            } => {
101                base_object.insert("event_type".to_string(), json!("connection_issue"));
102                base_object.insert("queue_id".to_string(), json!(queue_id));
103                base_object.insert("recipient".to_string(), json!(recipient));
104                base_object.insert("relay_host".to_string(), json!(relay_host));
105                base_object.insert("base".to_string(), json!(_base));
106                if let Some(ip) = relay_ip {
107                    base_object.insert("relay_ip".to_string(), json!(ip.to_string()));
108                }
109
110                base_object.insert("issue_type".to_string(), json!(issue_type));
111                base_object.insert("error_message".to_string(), json!(error_message));
112
113                // 添加连接问题分析
114                base_object.insert(
115                    "connection_analysis".to_string(),
116                    self.analyze_connection_issue(issue_type, relay_host),
117                );
118
119                // 添加故障排除建议
120                add_troubleshooting_info(
121                    &mut base_object,
122                    &format!("relay connection {} to {}", issue_type, relay_host),
123                );
124            }
125
126            RelayEvent::RelayConfiguration {
127                base: _base,
128                config_type,
129                details,
130            } => {
131                base_object.insert("event_type".to_string(), json!("relay_configuration"));
132                base_object.insert("config_type".to_string(), json!(config_type));
133                base_object.insert("config_details".to_string(), json!(details));
134                base_object.insert("base".to_string(), json!(_base));
135                // 添加配置分析
136                base_object.insert(
137                    "config_analysis".to_string(),
138                    self.analyze_config_event(config_type, details),
139                );
140            }
141        }
142
143        // 添加组件统计信息
144        base_object.insert(
145            "relay_statistics".to_string(),
146            self.get_relay_statistics(event),
147        );
148
149        Value::Object(base_object)
150    }
151
152    /// 分析延迟性能
153    fn analyze_delays(&self, delays: &crate::events::relay::DelayBreakdown) -> Value {
154        let total = delays.total_delay();
155        let mut analysis = Map::new();
156
157        // 性能评级
158        let performance_rating = if total < 1.0 {
159            "excellent"
160        } else if total < 5.0 {
161            "good"
162        } else if total < 30.0 {
163            "fair"
164        } else {
165            "poor"
166        };
167
168        analysis.insert("overall_rating".to_string(), json!(performance_rating));
169
170        // 瓶颈分析
171        let bottleneck = if delays.connection_time > total * 0.5 {
172            "connection_establishment"
173        } else if delays.transmission_time > total * 0.5 {
174            "data_transmission"
175        } else if delays.queue_wait > total * 0.5 {
176            "queue_processing"
177        } else {
178            "balanced"
179        };
180
181        analysis.insert("primary_bottleneck".to_string(), json!(bottleneck));
182
183        // 各阶段占比
184        if total > 0.0 {
185            analysis.insert(
186                "phase_percentages".to_string(),
187                json!({
188                    "queue_wait": (delays.queue_wait / total * 100.0).round(),
189                    "connection_setup": (delays.connection_setup / total * 100.0).round(),
190                    "connection_time": (delays.connection_time / total * 100.0).round(),
191                    "transmission_time": (delays.transmission_time / total * 100.0).round()
192                }),
193            );
194        }
195
196        Value::Object(analysis)
197    }
198
199    /// 分析投递状态
200    fn analyze_delivery_status(
201        &self,
202        status: &DeliveryStatus,
203        dsn: &str,
204        _description: &str,
205    ) -> Value {
206        let mut analysis = Map::new();
207
208        // 投递结果分类
209        let result_category = match status {
210            DeliveryStatus::Sent => "successful",
211            DeliveryStatus::Deferred => "temporary_failure",
212            DeliveryStatus::Bounced => "permanent_failure",
213            DeliveryStatus::Failed => "permanent_failure",
214            DeliveryStatus::Rejected => "permanent_failure",
215        };
216
217        analysis.insert("result_category".to_string(), json!(result_category));
218        analysis.insert(
219            "is_retryable".to_string(),
220            json!(matches!(status, DeliveryStatus::Deferred)),
221        );
222
223        // DSN分析
224        let dsn_class = self.classify_dsn(dsn);
225        analysis.insert("dsn_class".to_string(), json!(dsn_class));
226
227        // 建议操作
228        let recommended_action = match status {
229            DeliveryStatus::Sent => "none",
230            DeliveryStatus::Deferred => "monitor_retry",
231            DeliveryStatus::Bounced => "check_recipient_address",
232            DeliveryStatus::Failed => "investigate_server_config",
233            DeliveryStatus::Rejected => "verify_sender_reputation",
234        };
235
236        analysis.insert("recommended_action".to_string(), json!(recommended_action));
237
238        Value::Object(analysis)
239    }
240
241    /// 评估中继健康状况
242    fn assess_relay_health(&self, relay_host: &str, delay: &f64, status: &DeliveryStatus) -> Value {
243        let mut health = Map::new();
244
245        // 性能健康度
246        let performance_score = if *delay < 1.0 {
247            100
248        } else if *delay < 5.0 {
249            80
250        } else if *delay < 30.0 {
251            60
252        } else {
253            30
254        };
255
256        // 可靠性健康度
257        let reliability_score = match status {
258            DeliveryStatus::Sent => 100,
259            DeliveryStatus::Deferred => 70,
260            DeliveryStatus::Bounced => 30,
261            DeliveryStatus::Failed => 20,
262            DeliveryStatus::Rejected => 20,
263        };
264
265        let overall_score = (performance_score + reliability_score) / 2;
266
267        let health_status = if overall_score >= 90 {
268            "excellent"
269        } else if overall_score >= 70 {
270            "good"
271        } else if overall_score >= 50 {
272            "warning"
273        } else {
274            "critical"
275        };
276
277        health.insert("relay_host".to_string(), json!(relay_host));
278        health.insert("performance_score".to_string(), json!(performance_score));
279        health.insert("reliability_score".to_string(), json!(reliability_score));
280        health.insert("overall_score".to_string(), json!(overall_score));
281        health.insert("health_status".to_string(), json!(health_status));
282
283        Value::Object(health)
284    }
285
286    /// 分析连接问题
287    fn analyze_connection_issue(
288        &self,
289        issue_type: &ConnectionIssueType,
290        relay_host: &str,
291    ) -> Value {
292        let mut analysis = Map::new();
293
294        let (severity, likely_cause, resolution_time) = match issue_type {
295            ConnectionIssueType::LostConnection => {
296                ("medium", "network_instability", "5-30 minutes")
297            }
298            ConnectionIssueType::ConnectionTimeout => {
299                ("medium", "network_congestion", "5-60 minutes")
300            }
301            ConnectionIssueType::ConnectionRefused => {
302                ("high", "service_unavailable", "immediate to hours")
303            }
304            ConnectionIssueType::DnsResolutionFailed => {
305                ("high", "dns_configuration", "immediate to hours")
306            }
307            ConnectionIssueType::TlsHandshakeFailed => {
308                ("high", "certificate_issue", "immediate to days")
309            }
310            ConnectionIssueType::AuthenticationFailed => ("high", "credential_issue", "immediate"),
311            ConnectionIssueType::Other => ("medium", "unknown", "varies"),
312        };
313
314        analysis.insert("severity".to_string(), json!(severity));
315        analysis.insert("likely_cause".to_string(), json!(likely_cause));
316        analysis.insert(
317            "expected_resolution_time".to_string(),
318            json!(resolution_time),
319        );
320        analysis.insert("affected_relay".to_string(), json!(relay_host));
321
322        Value::Object(analysis)
323    }
324
325    /// 分析配置事件
326    fn analyze_config_event(&self, config_type: &RelayConfigType, details: &str) -> Value {
327        let mut analysis = Map::new();
328
329        let importance = match config_type {
330            RelayConfigType::RelayHostConfig => "critical",
331            RelayConfigType::TransportMapping => "high",
332            RelayConfigType::AuthConfig => "high",
333            RelayConfigType::TlsConfig => "medium",
334        };
335
336        analysis.insert("importance".to_string(), json!(importance));
337        analysis.insert("config_type".to_string(), json!(config_type));
338        analysis.insert(
339            "requires_attention".to_string(),
340            json!(details.contains("error") || details.contains("warning")),
341        );
342
343        Value::Object(analysis)
344    }
345
346    /// DSN代码分类
347    fn classify_dsn(&self, dsn: &str) -> &'static str {
348        if dsn.starts_with("2.") {
349            "success"
350        } else if dsn.starts_with("4.") {
351            "temporary_failure"
352        } else if dsn.starts_with("5.") {
353            "permanent_failure"
354        } else {
355            "unknown"
356        }
357    }
358
359    /// 获取中继统计信息
360    fn get_relay_statistics(&self, event: &RelayEvent) -> Value {
361        let mut stats = Map::new();
362
363        stats.insert("component".to_string(), json!("relay"));
364        stats.insert(
365            "component_description".to_string(),
366            json!("Postfix中继传输代理"),
367        );
368
369        match event {
370            RelayEvent::DeliveryStatus {
371                relay_host, status, ..
372            } => {
373                stats.insert("relay_destination".to_string(), json!(relay_host));
374                stats.insert("operation_type".to_string(), json!("mail_delivery"));
375                stats.insert("operation_result".to_string(), json!(status));
376            }
377            RelayEvent::ConnectionIssue {
378                relay_host,
379                issue_type,
380                ..
381            } => {
382                stats.insert("relay_destination".to_string(), json!(relay_host));
383                stats.insert("operation_type".to_string(), json!("connection"));
384                stats.insert("operation_result".to_string(), json!(issue_type));
385            }
386            RelayEvent::RelayConfiguration { config_type, .. } => {
387                stats.insert("operation_type".to_string(), json!("configuration"));
388                stats.insert("config_category".to_string(), json!(config_type));
389            }
390        }
391
392        Value::Object(stats)
393    }
394}
395
396impl Default for RelayJsonFormatter {
397    fn default() -> Self {
398        Self::new()
399    }
400}