1use 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 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 pub fn max_level(mut self, max_level: LevelFilter) -> Self {
72 self.max_level = max_level.into();
73 self
74 }
75
76 pub fn log_console(mut self, log_console: bool) -> Self {
79 self.log_console = log_console;
80 self
81 }
82
83 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 pub fn log_runtime(mut self, log_runtime: bool) -> Self {
106 self.log_runtime = log_runtime;
107 self
108 }
109
110 pub fn log_time(mut self, time_format: LogTimeFormat) -> Self {
122 self.time_format = time_format;
123 self
124 }
125
126 pub fn tracing_span_event(mut self, span: FmtSpan) -> Self {
130 self.span_event = span;
131 self
132 }
133
134 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}