1use chrono::Local;
36use colored::*;
37use log::{Log, Metadata, Record};
38use regex::Regex;
39use std::env;
40use std::fs::File;
41#[cfg(feature = "config")]
42use std::fs::OpenOptions;
43use std::io::{stderr, stdout, BufWriter, Write};
44use std::mem;
45use std::sync::{Arc, Mutex, Once};
46
47mod error;
49
50use error::{Error, ErrorKind, Result};
51use std::path::Path;
52
53#[cfg(feature = "config")]
54pub mod config;
55
56#[cfg(feature = "config")]
57pub use config::LogConfig;
58
59#[cfg(feature = "config")]
60pub use config::LogConfigBuilder;
61
62mod logger_params;
63
64pub use logger_params::LogDestination;
65use logger_params::LoggerParams;
66
67pub(crate) const DEFAULT_LOG_LEVEL: Level = Level::Info;
68
69pub(crate) const DEFAULT_LOG_DEST: LogDestination = LogDestination::Stderr;
71
72pub const NO_STREAM: Option<Box<dyn 'static + Write + Send>> = None;
73
74use crate::error::ToError;
75pub use log::Level;
76
77#[derive(Clone)]
85pub struct Logger {
86 inner: Arc<Mutex<LoggerParams>>,
87 module_re: Regex,
88 exe_name: Option<String>,
89}
90
91impl Logger {
92 fn new() -> Logger {
96 static mut LOGGER: *const Logger = 0 as *const Logger;
97 static ONCE: Once = Once::new();
98
99 let exe_name = match env::current_exe() {
102 Ok(exe_name) => match exe_name.file_name() {
103 Some(exe_name) => exe_name
104 .to_str()
105 .map(|name| name.to_owned().replace('-', "_")),
106 None => None,
107 },
108 Err(_why) => None,
109 };
110
111 let logger = unsafe {
112 ONCE.call_once(|| {
113 let singleton = Logger {
114 module_re: Regex::new(r#"^([^:]+)::(.*)$"#).unwrap(),
115 inner: Arc::new(Mutex::new(LoggerParams::new(DEFAULT_LOG_LEVEL))),
116 exe_name,
117 };
118
119 LOGGER = mem::transmute(Box::new(singleton));
121 });
122
123 (*LOGGER).clone()
124 };
125
126 if !logger.inner.lock().unwrap().initialised() {
128 #[cfg(feature = "config")]
131 if let Ok(config_path) = env::var("LOG_CONFIG") {
132 match LogConfigBuilder::from_file(&config_path) {
134 Ok(ref log_config) => match logger.int_set_log_config(log_config.build()) {
135 Ok(_res) => (),
136 Err(why) => {
137 eprintln!(
138 "Failed to apply log config from file: '{}', error: {:?}",
139 config_path, why
140 );
141 }
142 },
143 Err(why) => {
144 eprintln!(
145 "Failed to read log config from file: '{}', error: {:?}",
146 config_path, why
147 );
148 }
149 }
150 }
151
152 match log::set_boxed_logger(Box::new(logger.clone())) {
155 Ok(_dummy) => (),
156 Err(why) => {
157 dbg!(why);
158 }
159 }
160
161 log::set_max_level(logger.inner.lock().unwrap().max_level().to_level_filter());
162 }
163
164 logger
167 }
168
169 pub fn flush() {
171 Logger::new().flush();
172 }
173
174 pub fn create() {
176 let _logger = Logger::new();
177 }
178
179 pub fn set_default_level(log_level: Level) {
182 let logger = Logger::new();
183 let mut guarded_params = logger.inner.lock().unwrap();
184 let last_max_level = *guarded_params.max_level();
185 let max_level = guarded_params.set_default_level(log_level);
186
187 if last_max_level != max_level {
188 log::set_max_level(max_level.to_level_filter());
189 }
190 }
191
192 pub fn get_default_level(&self) -> Level {
194 let guarded_params = self.inner.lock().unwrap();
195 guarded_params.get_default_level()
196 }
197
198 pub fn set_mod_level(module: &str, log_level: Level) {
200 let logger = Logger::new();
201 let mut guarded_params = logger.inner.lock().unwrap();
202 let last_max_level = *guarded_params.max_level();
203 let max_level = guarded_params.set_mod_level(module, log_level);
204 if last_max_level != *max_level {
205 log::set_max_level(max_level.to_level_filter());
206 }
207 }
208
209 pub fn get_buffer() -> Option<Vec<u8>> {
211 let logger = Logger::new();
212 let mut guarded_params = logger.inner.lock().unwrap();
213 guarded_params.retrieve_log_buffer()
214 }
215
216 pub fn set_log_dest<S: 'static + Write + Send>(
218 dest: &LogDestination,
219 stream: Option<S>,
220 ) -> Result<()> {
221 let logger = Logger::new();
222 logger.flush();
223 let mut guarded_params = logger.inner.lock().unwrap();
224 guarded_params.set_log_dest(dest, stream)
225 }
226
227 pub fn set_log_file(log_dest: &LogDestination, log_file: &Path, buffered: bool) -> Result<()> {
229 let dest = if log_dest.is_stdout() {
230 LogDestination::StreamStdout
231 } else if log_dest.is_stderr() {
232 LogDestination::StreamStderr
233 } else {
234 LogDestination::Stream
235 };
236
237 let mut stream: Box<dyn Write + Send> = if buffered {
238 Box::new(BufWriter::new(
239 File::create(log_file).upstream_with_context(&format!(
240 "Failed to create file: '{}'",
241 log_file.display()
242 ))?,
243 ))
244 } else {
245 Box::new(File::create(log_file).upstream_with_context(&format!(
246 "Failed to create file: '{}'",
247 log_file.display()
248 ))?)
249 };
250
251 let logger = Logger::new();
252 logger.flush();
253
254 let mut guarded_params = logger.inner.lock().unwrap();
255 let buffer = guarded_params.retrieve_log_buffer();
256
257 if let Some(buffer) = buffer {
258 stream
259 .write_all(buffer.as_slice())
260 .upstream_with_context(&format!(
261 "Failed to write buffers to file: '{}'",
262 log_file.display()
263 ))?;
264 stream.flush().upstream_with_context(&format!(
265 "Failed to flush buffers to file: '{}'",
266 log_file.display()
267 ))?;
268 }
269
270 guarded_params.set_log_dest(&dest, Some(stream))
271 }
272
273 pub fn get_log_dest() -> LogDestination {
275 let logger = Logger::new();
276 let guarded_params = logger.inner.lock().unwrap();
277 guarded_params.get_log_dest().clone()
278 }
279
280 #[cfg(feature = "config")]
282 pub fn set_log_config(log_config: &LogConfig) -> Result<()> {
283 Logger::new().int_set_log_config(log_config)
284 }
285
286 pub fn set_color(color: bool) {
288 let logger = Logger::new();
289 let mut guarded_params = logger.inner.lock().unwrap();
290 guarded_params.set_color(color)
291 }
292
293 pub fn set_timestamp(val: bool) {
295 let logger = Logger::new();
296 let mut guarded_params = logger.inner.lock().unwrap();
297 guarded_params.set_timestamp(val)
298 }
299
300 pub fn set_millis(val: bool) {
302 let logger = Logger::new();
303 let mut guarded_params = logger.inner.lock().unwrap();
304 guarded_params.set_millis(val)
305 }
306
307 pub fn set_brief_info(val: bool) {
309 let logger = Logger::new();
310 let mut guarded_params = logger.inner.lock().unwrap();
311 guarded_params.set_brief_info(val)
312 }
313
314 #[cfg(feature = "config")]
315 fn int_set_log_config(&self, log_config: &LogConfig) -> Result<()> {
316 let mut guarded_params = self.inner.lock().unwrap();
317 let last_max_level = *guarded_params.max_level();
318
319 guarded_params.set_default_level(log_config.get_default_level());
320
321 let max_level = guarded_params.set_mod_config(log_config.get_mod_level());
322 if max_level != &last_max_level {
323 log::set_max_level(max_level.to_level_filter());
324 }
325
326 let log_dest = guarded_params.get_log_dest();
327 let cfg_log_dest = log_config.get_log_dest();
328 let stream_log = cfg_log_dest.is_stream_dest();
329
330 if cfg_log_dest != log_dest || stream_log {
331 if stream_log {
332 if let Some(log_stream) = log_config.get_log_stream() {
333 guarded_params.set_log_dest(
334 cfg_log_dest,
335 Some(
336 OpenOptions::new()
337 .append(true)
338 .create(true)
339 .open(log_stream)
340 .upstream_with_context(&format!(
341 "Failed to open log file: '{}'",
342 log_stream.display()
343 ))?,
344 ),
345 )?;
346 } else {
347 return Err(Error::with_context(
348 ErrorKind::InvParam,
349 &format!(
350 "Missing parameter log_stream for destination {:?}",
351 cfg_log_dest
352 ),
353 ));
354 }
355 } else {
356 guarded_params.set_log_dest(cfg_log_dest, NO_STREAM)?;
357 }
358 }
359
360 guarded_params.set_color(log_config.is_color());
361 guarded_params.set_brief_info(log_config.is_brief_info());
362
363 Ok(())
364 }
365}
366
367impl Log for Logger {
368 fn enabled(&self, _metadata: &Metadata) -> bool {
369 true
370 }
371
372 fn log(&self, record: &Record) {
373 let (mod_name, mod_tag) = if let Some(mod_path) = record.module_path() {
374 if let Some(ref exe_name) = self.exe_name {
375 if let Some(ref captures) = self.module_re.captures(mod_path) {
376 if captures.get(1).unwrap().as_str() == exe_name {
377 (
378 mod_path.to_owned(),
379 captures.get(2).unwrap().as_str().to_owned(),
380 )
381 } else {
382 (mod_path.to_owned(), mod_path.to_owned())
383 }
384 } else if mod_path == exe_name {
385 (mod_path.to_owned(), String::from("main"))
386 } else {
387 (mod_path.to_owned(), mod_path.to_owned())
388 }
389 } else {
390 (mod_path.to_owned(), mod_path.to_owned())
391 }
392 } else {
393 (String::from("undefined"), String::from("undefined"))
394 };
395
396 let curr_level = record.metadata().level();
397
398 let mut guarded_params = self.inner.lock().unwrap();
399 let mut level = guarded_params.get_default_level();
400 if let Some(mod_level) = guarded_params.get_mod_level(&mod_tag) {
401 level = mod_level;
402 }
403
404 if curr_level <= level {
405 let timestamp = if guarded_params.timestamp() {
406 let now = Local::now();
407 if guarded_params.millis() {
408 let ts_millis = now.timestamp_millis() % 1000;
409 format!("{}.{:03} ", now.format("%Y-%m-%d %H:%M:%S"), ts_millis)
410 } else {
411 format!("{} ", now.format("%Y-%m-%d %H:%M:%S"))
412 }
413 } else {
414 "".to_owned()
415 };
416
417 let mut output = if guarded_params.brief_info() && (curr_level == Level::Info) {
418 format!(
419 "{}{:<5} {}\n",
420 timestamp,
421 record.level().to_string(),
422 record.args()
423 )
424 } else {
425 format!(
426 "{}{:<5} [{}] {}\n",
427 timestamp,
428 record.level().to_string(),
429 &mod_name,
430 record.args()
431 )
432 };
433
434 if guarded_params.color() {
435 output = match curr_level {
436 Level::Error => format!("{}", output.red()),
437 Level::Warn => format!("{}", output.yellow()),
438 Level::Info => format!("{}", output.green()),
439 Level::Debug => format!("{}", output.cyan()),
440 Level::Trace => format!("{}", output.blue()),
441 };
442 }
443
444 let _res = match guarded_params.get_log_dest() {
445 LogDestination::Stderr => stderr().write(output.as_bytes()),
446 LogDestination::Stdout => stdout().write(output.as_bytes()),
447 LogDestination::Stream => {
448 if let Some(ref mut stream) = guarded_params.log_stream() {
449 stream.write(output.as_bytes())
450 } else {
451 stderr().write(output.as_bytes())
452 }
453 }
454 LogDestination::StreamStdout => {
455 if let Some(ref mut stream) = guarded_params.log_stream() {
456 let _wres = stream.write(output.as_bytes());
457 }
458 stdout().write(output.as_bytes())
459 }
460 LogDestination::StreamStderr => {
461 if let Some(ref mut stream) = guarded_params.log_stream() {
462 let _wres = stream.write(output.as_bytes());
463 }
464 stderr().write(output.as_bytes())
465 }
466 LogDestination::Buffer => {
467 if let Some(ref mut buffer) = guarded_params.log_buffer() {
468 buffer.write(output.as_bytes())
469 } else {
470 stderr().write(output.as_bytes())
471 }
472 }
473 LogDestination::BufferStdout => {
474 if let Some(ref mut buffer) = guarded_params.log_buffer() {
475 let _wres = buffer.write(output.as_bytes());
476 }
477 stdout().write(output.as_bytes())
478 }
479 LogDestination::BufferStderr => {
480 if let Some(ref mut buffer) = guarded_params.log_buffer() {
481 let _wres = buffer.write(output.as_bytes());
482 }
483 stderr().write(output.as_bytes())
484 }
485 };
486 }
487 }
488
489 fn flush(&self) {
490 let mut guarded_params = self.inner.lock().unwrap();
491 guarded_params.flush();
492 }
493}
494
495