postfix_log_parser/components/
postmap.rs1use super::ComponentParser;
33use crate::error::ParseError;
34use crate::events::base::BaseEvent;
35use crate::events::postmap::{ErrorType, PostmapDetails, PostmapEvent, PostmapEventType};
36use crate::events::ComponentEvent;
37
38use regex::Regex;
39use std::collections::HashMap;
40
41pub struct PostmapParser {
50 file_error_regex: Regex,
52
53 command_error_regex: Regex,
55}
56
57impl PostmapParser {
58 pub fn new() -> Self {
59 Self {
60 file_error_regex: Regex::new(r"^open (.+?): (.+)$")
62 .expect("POSTMAP文件错误正则表达式编译失败"),
63
64 command_error_regex: Regex::new(r"^specify .+$")
66 .expect("POSTMAP命令错误正则表达式编译失败"),
67 }
68 }
69
70 pub fn parse_line(&self, line: &str, base_event: BaseEvent) -> Option<PostmapEvent> {
72 if let Some(event) = self.parse_file_error(line, base_event.clone()) {
74 return Some(event);
75 }
76
77 if let Some(event) = self.parse_command_error(line, base_event) {
79 return Some(event);
80 }
81
82 None
83 }
84
85 fn parse_file_error(&self, line: &str, base_event: BaseEvent) -> Option<PostmapEvent> {
86 if let Some(caps) = self.file_error_regex.captures(line) {
87 let file_path = caps.get(1).unwrap().as_str().to_string();
88 let error_message = caps.get(2).unwrap().as_str().to_string();
89
90 let error_type = if error_message.contains("No such file or directory") {
92 if file_path.ends_with('/') {
93 ErrorType::DirectoryNotFound
94 } else {
95 ErrorType::FileNotFound
96 }
97 } else {
98 ErrorType::FileNotFound
99 };
100
101 let details = PostmapDetails::new_file_error(error_type, file_path, error_message);
102
103 let event = PostmapEvent {
104 timestamp: base_event.timestamp,
105 process_id: base_event.process_id,
106 event_type: PostmapEventType::FileError,
107 details,
108 extensions: HashMap::new(),
109 };
110
111 return Some(event);
112 }
113
114 None
115 }
116
117 fn parse_command_error(&self, line: &str, base_event: BaseEvent) -> Option<PostmapEvent> {
118 if self.command_error_regex.is_match(line) {
119 let error_message = line.to_string();
120
121 if error_message.starts_with("open ") {
123 return None;
124 }
125
126 let command_args = if error_message.contains("specify -b -h or -m only with") {
128 Some("-b -h -m".to_string())
129 } else {
130 None
131 };
132
133 let details = PostmapDetails::new_command_error(error_message, command_args);
134
135 let event = PostmapEvent {
136 timestamp: base_event.timestamp,
137 process_id: base_event.process_id,
138 event_type: PostmapEventType::CommandError,
139 details,
140 extensions: HashMap::new(),
141 };
142
143 return Some(event);
144 }
145
146 None
147 }
148}
149
150impl ComponentParser for PostmapParser {
151 fn parse(&self, message: &str) -> Result<ComponentEvent, ParseError> {
152 let base_event = BaseEvent {
154 timestamp: chrono::Utc::now(), hostname: String::new(), component: "postmap".to_string(),
157 process_id: 0, log_level: crate::events::base::PostfixLogLevel::Fatal, raw_message: message.to_string(),
160 };
161
162 if let Some(event) = self.parse_line(message, base_event) {
163 Ok(ComponentEvent::Postmap(event))
164 } else {
165 Err(ParseError::ComponentParseError {
166 component: "postmap".to_string(),
167 reason: format!("无法识别的POSTMAP消息格式: {}", message),
168 })
169 }
170 }
171
172 fn component_name(&self) -> &'static str {
173 "postmap"
174 }
175}
176
177impl Default for PostmapParser {
178 fn default() -> Self {
179 Self::new()
180 }
181}