jlogger_tracing/
lib.rs

1//! A simple log utility.
2
3use std::fs;
4use tracing_subscriber::filter::LevelFilter as TraceLevelFilter;
5use tracing_subscriber::prelude::*;
6use tracing_subscriber::EnvFilter;
7use tracing_subscriber::Layer;
8
9pub use tracing::debug as jdebug;
10pub use tracing::error as jerror;
11pub use tracing::info as jinfo;
12pub use tracing::trace as jtrace;
13pub use tracing::warn as jwarn;
14pub use tracing_subscriber::fmt::format::FmtSpan;
15
16mod timer;
17use timer::JloggerTimer;
18pub use timer::LogTimeFormat;
19
20mod level;
21pub use level::LevelFilter;
22
23use tracing_subscriber::prelude::__tracing_subscriber_SubscriberExt;
24
25pub struct JloggerBuilder<'a> {
26    max_level: TraceLevelFilter,
27    log_console: bool,
28    log_file: Option<&'a str>,
29    log_file_append: bool,
30    log_runtime: bool,
31    time_format: LogTimeFormat,
32    span_event: FmtSpan,
33}
34
35impl<'a> Default for JloggerBuilder<'a> {
36    fn default() -> Self {
37        JloggerBuilder::new()
38    }
39}
40
41impl<'a> JloggerBuilder<'a> {
42    /// Create a new JloggerBuilder which is used to build a Jlogger.
43    ///
44    /// # Examples
45    /// ```
46    ///     use jlogger_tracing::{JloggerBuilder, LogTimeFormat, LevelFilter};
47    ///
48    ///     JloggerBuilder::new()
49    ///        .max_level(LevelFilter::DEBUG)
50    ///        .log_console(true)
51    ///        .log_time(LogTimeFormat::TimeStamp)
52    ///        .log_file(Some(("/tmp/my_log.log", false)))
53    ///        .build();
54    ///
55    /// ```
56    pub fn new() -> Self {
57        JloggerBuilder {
58            max_level: TraceLevelFilter::INFO,
59            log_console: true,
60            log_file: None,
61            log_file_append: true,
62            log_runtime: false,
63            time_format: LogTimeFormat::TimeNone,
64            span_event: FmtSpan::NONE,
65        }
66    }
67
68    /// Set the max level to be outputted.
69    /// Log messages with a level below it will not be outputted.
70    /// At runtime, the log level can be filtered though "JLOGGER_LEVEL" environment variable.
71    pub fn max_level(mut self, max_level: LevelFilter) -> Self {
72        self.max_level = max_level.into();
73        self
74    }
75
76    /// If enabled, log message will be printed to the console.
77    /// Default is true.
78    pub fn log_console(mut self, log_console: bool) -> Self {
79        self.log_console = log_console;
80        self
81    }
82
83    /// Log file name.
84    /// If specified, log message will be outputted to it.
85    /// A tuple (log_file: &str, append: bool) is used to specify the log file.
86    /// if "append" is true and the log file already exists, the log message will be appended to
87    /// the log file. Otherwise a new log file will be created.
88    pub fn log_file(mut self, log_file: Option<(&'a str, bool)>) -> Self {
89        if let Some((log_file, append)) = log_file {
90            self.log_file = Some(log_file);
91            self.log_file_append = append;
92        }
93
94        self
95    }
96
97    /// Add runtime information to log message.
98    /// If the current thread name is set, it will be used as runtime information, otherwise
99    /// process name is used
100    ///
101    /// >DEBUG thread1 : logging from thread thread1.  
102    /// >DEBUG jlogger-cac0970c6f073082 : logging from a thread whose name is not set.
103    ///
104    ///
105    pub fn log_runtime(mut self, log_runtime: bool) -> Self {
106        self.log_runtime = log_runtime;
107        self
108    }
109
110    /// Time stamp string format, only take effect when time stamp is enable in the log.
111    /// * TimeStamp  
112    /// Timestamp (from system boot) will be outputted in the log message.
113    /// > 9080.163365118 DEBUG test_debug_macro : src/lib.rs-364 : this is debug  
114    /// > 9083.164066687 INFO  test_debug_macro : this is info
115    /// * TimeLocal  
116    /// Date and time are printed in the log message.  
117    /// > 2022-05-17 13:00:03 DEBUG : src/lib.rs-363 : this is debug  
118    /// > 2022-05-17 13:00:06 INFO  : this is info
119    /// * TimeNone
120    /// No timestamp included in the log message.
121    pub fn log_time(mut self, time_format: LogTimeFormat) -> Self {
122        self.time_format = time_format;
123        self
124    }
125
126    /// Enable tracing span event.
127    /// See tracing_subscriber::fmt::format::FmtSpan for detail
128    /// This only take affect when tracing::Span is used.
129    pub fn tracing_span_event(mut self, span: FmtSpan) -> Self {
130        self.span_event = span;
131        self
132    }
133
134    /// Build a Jlogger.
135    pub fn build(self) {
136        let timer = JloggerTimer::new(self.time_format);
137
138        let mut layers = Vec::new();
139        if self.log_console {
140            layers.push(
141                tracing_subscriber::fmt::layer()
142                    .with_writer(std::io::stderr)
143                    .with_timer(timer)
144                    .with_target(self.log_runtime)
145                    .with_span_events(self.span_event.clone())
146                    .boxed(),
147            );
148        }
149
150        if let Some(log) = &self.log_file {
151            if !self.log_file_append {
152                let _ = fs::remove_file(log);
153            }
154
155            let writer = fs::OpenOptions::new()
156                .create(true)
157                .write(true)
158                .append(true)
159                .read(true)
160                .open(log)
161                .unwrap();
162
163            layers.push(
164                tracing_subscriber::fmt::layer()
165                    .with_writer(writer)
166                    .with_timer(timer)
167                    .with_target(self.log_runtime)
168                    .with_span_events(self.span_event.clone())
169                    .boxed(),
170            );
171        }
172
173        let env_filter = EnvFilter::builder()
174            .with_default_directive(self.max_level.into())
175            .with_env_var("JLOGGER")
176            .from_env_lossy();
177
178        tracing_subscriber::registry()
179            .with(layers)
180            .with(env_filter)
181            .init();
182    }
183}
184
185#[test]
186fn test_debug_macro() {
187    use tracing::{debug, error, info};
188
189    JloggerBuilder::new()
190        .max_level(LevelFilter::TRACE)
191        .log_console(true)
192        .log_runtime(true)
193        .log_time(LogTimeFormat::TimeLocal)
194        .log_file(Some(("/tmp/abc", false)))
195        .build();
196
197    jdebug!("{} - {} test: {}", file!(), line!(), String::from("hello"));
198    debug!("this is debug");
199
200    std::thread::Builder::new()
201        .name("thread1".to_string())
202        .spawn(|| {
203            debug!(
204                "this is debug in the thread {}.",
205                std::thread::current().name().unwrap()
206            );
207            info!("this is info in the thread.");
208        })
209        .unwrap()
210        .join()
211        .unwrap();
212
213    jerror!(file = file!(), l = line!(), msg = "this is error");
214    error!(file = file!(), l = line!(), msg = "this is another error");
215    jinfo!("this is info");
216    std::thread::spawn(|| {
217        debug!(
218            "this is debug in the thread {}.",
219            std::thread::current()
220                .name()
221                .unwrap_or("No thread name set"),
222        );
223        info!("this is info in the thread.");
224    })
225    .join()
226    .unwrap();
227    info!("this is info");
228    debug!("default");
229}