postfix_log_parser/events/
discard.rs

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