1use std::io::{Write, Stdout, stdout};
2
3use log::{self, Log, Metadata, Record, SetLoggerError, Level, LevelFilter};
4use filter::Filter;
5
6use chrono::{Local, DateTime};
7
8use termcolor::{Buffer, Color, ColorSpec, WriteColor};
9use once_cell::sync::Lazy;
10
11pub mod filter;
12
13pub struct Logger<Writer> {
14 filter: Filter,
15 writer: Writer,
16}
17
18static IS_STDOUT: Lazy<bool> = Lazy::new(|| atty::is(atty::Stream::Stdout));
19pub trait Writer {
20 fn writer(&self, record: &Record);
21}
22
23impl Writer for Stdout {
24 fn writer(&self, record: &Record) {
25 let time_now: DateTime<Local> = Local::now();
26
27 let s = format!("[{}] {} | {} | {} | {}:{}",
28 time_now.format("%Y/%m/%d %H:%M:%S %z").to_string(),
29 record.level(),
30 record.target(),
31 record.args(),
32 record.file().unwrap_or("unknow"),
33 record.line().unwrap_or_default()
34 );
35
36 if *IS_STDOUT {
37 let mut buffer = Buffer::ansi();
38 let mut color_spec = ColorSpec::new();
39
40 match record.level() {
41 Level::Trace => {
42 color_spec.set_fg(Some(Color::Magenta));
43 },
44 Level::Debug => {
45 color_spec.set_fg(Some(Color::Blue));
46 },
47 Level::Info => {
48 color_spec.set_fg(Some(Color::Green));
49 },
50 Level::Warn => {
51 color_spec.set_fg(Some(Color::Yellow));
52 },
53 Level::Error => {
54 color_spec.set_fg(Some(Color::Red));
55 }
56 }
57
58 color_spec.set_bold(true);
59 let _ = buffer.set_color(&color_spec);
60 let _ = buffer.write_all(s.as_bytes());
61
62 let mut handle = self.lock();
63 let _ = handle.write_all(buffer.as_slice());
64 let _ = handle.write_all(b"\n");
65 let _ = handle.flush();
66 } else {
67 let mut handle = self.lock();
68 let _ = handle.write_all(s.as_bytes());
69 let _ = handle.write_all(b"\n");
70 let _ = handle.flush();
71 }
72 }
73}
74
75impl<W: Writer> Logger<W> {
76 pub fn new(filter: Filter, writer: W) -> Self {
77 Self {
78 filter,
79 writer
80 }
81 }
82}
83
84impl Default for Logger<Stdout> {
85 fn default() -> Self {
86 let mut builder = filter::Builder::new();
87
88 if let Ok(ref filter) = std::env::var("LOG_LEVEL") {
89 builder.parse(filter);
90 }
91
92 Logger::new( builder.build(), stdout())
93 }
94}
95
96impl<P: Writer + Sync + Send> Log for Logger<P> {
97 fn enabled(&self, metadata: &Metadata) -> bool {
98 self.filter.enabled(metadata)
99 }
100
101 fn log(&self, record: &Record) {
102 if self.filter.matches(record) {
103 self.writer.writer(record);
104 }
105 }
106
107 fn flush(&self) {}
108}
109
110pub fn init(level: LevelFilter) -> Result<(), SetLoggerError> {
111 log::set_boxed_logger(Box::new(Logger::default()))
112 .map(|()| log::set_max_level(level))
113}
114
115pub fn init_with_logger<P: Writer + Sync + Send + 'static>(
116 level: LevelFilter,
117 logger: Logger<P>
118) -> Result<(), SetLoggerError> {
119 log::set_boxed_logger(Box::new(logger))
120 .map(|()| log::set_max_level(level))
121}