1use std::borrow::Cow;
18use std::fmt;
19use std::str::FromStr;
20use std::time::SystemTime;
21
22use crate::Error;
23use crate::kv;
24use crate::kv::KeyValues;
25use crate::str::OwnedStr;
26use crate::str::RefStr;
27
28#[derive(Clone, Debug)]
30pub struct Record<'a> {
31 now: SystemTime,
33
34 level: Level,
36 target: RefStr<'a>,
37 module_path: Option<RefStr<'a>>,
38 file: Option<RefStr<'a>>,
39 line: Option<u32>,
40 column: Option<u32>,
41
42 payload: OwnedStr,
44
45 kvs: KeyValues<'a>,
47}
48
49impl<'a> Record<'a> {
50 pub fn time(&self) -> SystemTime {
52 self.now
53 }
54
55 pub fn level(&self) -> Level {
59 self.level
60 }
61
62 pub fn target(&self) -> &'a str {
66 self.target.get()
67 }
68
69 pub fn target_static(&self) -> Option<&'a str> {
73 self.target.get_static()
74 }
75
76 pub fn module_path(&self) -> Option<&'a str> {
78 self.module_path.map(|s| s.get())
79 }
80
81 pub fn module_path_static(&self) -> Option<&'static str> {
83 self.module_path.and_then(|s| s.get_static())
84 }
85
86 pub fn file(&self) -> Option<&'a str> {
88 self.file.map(|s| s.get())
89 }
90
91 pub fn file_static(&self) -> Option<&'static str> {
93 self.file.and_then(|s| s.get_static())
94 }
95
96 pub fn filename(&self) -> Cow<'a, str> {
100 self.file()
101 .map(std::path::Path::new)
102 .and_then(std::path::Path::file_name)
103 .map(std::ffi::OsStr::to_string_lossy)
104 .unwrap_or_default()
105 }
106
107 pub fn line(&self) -> Option<u32> {
112 self.line
113 }
114
115 pub fn column(&self) -> Option<u32> {
119 self.column
120 }
121
122 pub fn payload(&self) -> &str {
124 self.payload.get()
125 }
126
127 pub fn payload_static(&self) -> Option<&'static str> {
129 self.payload.get_static()
130 }
131
132 pub fn key_values(&self) -> &KeyValues<'a> {
134 &self.kvs
135 }
136
137 pub fn to_owned(&self) -> RecordOwned {
139 RecordOwned {
140 now: self.now,
141 level: self.level,
142 target: self.target.into_owned(),
143 module_path: self.module_path.map(RefStr::into_owned),
144 file: self.file.map(RefStr::into_owned),
145 line: self.line,
146 column: self.column,
147 payload: self.payload.clone(),
148 kvs: self
149 .kvs
150 .iter()
151 .map(|(k, v)| (k.to_owned(), v.to_owned()))
152 .collect(),
153 }
154 }
155
156 pub fn to_builder(&self) -> RecordBuilder<'a> {
158 RecordBuilder {
159 record: Record {
160 now: self.now,
161 level: self.level,
162 target: self.target,
163 module_path: self.module_path,
164 file: self.file,
165 line: self.line,
166 column: self.column,
167 payload: self.payload.clone(),
168 kvs: self.kvs.clone(),
169 },
170 }
171 }
172
173 pub fn builder() -> RecordBuilder<'a> {
175 RecordBuilder::default()
176 }
177}
178
179#[derive(Debug)]
181pub struct RecordBuilder<'a> {
182 record: Record<'a>,
183}
184
185impl Default for RecordBuilder<'_> {
186 fn default() -> Self {
187 RecordBuilder {
188 record: Record {
189 now: SystemTime::now(),
190 level: Level::Info,
191 target: RefStr::Static(""),
192 module_path: None,
193 file: None,
194 line: None,
195 column: None,
196 payload: OwnedStr::Static(""),
197 kvs: Default::default(),
198 },
199 }
200 }
201}
202
203impl<'a> RecordBuilder<'a> {
204 pub fn payload(mut self, payload: impl Into<Cow<'static, str>>) -> Self {
206 self.record.payload = match payload.into() {
207 Cow::Borrowed(s) => OwnedStr::Static(s),
208 Cow::Owned(s) => OwnedStr::Owned(s.into_boxed_str()),
209 };
210 self
211 }
212
213 pub fn level(mut self, level: Level) -> Self {
215 self.record.level = level;
216 self
217 }
218
219 pub fn target(mut self, target: &'a str) -> Self {
221 self.record.target = RefStr::Borrowed(target);
222 self
223 }
224
225 pub fn target_static(mut self, target: &'static str) -> Self {
227 self.record.target = RefStr::Static(target);
228 self
229 }
230
231 pub fn module_path(mut self, path: Option<&'a str>) -> Self {
233 self.record.module_path = path.map(RefStr::Borrowed);
234 self
235 }
236
237 pub fn module_path_static(mut self, path: &'static str) -> Self {
239 self.record.module_path = Some(RefStr::Static(path));
240 self
241 }
242
243 pub fn file(mut self, file: Option<&'a str>) -> Self {
245 self.record.file = file.map(RefStr::Borrowed);
246 self
247 }
248
249 pub fn file_static(mut self, file: &'static str) -> Self {
251 self.record.file = Some(RefStr::Static(file));
252 self
253 }
254
255 pub fn line(mut self, line: Option<u32>) -> Self {
257 self.record.line = line;
258 self
259 }
260
261 pub fn key_values(mut self, kvs: impl Into<KeyValues<'a>>) -> Self {
263 self.record.kvs = kvs.into();
264 self
265 }
266
267 pub fn build(self) -> Record<'a> {
269 self.record
270 }
271}
272
273#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
275pub struct FilterCriteria<'a> {
276 level: Level,
277 target: &'a str,
278}
279
280impl<'a> FilterCriteria<'a> {
281 pub fn level(&self) -> Level {
283 self.level
284 }
285
286 pub fn target(&self) -> &'a str {
288 self.target
289 }
290
291 pub fn to_builder(&self) -> FilterCriteriaBuilder<'a> {
293 FilterCriteriaBuilder {
294 metadata: FilterCriteria {
295 level: self.level,
296 target: self.target,
297 },
298 }
299 }
300
301 pub fn builder() -> FilterCriteriaBuilder<'a> {
303 FilterCriteriaBuilder::default()
304 }
305}
306
307#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
309pub struct FilterCriteriaBuilder<'a> {
310 metadata: FilterCriteria<'a>,
311}
312
313impl Default for FilterCriteriaBuilder<'_> {
314 fn default() -> Self {
315 FilterCriteriaBuilder {
316 metadata: FilterCriteria {
317 level: Level::Info,
318 target: "",
319 },
320 }
321 }
322}
323
324impl<'a> FilterCriteriaBuilder<'a> {
325 pub fn level(mut self, arg: Level) -> Self {
327 self.metadata.level = arg;
328 self
329 }
330
331 pub fn target(mut self, target: &'a str) -> Self {
333 self.metadata.target = target;
334 self
335 }
336
337 pub fn build(self) -> FilterCriteria<'a> {
339 self.metadata
340 }
341}
342
343#[derive(Clone, Debug)]
345pub struct RecordOwned {
346 now: SystemTime,
348
349 level: Level,
351 target: OwnedStr,
352 module_path: Option<OwnedStr>,
353 file: Option<OwnedStr>,
354 line: Option<u32>,
355 column: Option<u32>,
356
357 payload: OwnedStr,
359
360 kvs: Vec<(kv::KeyOwned, kv::ValueOwned)>,
362}
363
364impl RecordOwned {
365 pub fn as_record(&self) -> Record<'_> {
367 Record {
368 now: self.now,
369 level: self.level,
370 target: self.target.by_ref(),
371 module_path: self.module_path.as_ref().map(OwnedStr::by_ref),
372 file: self.file.as_ref().map(OwnedStr::by_ref),
373 line: self.line,
374 column: self.column,
375 payload: self.payload.clone(),
376 kvs: KeyValues::from(self.kvs.as_slice()),
377 }
378 }
379}
380
381#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
391#[repr(u8)]
392pub enum Level {
393 Trace = 1,
397 Trace2 = 2,
401 Trace3 = 3,
405 Trace4 = 4,
409 Debug = 5,
411 Debug2 = 6,
413 Debug3 = 7,
415 Debug4 = 8,
417 Info = 9,
421 Info2 = 10,
425 Info3 = 11,
429 Info4 = 12,
433 Warn = 13,
437 Warn2 = 14,
441 Warn3 = 15,
445 Warn4 = 16,
449 Error = 17,
453 Error2 = 18,
457 Error3 = 19,
461 Error4 = 20,
465 Fatal = 21,
467 Fatal2 = 22,
469 Fatal3 = 23,
471 Fatal4 = 24,
473}
474
475impl Level {
476 pub const fn name(&self) -> &'static str {
480 const LEVEL_NAMES: [&str; 24] = [
481 "TRACE", "TRACE2", "TRACE3", "TRACE4", "DEBUG", "DEBUG2", "DEBUG3", "DEBUG4", "INFO",
482 "INFO2", "INFO3", "INFO4", "WARN", "WARN2", "WARN3", "WARN4", "ERROR", "ERROR2",
483 "ERROR3", "ERROR4", "FATAL", "FATAL2", "FATAL3", "FATAL4",
484 ];
485 LEVEL_NAMES[*self as usize - 1]
486 }
487}
488
489impl fmt::Debug for Level {
490 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
491 f.pad(self.name())
492 }
493}
494
495impl fmt::Display for Level {
496 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
497 f.pad(self.name())
498 }
499}
500
501#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
503pub enum LevelFilter {
504 Off,
506 Equal(Level),
508 NotEqual(Level),
510 MoreSevere(Level),
512 MoreSevereEqual(Level),
515 MoreVerbose(Level),
517 MoreVerboseEqual(Level),
520 All,
522}
523
524impl LevelFilter {
525 pub fn test(&self, level: Level) -> bool {
541 match self {
542 LevelFilter::Off => false,
543 LevelFilter::Equal(l) => level == *l,
544 LevelFilter::NotEqual(l) => level != *l,
545 LevelFilter::MoreSevere(l) => level > *l,
546 LevelFilter::MoreSevereEqual(l) => level >= *l,
547 LevelFilter::MoreVerbose(l) => level < *l,
548 LevelFilter::MoreVerboseEqual(l) => level <= *l,
549 LevelFilter::All => true,
550 }
551 }
552}
553
554impl FromStr for Level {
555 type Err = Error;
556 fn from_str(s: &str) -> Result<Level, Self::Err> {
557 for (repr, level) in [
558 ("fatal", Level::Fatal),
560 ("error", Level::Error),
561 ("warn", Level::Warn),
562 ("info", Level::Info),
563 ("debug", Level::Debug),
564 ("trace", Level::Trace),
565 ("fatal2", Level::Fatal2),
567 ("fatal3", Level::Fatal3),
568 ("fatal4", Level::Fatal4),
569 ("error2", Level::Error2),
570 ("error3", Level::Error3),
571 ("error4", Level::Error4),
572 ("warn2", Level::Warn2),
573 ("warn3", Level::Warn3),
574 ("warn4", Level::Warn4),
575 ("info2", Level::Info2),
576 ("info3", Level::Info3),
577 ("info4", Level::Info4),
578 ("debug2", Level::Debug2),
579 ("debug3", Level::Debug3),
580 ("debug4", Level::Debug4),
581 ("trace2", Level::Trace2),
582 ("trace3", Level::Trace3),
583 ("trace4", Level::Trace4),
584 ] {
585 if s.eq_ignore_ascii_case(repr) {
586 return Ok(level);
587 }
588 }
589
590 Err(Error::new(format!("malformed level: {s:?}")))
591 }
592}
593
594#[cfg(test)]
595mod tests {
596 use super::*;
597
598 #[test]
599 fn round_trip_level() {
600 let levels = [
601 Level::Trace,
602 Level::Trace2,
603 Level::Trace3,
604 Level::Trace4,
605 Level::Debug,
606 Level::Debug2,
607 Level::Debug3,
608 Level::Debug4,
609 Level::Info,
610 Level::Info2,
611 Level::Info3,
612 Level::Info4,
613 Level::Warn,
614 Level::Warn2,
615 Level::Warn3,
616 Level::Warn4,
617 Level::Error,
618 Level::Error2,
619 Level::Error3,
620 Level::Error4,
621 Level::Fatal,
622 Level::Fatal2,
623 Level::Fatal3,
624 Level::Fatal4,
625 ];
626
627 for &level in &levels {
628 let s = level.name();
629 let parsed = s.parse::<Level>().unwrap();
630 assert_eq!(level, parsed);
631 }
632 }
633}