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::RefStr;
26
27#[derive(Clone, Debug)]
29pub struct Record<'a> {
30 now: SystemTime,
32
33 level: Level,
35 target: RefStr<'a>,
36 module_path: Option<RefStr<'a>>,
37 file: Option<RefStr<'a>>,
38 line: Option<u32>,
39 column: Option<u32>,
40
41 payload: fmt::Arguments<'a>,
43
44 kvs: KeyValues<'a>,
46}
47
48impl<'a> Record<'a> {
49 pub fn time(&self) -> SystemTime {
51 self.now
52 }
53
54 pub fn level(&self) -> Level {
58 self.level
59 }
60
61 pub fn target(&self) -> &'a str {
65 self.target.get()
66 }
67
68 pub fn target_static(&self) -> Option<&'static str> {
72 self.target.get_static()
73 }
74
75 pub fn module_path(&self) -> Option<&'a str> {
77 self.module_path.map(|s| s.get())
78 }
79
80 pub fn module_path_static(&self) -> Option<&'static str> {
82 self.module_path.and_then(|s| s.get_static())
83 }
84
85 pub fn file(&self) -> Option<&'a str> {
87 self.file.map(|s| s.get())
88 }
89
90 pub fn file_static(&self) -> Option<&'static str> {
92 self.file.and_then(|s| s.get_static())
93 }
94
95 pub fn filename(&self) -> Cow<'a, str> {
99 self.file()
100 .map(std::path::Path::new)
101 .and_then(std::path::Path::file_name)
102 .map(std::ffi::OsStr::to_string_lossy)
103 .unwrap_or_default()
104 }
105
106 pub fn line(&self) -> Option<u32> {
111 self.line
112 }
113
114 pub fn column(&self) -> Option<u32> {
118 self.column
119 }
120
121 pub fn payload(&self) -> fmt::Arguments<'a> {
123 self.payload
124 }
125
126 pub fn payload_static(&self) -> Option<&'static str> {
128 self.payload.as_str()
129 }
130
131 pub fn key_values(&self) -> &KeyValues<'a> {
133 &self.kvs
134 }
135
136 pub fn to_builder(&self) -> RecordBuilder<'a> {
138 RecordBuilder {
139 record: Record {
140 now: self.now,
141 level: self.level,
142 target: self.target,
143 module_path: self.module_path,
144 file: self.file,
145 line: self.line,
146 column: self.column,
147 payload: self.payload,
148 kvs: self.kvs,
149 },
150 }
151 }
152
153 pub fn to_owned(&self) -> RecordOwned {
155 RecordOwned {
156 now: self.now,
157 level: self.level,
158 target: self.target.into_cow_static(),
159 module_path: self.module_path.map(|m| m.into_cow_static()),
160 file: self.file.map(|f| f.into_cow_static()),
161 line: self.line,
162 column: self.column,
163 payload: if let Some(s) = self.payload.as_str() {
164 Cow::Borrowed(s)
165 } else {
166 Cow::Owned(self.payload.to_string())
167 },
168 kvs: self
169 .kvs
170 .iter()
171 .map(|(k, v)| (k.to_owned(), v.to_owned()))
172 .collect(),
173 }
174 }
175
176 pub fn builder() -> RecordBuilder<'a> {
178 RecordBuilder::default()
179 }
180}
181
182#[derive(Debug)]
184pub struct RecordBuilder<'a> {
185 record: Record<'a>,
186}
187
188impl Default for RecordBuilder<'_> {
189 fn default() -> Self {
190 RecordBuilder {
191 record: Record {
192 now: SystemTime::now(),
193 level: Level::Info,
194 target: RefStr::Static(""),
195 module_path: None,
196 file: None,
197 line: None,
198 column: None,
199 payload: format_args!(""),
200 kvs: KeyValues::empty(),
201 },
202 }
203 }
204}
205
206impl<'a> RecordBuilder<'a> {
207 pub fn payload(mut self, payload: fmt::Arguments<'a>) -> Self {
209 self.record.payload = payload;
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 column(mut self, column: Option<u32>) -> Self {
263 self.record.column = column;
264 self
265 }
266
267 pub fn key_values(mut self, kvs: impl Into<KeyValues<'a>>) -> Self {
269 self.record.kvs = kvs.into();
270 self
271 }
272
273 pub fn build(self) -> Record<'a> {
275 self.record
276 }
277}
278
279#[derive(Clone, Debug)]
281pub struct RecordOwned {
282 now: SystemTime,
284
285 level: Level,
287 target: Cow<'static, str>,
288 module_path: Option<Cow<'static, str>>,
289 file: Option<Cow<'static, str>>,
290 line: Option<u32>,
291 column: Option<u32>,
292
293 payload: Cow<'static, str>,
295
296 kvs: Vec<(kv::KeyOwned, kv::ValueOwned)>,
298}
299
300impl RecordOwned {
301 pub fn with(&self, f: impl FnOnce(Record<'_>)) {
303 f(Record {
304 now: self.now,
305 level: self.level,
306 target: match &self.target {
307 Cow::Borrowed(s) => RefStr::Static(s),
308 Cow::Owned(s) => RefStr::Borrowed(s.as_ref()),
309 },
310 module_path: match &self.module_path {
311 Some(Cow::Borrowed(s)) => Some(RefStr::Static(s)),
312 Some(Cow::Owned(s)) => Some(RefStr::Borrowed(s)),
313 None => None,
314 },
315 file: match &self.file {
316 Some(Cow::Borrowed(s)) => Some(RefStr::Static(s)),
317 Some(Cow::Owned(s)) => Some(RefStr::Borrowed(s)),
318 None => None,
319 },
320 line: self.line,
321 column: self.column,
322 payload: format_args!("{}", self.payload),
323 kvs: KeyValues::from(self.kvs.as_slice()),
324 });
325 }
326}
327
328#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
330pub struct FilterCriteria<'a> {
331 level: Level,
332 target: &'a str,
333}
334
335impl<'a> FilterCriteria<'a> {
336 pub fn level(&self) -> Level {
338 self.level
339 }
340
341 pub fn target(&self) -> &'a str {
343 self.target
344 }
345
346 pub fn to_builder(&self) -> FilterCriteriaBuilder<'a> {
348 FilterCriteriaBuilder {
349 metadata: FilterCriteria {
350 level: self.level,
351 target: self.target,
352 },
353 }
354 }
355
356 pub fn builder() -> FilterCriteriaBuilder<'a> {
358 FilterCriteriaBuilder::default()
359 }
360}
361
362#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
364pub struct FilterCriteriaBuilder<'a> {
365 metadata: FilterCriteria<'a>,
366}
367
368impl Default for FilterCriteriaBuilder<'_> {
369 fn default() -> Self {
370 FilterCriteriaBuilder {
371 metadata: FilterCriteria {
372 level: Level::Info,
373 target: "",
374 },
375 }
376 }
377}
378
379impl<'a> FilterCriteriaBuilder<'a> {
380 pub fn level(mut self, arg: Level) -> Self {
382 self.metadata.level = arg;
383 self
384 }
385
386 pub fn target(mut self, target: &'a str) -> Self {
388 self.metadata.target = target;
389 self
390 }
391
392 pub fn build(self) -> FilterCriteria<'a> {
394 self.metadata
395 }
396}
397
398#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
408#[repr(u8)]
409pub enum Level {
410 Trace = 1,
414 Trace2 = 2,
418 Trace3 = 3,
422 Trace4 = 4,
426 Debug = 5,
428 Debug2 = 6,
430 Debug3 = 7,
432 Debug4 = 8,
434 Info = 9,
438 Info2 = 10,
442 Info3 = 11,
446 Info4 = 12,
450 Warn = 13,
454 Warn2 = 14,
458 Warn3 = 15,
462 Warn4 = 16,
466 Error = 17,
470 Error2 = 18,
474 Error3 = 19,
478 Error4 = 20,
482 Fatal = 21,
484 Fatal2 = 22,
486 Fatal3 = 23,
488 Fatal4 = 24,
490}
491
492impl Level {
493 pub const fn name(&self) -> &'static str {
497 const LEVEL_NAMES: [&str; 24] = [
498 "TRACE", "TRACE2", "TRACE3", "TRACE4", "DEBUG", "DEBUG2", "DEBUG3", "DEBUG4", "INFO",
499 "INFO2", "INFO3", "INFO4", "WARN", "WARN2", "WARN3", "WARN4", "ERROR", "ERROR2",
500 "ERROR3", "ERROR4", "FATAL", "FATAL2", "FATAL3", "FATAL4",
501 ];
502 LEVEL_NAMES[*self as usize - 1]
503 }
504}
505
506impl fmt::Debug for Level {
507 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
508 f.pad(self.name())
509 }
510}
511
512impl fmt::Display for Level {
513 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
514 f.pad(self.name())
515 }
516}
517
518#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
520pub enum LevelFilter {
521 Off,
523 Equal(Level),
525 NotEqual(Level),
527 MoreSevere(Level),
529 MoreSevereEqual(Level),
532 MoreVerbose(Level),
534 MoreVerboseEqual(Level),
537 All,
539}
540
541impl LevelFilter {
542 pub fn test(&self, level: Level) -> bool {
558 match self {
559 LevelFilter::Off => false,
560 LevelFilter::Equal(l) => level == *l,
561 LevelFilter::NotEqual(l) => level != *l,
562 LevelFilter::MoreSevere(l) => level > *l,
563 LevelFilter::MoreSevereEqual(l) => level >= *l,
564 LevelFilter::MoreVerbose(l) => level < *l,
565 LevelFilter::MoreVerboseEqual(l) => level <= *l,
566 LevelFilter::All => true,
567 }
568 }
569}
570
571impl FromStr for Level {
572 type Err = Error;
573 fn from_str(s: &str) -> Result<Level, Self::Err> {
574 for (repr, level) in [
575 ("fatal", Level::Fatal),
577 ("error", Level::Error),
578 ("warn", Level::Warn),
579 ("info", Level::Info),
580 ("debug", Level::Debug),
581 ("trace", Level::Trace),
582 ("fatal2", Level::Fatal2),
584 ("fatal3", Level::Fatal3),
585 ("fatal4", Level::Fatal4),
586 ("error2", Level::Error2),
587 ("error3", Level::Error3),
588 ("error4", Level::Error4),
589 ("warn2", Level::Warn2),
590 ("warn3", Level::Warn3),
591 ("warn4", Level::Warn4),
592 ("info2", Level::Info2),
593 ("info3", Level::Info3),
594 ("info4", Level::Info4),
595 ("debug2", Level::Debug2),
596 ("debug3", Level::Debug3),
597 ("debug4", Level::Debug4),
598 ("trace2", Level::Trace2),
599 ("trace3", Level::Trace3),
600 ("trace4", Level::Trace4),
601 ] {
602 if s.eq_ignore_ascii_case(repr) {
603 return Ok(level);
604 }
605 }
606
607 Err(Error::new(format!("malformed level: {s:?}")))
608 }
609}
610
611#[cfg(test)]
612mod tests {
613 use super::*;
614
615 #[test]
616 fn round_trip_level() {
617 let levels = [
618 Level::Trace,
619 Level::Trace2,
620 Level::Trace3,
621 Level::Trace4,
622 Level::Debug,
623 Level::Debug2,
624 Level::Debug3,
625 Level::Debug4,
626 Level::Info,
627 Level::Info2,
628 Level::Info3,
629 Level::Info4,
630 Level::Warn,
631 Level::Warn2,
632 Level::Warn3,
633 Level::Warn4,
634 Level::Error,
635 Level::Error2,
636 Level::Error3,
637 Level::Error4,
638 Level::Fatal,
639 Level::Fatal2,
640 Level::Fatal3,
641 Level::Fatal4,
642 ];
643
644 for &level in &levels {
645 let s = level.name();
646 let parsed = s.parse::<Level>().unwrap();
647 assert_eq!(level, parsed);
648 }
649 }
650}