1use num_derive::FromPrimitive;
4use std::error::Error;
5use std::fmt::{Display, Error as FmtError, Formatter, Result as FmtResult, Write};
6use std::time::Duration;
7use strum_macros::{Display, EnumString};
8
9#[cfg(feature = "extra-traits")]
10use serde::{Deserialize, Serialize};
11
12#[derive(PartialEq, Debug, Clone)]
14pub struct Entry {
15 pub facility: Option<LogFacility>,
17
18 pub level: Option<LogLevel>,
20
21 pub sequence_num: Option<usize>,
23
24 pub timestamp_from_system_start: Option<Duration>,
26
27 pub message: String,
29}
30
31impl Entry {
32 pub fn to_faclev(&self) -> Option<u8> {
33 match (self.facility, self.level) {
34 (Some(facility), Some(level)) => Some(((facility as u8) << 3) + (level as u8)),
35 _ => None,
36 }
37 }
38
39 pub fn to_klog_str(&self) -> Result<String, FmtError> {
44 if let Some(faclev) = self.to_faclev() {
45 let mut retstr = String::with_capacity(35 + self.message.len());
47
48 write!(retstr, "<{}>", faclev)?;
49
50 if let Some(ts) = self.timestamp_from_system_start {
51 write!(retstr, "[{: >16.6}]", ts.as_secs_f64())?;
52 }
53
54 write!(retstr, "{}", self.message)?;
55
56 Ok(retstr)
57 } else {
58 Ok(self.message.to_owned())
59 }
60 }
61
62 pub fn to_kmsg_str(&self) -> Result<String, FmtError> {
67 if let Some(faclev) = self.to_faclev() {
68 let mut retstr = String::with_capacity(25 + self.message.len());
70
71 let sequence_num = self.sequence_num.unwrap_or(0);
72 write!(retstr, "{},{},", faclev, sequence_num)?;
73
74 if let Some(ts) = self.timestamp_from_system_start {
75 write!(retstr, "{},-;", ts.as_micros())?;
76 } else {
77 retstr.push_str("0,-;");
78 }
79
80 write!(retstr, "{}", self.message)?;
81
82 Ok(retstr)
83 } else {
84 Ok(self.message.to_string())
85 }
86 }
87}
88
89impl Display for Entry {
90 fn fmt(&self, f: &mut Formatter) -> FmtResult {
91 if let Some(ts) = self.timestamp_from_system_start {
92 write!(f, "[{: >16.6}] ", ts.as_secs_f64())?
93 }
94
95 write!(f, "{}", self.message)
96 }
97}
98
99#[cfg_attr(feature = "extra-traits", derive(Serialize, Deserialize))]
101#[derive(EnumString, Debug, PartialEq, Display, Copy, Clone, FromPrimitive)]
102pub enum LogFacility {
103 #[strum(serialize = "kern")]
104 Kern = 0,
105
106 #[strum(serialize = "user")]
107 User,
108
109 #[strum(serialize = "mail")]
110 Mail,
111
112 #[strum(serialize = "daemon")]
113 Daemon,
114
115 #[strum(serialize = "auth")]
116 Auth,
117
118 #[strum(serialize = "syslog")]
119 Syslog,
120
121 #[strum(serialize = "lpr")]
122 Lpr,
123
124 #[strum(serialize = "news")]
125 News,
126
127 #[strum(serialize = "uucp")]
128 UUCP,
129
130 #[strum(serialize = "cron")]
131 Cron,
132
133 #[strum(serialize = "authpriv")]
134 AuthPriv,
135
136 #[strum(serialize = "ftp")]
137 FTP,
138}
139
140#[cfg_attr(feature = "extra-traits", derive(Serialize, Deserialize))]
142#[derive(EnumString, Debug, PartialEq, Display, Copy, Clone, FromPrimitive)]
143pub enum LogLevel {
144 #[strum(serialize = "emerg")]
145 Emergency = 0,
146
147 #[strum(serialize = "alert")]
148 Alert,
149
150 #[strum(serialize = "crit")]
151 Critical,
152
153 #[strum(serialize = "err")]
154 Error,
155
156 #[strum(serialize = "warn")]
157 Warning,
158
159 #[strum(serialize = "notice")]
160 Notice,
161
162 #[strum(serialize = "info")]
163 Info,
164
165 #[strum(serialize = "debug")]
166 Debug,
167}
168
169#[derive(Debug)]
170pub enum EntryParsingError {
171 Completed,
172 EventTooOld,
173 Generic(String),
174}
175impl Error for EntryParsingError {}
176impl Display for EntryParsingError {
177 fn fmt(&self, f: &mut Formatter) -> FmtResult {
178 write!(
179 f,
180 "KMsgParsingError:: {}",
181 match self {
182 Self::Completed => "Completed Parsing",
183 Self::EventTooOld =>
184 "Event too old due to timestamp or sequence number (we've parsed newer messages than these)",
185 Self::Generic(s) => s,
186 }
187 )
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn test_serialize_to_klog() {
197 let entry_struct = Entry {
198 timestamp_from_system_start: Some(Duration::from_secs_f64(24241.325252)),
199 facility: Some(LogFacility::Kern),
200 level: Some(LogLevel::Info),
201 sequence_num: Some(10),
202 message: "Test message".to_owned(),
203 };
204 let expected_serialization = "<6>[ 24241.325252]Test message";
205
206 let boxed_entry_struct = Box::new(entry_struct.clone());
207
208 let printed_entry_struct = entry_struct.to_klog_str().unwrap();
209 assert_eq!(printed_entry_struct, expected_serialization);
210
211 let printed_boxed_entry_struct = boxed_entry_struct.to_klog_str().unwrap();
212 assert_eq!(printed_boxed_entry_struct, expected_serialization);
213 }
214
215 #[test]
216 fn test_serialize_to_kmsg() {
217 let entry_struct = Entry {
218 timestamp_from_system_start: Some(Duration::from_secs_f64(24241.325252)),
219 facility: Some(LogFacility::Kern),
220 level: Some(LogLevel::Info),
221 sequence_num: Some(23),
222 message: "Test message".to_owned(),
223 };
224 let expected_serialization = "6,23,24241325252,-;Test message";
225
226 let boxed_entry_struct = Box::new(entry_struct.clone());
227
228 let printed_entry_struct = entry_struct.to_kmsg_str().unwrap();
229 assert_eq!(printed_entry_struct, expected_serialization);
230
231 let printed_boxed_entry_struct = boxed_entry_struct.to_kmsg_str().unwrap();
232 assert_eq!(printed_boxed_entry_struct, expected_serialization);
233 }
234
235 #[test]
236 fn test_display() {
237 let entry_struct = Entry {
238 timestamp_from_system_start: Some(Duration::from_secs_f64(24241.325252)),
239 facility: Some(LogFacility::Kern),
240 level: Some(LogLevel::Info),
241 sequence_num: Some(15),
242 message: "Test message".to_owned(),
243 };
244 let expected_serialization = "[ 24241.325252] Test message";
245
246 let boxed_entry_struct = Box::new(entry_struct.clone());
247
248 let printed_entry_struct = format!("{}", entry_struct);
249 assert_eq!(printed_entry_struct, expected_serialization);
250
251 let printed_boxed_entry_struct = format!("{}", boxed_entry_struct);
252 assert_eq!(printed_boxed_entry_struct, expected_serialization);
253 }
254}