postfix_log_parser/events/
discard.rs

1use serde::{Deserialize, Serialize};
2
3use super::base::BaseEvent;
4
5/// DISCARD组件事件类型
6/// 基于Postfix源码和真实生产数据分析
7/// DISCARD是"假装投递"的邮件丢弃代理,不进行实际网络投递
8#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(tag = "event_type")]
10pub enum DiscardEvent {
11    /// 邮件丢弃事件 - DISCARD的主要功能,假装投递但实际丢弃
12    MessageDiscard {
13        #[serde(flatten)]
14        base: BaseEvent,
15        queue_id: String,
16        recipient: String,
17        /// DISCARD总是为"none",表示无实际中继
18        relay: String,
19        /// 处理延迟时间(秒)
20        delay: f64,
21        /// 延迟时间细分 (queue/conn_setup/conn/transmission)
22        delays: DelayBreakdown,
23        /// DSN状态码,通常为2.0.0表示"成功丢弃"
24        dsn: String,
25        /// 投递状态,DISCARD总是报告为"sent"
26        status: String,
27        /// 丢弃原因,通常是下一跳目的地或配置的原因
28        discard_reason: String,
29    },
30    /// 配置事件 - DISCARD代理的配置相关事件
31    Configuration {
32        #[serde(flatten)]
33        base: BaseEvent,
34        config_type: DiscardConfigType,
35        details: String,
36    },
37}
38
39/// 延迟时间细分 - 与其他投递代理保持一致的格式
40#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct DelayBreakdown {
42    /// 等待在队列中的时间
43    pub queue_wait: f64,
44    /// 连接建立前的延迟(DISCARD中通常为0)
45    pub connection_setup: f64,
46    /// 建立连接的时间(DISCARD中通常为0)
47    pub connection_time: f64,
48    /// 数据传输时间(DISCARD中通常为0)
49    pub transmission_time: f64,
50}
51
52/// DISCARD配置类型
53#[derive(Debug, Clone, Serialize, Deserialize)]
54#[serde(rename_all = "snake_case")]
55pub enum DiscardConfigType {
56    /// 传输映射配置
57    TransportMapping,
58    /// 丢弃规则配置
59    DiscardRules,
60    /// 服务启动配置
61    ServiceStartup,
62    /// 其他配置
63    Other,
64}
65
66impl DiscardEvent {
67    /// 获取队列ID(如果存在)
68    pub fn queue_id(&self) -> Option<&str> {
69        match self {
70            DiscardEvent::MessageDiscard { queue_id, .. } => Some(queue_id),
71            DiscardEvent::Configuration { .. } => None,
72        }
73    }
74
75    /// 获取收件人地址(如果存在)
76    pub fn recipient(&self) -> Option<&str> {
77        match self {
78            DiscardEvent::MessageDiscard { recipient, .. } => Some(recipient),
79            DiscardEvent::Configuration { .. } => None,
80        }
81    }
82
83    /// 获取丢弃原因
84    pub fn discard_reason(&self) -> Option<&str> {
85        match self {
86            DiscardEvent::MessageDiscard { discard_reason, .. } => Some(discard_reason),
87            DiscardEvent::Configuration { .. } => None,
88        }
89    }
90
91    /// 判断是否为邮件丢弃事件
92    pub fn is_message_discard(&self) -> bool {
93        matches!(self, DiscardEvent::MessageDiscard { .. })
94    }
95
96    /// 获取事件严重性级别
97    pub fn severity(&self) -> &'static str {
98        match self {
99            DiscardEvent::MessageDiscard { .. } => "info", // 正常的邮件丢弃
100            DiscardEvent::Configuration { .. } => "info",  // 配置信息
101        }
102    }
103
104    /// 检查是否为成功的丢弃操作
105    pub fn is_successful_discard(&self) -> bool {
106        match self {
107            DiscardEvent::MessageDiscard { status, dsn, .. } => {
108                status == "sent" && dsn.starts_with("2.") // 2.x.x DSN表示成功
109            }
110            DiscardEvent::Configuration { .. } => false,
111        }
112    }
113}
114
115impl DelayBreakdown {
116    /// 从Postfix的delays字符串解析 (格式: "queue/conn_setup/conn/transmission")
117    pub fn from_delays_string(delays_str: &str) -> Option<Self> {
118        let parts: Vec<&str> = delays_str.split('/').collect();
119        if parts.len() != 4 {
120            return None;
121        }
122
123        let queue_wait = parts[0].parse().ok()?;
124        let connection_setup = parts[1].parse().ok()?;
125        let connection_time = parts[2].parse().ok()?;
126        let transmission_time = parts[3].parse().ok()?;
127
128        Some(DelayBreakdown {
129            queue_wait,
130            connection_setup,
131            connection_time,
132            transmission_time,
133        })
134    }
135
136    /// 计算总延迟时间
137    pub fn total_delay(&self) -> f64 {
138        self.queue_wait + self.connection_setup + self.connection_time + self.transmission_time
139    }
140
141    /// 判断是否为快速丢弃(总延迟<0.1秒)
142    pub fn is_fast_discard(&self) -> bool {
143        self.total_delay() < 0.1
144    }
145}
146
147impl std::fmt::Display for DiscardConfigType {
148    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149        match self {
150            DiscardConfigType::TransportMapping => write!(f, "transport mapping"),
151            DiscardConfigType::DiscardRules => write!(f, "discard rules"),
152            DiscardConfigType::ServiceStartup => write!(f, "service startup"),
153            DiscardConfigType::Other => write!(f, "other"),
154        }
155    }
156}