postfix_log_parser/components/
postmap.rs1use super::ComponentParser;
4use crate::error::ParseError;
5use crate::events::base::BaseEvent;
6use crate::events::postmap::{ErrorType, PostmapDetails, PostmapEvent, PostmapEventType};
7use crate::events::ComponentEvent;
8
9use regex::Regex;
10use std::collections::HashMap;
11
12pub struct PostmapParser {
21 file_error_regex: Regex,
23
24 command_error_regex: Regex,
26}
27
28impl PostmapParser {
29 pub fn new() -> Self {
30 Self {
31 file_error_regex: Regex::new(r"^open (.+?): (.+)$")
33 .expect("POSTMAP文件错误正则表达式编译失败"),
34
35 command_error_regex: Regex::new(r"^specify .+$")
37 .expect("POSTMAP命令错误正则表达式编译失败"),
38 }
39 }
40
41 pub fn parse_line(&self, line: &str, base_event: BaseEvent) -> Option<PostmapEvent> {
43 if let Some(event) = self.parse_file_error(line, base_event.clone()) {
45 return Some(event);
46 }
47
48 if let Some(event) = self.parse_command_error(line, base_event) {
50 return Some(event);
51 }
52
53 None
54 }
55
56 fn parse_file_error(&self, line: &str, base_event: BaseEvent) -> Option<PostmapEvent> {
57 if let Some(caps) = self.file_error_regex.captures(line) {
58 let file_path = caps.get(1).unwrap().as_str().to_string();
59 let error_message = caps.get(2).unwrap().as_str().to_string();
60
61 let error_type = if error_message.contains("No such file or directory") {
63 if file_path.ends_with('/') {
64 ErrorType::DirectoryNotFound
65 } else {
66 ErrorType::FileNotFound
67 }
68 } else {
69 ErrorType::FileNotFound
70 };
71
72 let details = PostmapDetails::new_file_error(error_type, file_path, error_message);
73
74 let event = PostmapEvent {
75 timestamp: base_event.timestamp,
76 process_id: base_event.process_id,
77 event_type: PostmapEventType::FileError,
78 details,
79 extensions: HashMap::new(),
80 };
81
82 return Some(event);
83 }
84
85 None
86 }
87
88 fn parse_command_error(&self, line: &str, base_event: BaseEvent) -> Option<PostmapEvent> {
89 if self.command_error_regex.is_match(line) {
90 let error_message = line.to_string();
91
92 if error_message.starts_with("open ") {
94 return None;
95 }
96
97 let command_args = if error_message.contains("specify -b -h or -m only with") {
99 Some("-b -h -m".to_string())
100 } else {
101 None
102 };
103
104 let details = PostmapDetails::new_command_error(error_message, command_args);
105
106 let event = PostmapEvent {
107 timestamp: base_event.timestamp,
108 process_id: base_event.process_id,
109 event_type: PostmapEventType::CommandError,
110 details,
111 extensions: HashMap::new(),
112 };
113
114 return Some(event);
115 }
116
117 None
118 }
119}
120
121impl ComponentParser for PostmapParser {
122 fn parse(&self, message: &str) -> Result<ComponentEvent, ParseError> {
123 let base_event = BaseEvent {
125 timestamp: chrono::Utc::now(), hostname: String::new(), component: "postmap".to_string(),
128 process_id: 0, log_level: crate::events::base::PostfixLogLevel::Fatal, raw_message: message.to_string(),
131 };
132
133 if let Some(event) = self.parse_line(message, base_event) {
134 Ok(ComponentEvent::Postmap(event))
135 } else {
136 Err(ParseError::ComponentParseError {
137 component: "postmap".to_string(),
138 reason: format!("无法识别的POSTMAP消息格式: {}", message),
139 })
140 }
141 }
142
143 fn component_name(&self) -> &'static str {
144 "postmap"
145 }
146}
147