minifemme/
ndjson.rs

1//! Print logs as ndjson.
2
3use log::{kv, LevelFilter, Log, Metadata, Record};
4use std::io::{self, StdoutLock, Write};
5use std::time;
6
7/// Start logging.
8pub(crate) fn start(level: LevelFilter) {
9    let logger = Box::new(Logger {});
10    log::set_boxed_logger(logger).expect("Could not start logging");
11    log::set_max_level(level);
12}
13
14#[derive(Debug)]
15pub(crate) struct Logger {}
16
17impl Log for Logger {
18    fn enabled(&self, metadata: &Metadata<'_>) -> bool {
19        metadata.level() <= log::max_level()
20    }
21
22    fn log(&self, record: &Record<'_>) {
23        if self.enabled(record.metadata()) {
24            let stdout = io::stdout();
25            let mut handle = stdout.lock();
26            let level = get_level(record.level());
27            let time = time::UNIX_EPOCH.elapsed().unwrap().as_millis();
28            write!(&mut handle, "{{\"level\":{},\"time\":{},\"msg\":", level, time).unwrap();
29            serde_json::to_writer(&mut handle, record.args()).unwrap();
30            format_kv_pairs(&mut handle, &record);
31            writeln!(&mut handle, "}}").unwrap();
32        }
33    }
34    fn flush(&self) {}
35}
36
37fn get_level(level: log::Level) -> u8 {
38    use log::Level::*;
39    match level {
40        Trace => 10,
41        Debug => 20,
42        Info => 30,
43        Warn => 40,
44        Error => 50,
45    }
46}
47
48fn format_kv_pairs<'b>(mut out: &mut StdoutLock<'b>, record: &Record) {
49    struct Visitor<'a, 'b> {
50        string: &'a mut StdoutLock<'b>,
51    }
52
53    impl<'kvs, 'a, 'b> kv::Visitor<'kvs> for Visitor<'a, 'b> {
54        fn visit_pair(
55            &mut self,
56            key: kv::Key<'kvs>,
57            val: kv::Value<'kvs>,
58        ) -> Result<(), kv::Error> {
59            write!(self.string, ",\"{}\":\"{}\"", key, val)?;
60            Ok(())
61        }
62    }
63
64    let mut visitor = Visitor { string: &mut out };
65    record.key_values().visit(&mut visitor).unwrap();
66}