postfix_log_parser/events/
relay.rs

1use serde::{Deserialize, Serialize};
2use std::net::IpAddr;
3
4use super::base::BaseEvent;
5
6/// RELAY组件事件类型
7/// 基于Postfix源码和真实生产数据分析
8#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(tag = "event_type")]
10pub enum RelayEvent {
11    /// 邮件投递状态 - 占95%+的relay日志
12    DeliveryStatus {
13        #[serde(flatten)]
14        base: BaseEvent,
15        queue_id: String,
16        recipient: String,
17        relay_host: String,
18        relay_ip: Option<IpAddr>,
19        relay_port: Option<u16>,
20        delay: f64,
21        delays: DelayBreakdown,
22        dsn: String,
23        status: DeliveryStatus,
24        status_description: String,
25    },
26    /// 连接问题事件
27    ConnectionIssue {
28        #[serde(flatten)]
29        base: BaseEvent,
30        queue_id: String,
31        recipient: String,
32        relay_host: String,
33        relay_ip: Option<IpAddr>,
34        issue_type: ConnectionIssueType,
35        error_message: String,
36    },
37    /// 中继配置事件
38    RelayConfiguration {
39        #[serde(flatten)]
40        base: BaseEvent,
41        config_type: RelayConfigType,
42        details: String,
43    },
44}
45
46/// 投递状态类型
47#[derive(Debug, Clone, Serialize, Deserialize)]
48#[serde(rename_all = "lowercase")]
49pub enum DeliveryStatus {
50    /// 投递成功
51    Sent,
52    /// 投递延迟/推迟
53    Deferred,
54    /// 投递反弹
55    Bounced,
56    /// 投递失败
57    Failed,
58    /// 拒绝投递
59    Rejected,
60}
61
62impl std::fmt::Display for DeliveryStatus {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        match self {
65            DeliveryStatus::Sent => write!(f, "sent"),
66            DeliveryStatus::Deferred => write!(f, "deferred"),
67            DeliveryStatus::Bounced => write!(f, "bounced"),
68            DeliveryStatus::Failed => write!(f, "failed"),
69            DeliveryStatus::Rejected => write!(f, "rejected"),
70        }
71    }
72}
73
74/// 延迟时间细分 - Postfix特有的delays格式
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct DelayBreakdown {
77    /// 等待在队列中的时间
78    pub queue_wait: f64,
79    /// 连接建立前的延迟
80    pub connection_setup: f64,
81    /// 建立连接的时间
82    pub connection_time: f64,
83    /// 数据传输时间
84    pub transmission_time: f64,
85}
86
87/// 连接问题类型
88#[derive(Debug, Clone, Serialize, Deserialize)]
89#[serde(rename_all = "snake_case")]
90pub enum ConnectionIssueType {
91    /// 连接丢失
92    LostConnection,
93    /// 连接超时
94    ConnectionTimeout,
95    /// 连接拒绝
96    ConnectionRefused,
97    /// DNS解析失败
98    DnsResolutionFailed,
99    /// TLS握手失败
100    TlsHandshakeFailed,
101    /// 认证失败
102    AuthenticationFailed,
103    /// 其他连接问题
104    Other,
105}
106
107impl std::fmt::Display for ConnectionIssueType {
108    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109        match self {
110            ConnectionIssueType::LostConnection => write!(f, "lost connection"),
111            ConnectionIssueType::ConnectionTimeout => write!(f, "connection timeout"),
112            ConnectionIssueType::ConnectionRefused => write!(f, "connection refused"),
113            ConnectionIssueType::DnsResolutionFailed => write!(f, "DNS resolution failed"),
114            ConnectionIssueType::TlsHandshakeFailed => write!(f, "TLS handshake failed"),
115            ConnectionIssueType::AuthenticationFailed => write!(f, "authentication failed"),
116            ConnectionIssueType::Other => write!(f, "other connection issue"),
117        }
118    }
119}
120
121/// 中继配置类型
122#[derive(Debug, Clone, Serialize, Deserialize)]
123#[serde(rename_all = "snake_case")]
124pub enum RelayConfigType {
125    /// 中继主机配置
126    RelayHostConfig,
127    /// 传输映射配置
128    TransportMapping,
129    /// 认证配置
130    AuthConfig,
131    /// TLS配置
132    TlsConfig,
133}
134
135impl RelayEvent {
136    /// 获取队列ID(如果存在)
137    pub fn queue_id(&self) -> Option<&str> {
138        match self {
139            RelayEvent::DeliveryStatus { queue_id, .. } => Some(queue_id),
140            RelayEvent::ConnectionIssue { queue_id, .. } => Some(queue_id),
141            RelayEvent::RelayConfiguration { .. } => None,
142        }
143    }
144
145    /// 获取收件人地址(如果存在)
146    pub fn recipient(&self) -> Option<&str> {
147        match self {
148            RelayEvent::DeliveryStatus { recipient, .. } => Some(recipient),
149            RelayEvent::ConnectionIssue { recipient, .. } => Some(recipient),
150            RelayEvent::RelayConfiguration { .. } => None,
151        }
152    }
153
154    /// 获取中继主机(如果存在)
155    pub fn relay_host(&self) -> Option<&str> {
156        match self {
157            RelayEvent::DeliveryStatus { relay_host, .. } => Some(relay_host),
158            RelayEvent::ConnectionIssue { relay_host, .. } => Some(relay_host),
159            RelayEvent::RelayConfiguration { .. } => None,
160        }
161    }
162
163    /// 判断是否为投递成功事件
164    pub fn is_successful_delivery(&self) -> bool {
165        matches!(
166            self,
167            RelayEvent::DeliveryStatus {
168                status: DeliveryStatus::Sent,
169                ..
170            }
171        )
172    }
173
174    /// 判断是否为连接问题
175    pub fn is_connection_issue(&self) -> bool {
176        matches!(self, RelayEvent::ConnectionIssue { .. })
177    }
178
179    /// 获取事件严重性级别
180    pub fn severity(&self) -> &'static str {
181        match self {
182            RelayEvent::DeliveryStatus { status, .. } => match status {
183                DeliveryStatus::Sent => "info",
184                DeliveryStatus::Deferred => "warning",
185                DeliveryStatus::Bounced => "error",
186                DeliveryStatus::Failed => "error",
187                DeliveryStatus::Rejected => "error",
188            },
189            RelayEvent::ConnectionIssue { .. } => "warning",
190            RelayEvent::RelayConfiguration { .. } => "info",
191        }
192    }
193}
194
195impl DelayBreakdown {
196    /// 从Postfix的delays字符串解析 (格式: "queue/conn_setup/conn/transmission")
197    pub fn from_delays_string(delays_str: &str) -> Option<Self> {
198        let parts: Vec<&str> = delays_str.split('/').collect();
199        if parts.len() != 4 {
200            return None;
201        }
202
203        Some(DelayBreakdown {
204            queue_wait: parts[0].parse().unwrap_or(0.0),
205            connection_setup: parts[1].parse().unwrap_or(0.0),
206            connection_time: parts[2].parse().unwrap_or(0.0),
207            transmission_time: parts[3].parse().unwrap_or(0.0),
208        })
209    }
210
211    /// 获取总延迟时间
212    pub fn total_delay(&self) -> f64 {
213        self.queue_wait + self.connection_setup + self.connection_time + self.transmission_time
214    }
215}