1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#![allow(clippy::uninlined_format_args, clippy::missing_const_for_fn)]
mod timestamp;
use log::{Level, LevelFilter, Log, Metadata, Record};
/// The main logging struct
pub struct PicoLogger {
level: LevelFilter,
colors: bool,
}
impl PicoLogger {
/// Create (but not initialize!) a new logger instance. Must be initialized for the logger to
/// take effect.
///
/// # Example
///
/// ```rust
/// use log::{debug, LevelFilter};
/// use picolog::PicoLogger;
///
/// PicoLogger::new(LevelFilter::Trace).init();
/// debug!("hi!");
/// ```
#[must_use]
pub fn new(level: LevelFilter) -> Self {
Self {
level,
colors: true,
}
}
/// Enable or disable colors.
///
/// # Example
///
/// ```rust
/// # use log::LevelFilter;
/// # use picolog::PicoLogger;
/// PicoLogger::new(LevelFilter::Trace)
/// .with_colors(false)
/// .init();
/// ```
#[must_use]
pub fn with_colors(mut self, colors: bool) -> Self {
self.colors = colors;
self
}
/// Initialize the logger.
///
/// # Panics
///
/// This function panics if called more than once.
pub fn init(self) {
log::set_max_level(self.level);
log::set_boxed_logger(Box::new(self)).expect("logger already set");
}
}
impl Log for PicoLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= self.level
}
fn log(&self, record: &Record) {
if !self.enabled(record.metadata()) {
return;
}
let level = {
let level = record.level();
let red = "\x1b[31m";
let yellow = "\x1b[33m";
let cyan = "\x1b[36m";
let purple = "\x1b[35m";
let normal = "\x1b[m";
if self.colors {
let color = match level {
Level::Error => red,
Level::Warn => yellow,
Level::Info => cyan,
Level::Debug => purple,
Level::Trace => normal,
};
format!("{}{:<5}{}", color, level, normal)
} else {
format!("{:<5}", level)
}
};
let location = {
let module = if !record.target().is_empty() {
record.target()
} else if let Some(path) = record.module_path() {
path
} else {
"?"
};
module.split("::").last().unwrap_or("?")
};
let thread = match std::thread::current().name() {
Some("main") | None => String::new(),
Some(name) => format!("/{}", name),
};
let timestamp = timestamp::Timestamp::new();
println!(
"{} {} [{}{}] {}",
timestamp,
level,
location,
thread,
record.args()
);
}
fn flush(&self) {}
}