stdweb_logger/
lib.rs

1use log::*;
2use std::fmt::Write;
3use stdweb::js;
4
5struct Logger {
6    filter: LevelFilter,
7    format: Box<dyn Fn(&mut String, &Record) -> std::fmt::Result + Send + Sync + 'static>,
8}
9
10impl Logger {
11    fn new() -> Self {
12        Self {
13            filter: LevelFilter::Trace,
14            format: Box::new(|s, r| write!(s, "{}: {}", r.level(), r.args())),
15        }
16    }
17}
18
19///
20/// Builder object to build a logger
21///
22pub struct Builder {
23    logger: Logger,
24}
25
26impl Builder {
27    fn new() -> Self {
28        Self {
29            logger: Logger::new(),
30        }
31    }
32
33    ///
34    /// Set a function which is called every time logger formats strings
35    ///
36    pub fn format(
37        mut self,
38        fmt: impl Fn(&mut String, &Record) -> std::fmt::Result + Send + Sync + 'static,
39    ) -> Self {
40        self.logger.format = Box::new(fmt);
41        self
42    }
43
44    ///
45    /// Set log level filter
46    ///
47    pub fn filter(mut self, filter: LevelFilter) -> Self {
48        self.logger.filter = filter;
49        self
50    }
51
52    ///
53    /// Show more detail (line numbers, file names etc.) in log
54    ///
55    pub fn detail(mut self) -> Self {
56        self.logger.format = Box::new(|s, r| {
57            write!(
58                s,
59                "{}: {} ({}({}))",
60                r.level(),
61                r.args(),
62                r.file().unwrap_or("<unknown>"),
63                r.line().unwrap_or(0),
64            )
65        });
66        self
67    }
68
69    ///
70    /// Sets the logger
71    ///
72    pub fn build(self) {
73        let level = self.logger.filter.clone();
74        if set_boxed_logger(Box::new(self.logger)).is_ok() {
75            set_max_level(level);
76        }
77    }
78}
79
80impl log::Log for Logger {
81    fn enabled(&self, m: &Metadata) -> bool {
82        match self.filter.to_level() {
83            Some(level) => m.level() <= level,
84            None => false,
85        }
86    }
87
88    fn log(&self, record: &Record) {
89        let mut s = String::new();
90
91        if let Err(e) = (self.format)(&mut s, record) {
92            js! { @(no_return) console.error(@{e.to_string()}); }
93        } else {
94            match record.level() {
95                Level::Error => js! { @(no_return) console.error(@{s}); },
96                Level::Warn => js! { @(no_return) console.warn(@{s}); },
97                Level::Info => js! { @(no_return) console.info(@{s}); },
98                Level::Debug => js! { @(no_return) console.debug(@{s}); },
99                Level::Trace => js! { @(no_return) console.trace(@{s}); },
100            }
101        }
102    }
103
104    fn flush(&self) {}
105}
106
107///
108/// Initialize logger with default settings
109///
110pub fn init() {
111    builder().build()
112}
113
114///
115/// Create a builder for a logger
116///
117pub fn builder() -> Builder {
118    Builder::new()
119}
120
121///
122/// Initialize logger with specified log level
123///
124pub fn init_with_level(level: Level) {
125    builder().filter(level.to_level_filter()).build()
126}