1use log::{Level, Record};
2
3use std::convert::AsRef;
4use std::{fmt, mem, thread};
5
6pub trait Formater: Send + Sync + 'static {
7 fn boxed(self) -> Box<dyn Formater>;
8 fn format(&self, record: &Record) -> String;
9}
10
11impl Formater for BaseFormater {
12 fn boxed(self) -> Box<dyn Formater> {
13 Box::new(self) as _
14 }
15
16 fn format(&self, record: &Record) -> String {
17 self.formater_get()(self, record)
18 }
19}
20
21pub fn format(base: &BaseFormater, record: &Record) -> String {
22 let datetime = if base.local_get() {
23 chrono::Local::now().format(base.datetime_get())
24 } else {
25 chrono::Utc::now().format(base.datetime_get())
26 };
27
28 #[cfg(any(feature = "color"))]
29 let level = FixedLevel::with_color(record.level(), base.color_get())
30 .length(base.level_get())
31 .into_colored()
32 .into_coloredfg();
33 #[cfg(not(feature = "color"))]
34 let level = FixedLevel::new(record.level()).length(base.level_get());
35
36 current_thread_name(|ctn| {
37 format!(
38 "{} {} [{}] ({}:{}) [{}] -- {}\n",
39 datetime,
40 level,
41 ctn,
42 record.file().unwrap_or("*"),
43 record.line().unwrap_or(0),
44 record.target(),
45 record.args()
46 )
47 })
48}
49
50impl fmt::Debug for BaseFormater {
51 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
52 #[cfg(any(feature = "color"))]
53 return fmt
54 .debug_struct("BaseFormater")
55 .field("local", &self.local)
56 .field("level", &self.level)
57 .field("datetime", &self.datetime)
58 .field("color", &self.color)
59 .finish();
60
61 #[cfg(not(feature = "color"))]
62 fmt.debug_struct("BaseFormater")
63 .field("local", &self.local)
64 .field("level", &self.level)
65 .field("datetime", &self.datetime)
66 .finish()
67 }
68}
69
70pub struct BaseFormater {
72 local: bool,
73 level: usize,
74 datetime: String,
75 formater: Box<dyn Fn(&Self, &Record) -> String + Send + Sync + 'static>,
76 #[cfg(any(feature = "color"))]
77 color: ColoredLogConfig,
78}
79
80impl Default for BaseFormater {
81 fn default() -> Self {
82 Self::new()
83 }
84}
85
86impl BaseFormater {
87 pub fn new() -> Self {
88 Self {
89 local: false,
90 level: 5,
91 formater: Box::new(format) as _,
92 datetime: "%Y-%m-%d %H:%M:%S.%3f".to_owned(),
93 #[cfg(any(feature = "color"))]
94 color: ColoredLogConfig::new(),
95 }
96 }
97
98 pub fn local(mut self, local: bool) -> Self {
99 self.local = local;
100 self
101 }
102
103 #[inline]
104 pub fn local_get(&self) -> bool {
105 self.local
106 }
107
108 pub fn level(mut self, chars: usize) -> Self {
109 self.level = chars;
110 self
111 }
112
113 #[inline]
114 pub fn level_get(&self) -> usize {
115 self.level
116 }
117
118 pub fn datetime<S: Into<String>>(mut self, datetime: S) -> Self {
119 self.datetime = datetime.into();
120 self
121 }
122
123 #[inline]
124 pub fn datetime_get(&self) -> &str {
125 &self.datetime
126 }
127
128 pub fn formater<F>(mut self, formater: F) -> Self
129 where
130 F: Fn(&Self, &Record) -> String + Send + Sync + 'static,
131 {
132 self.formater = Box::new(formater) as _;
133 self
134 }
135
136 #[inline]
137 pub fn formater_get(&self) -> &(dyn Fn(&Self, &Record) -> String + Send + Sync + 'static) {
138 &*self.formater
139 }
140
141 #[cfg(any(feature = "color"))]
142 pub fn color(mut self, color_: bool) -> Self {
143 self.color.color = color_;
144 self
145 }
146
147 #[inline]
148 #[cfg(any(feature = "color"))]
149 pub fn color_get(&self) -> &ColoredLogConfig {
150 &self.color
151 }
152
153 #[cfg(any(feature = "color"))]
154 pub fn colored(mut self, color: ColoredLogConfig) -> Self {
155 self.color = color;
156 self
157 }
158}
159
160pub fn current_thread_name<F, U>(f: F) -> U
161where
162 F: Fn(&str) -> U,
163{
164 struct ThreadId(u64);
165
166 thread_local!(static THREAD_NAME: String = {
167 let thread = thread::current();
168 format!("{}.{}", unsafe { mem::transmute::<_, ThreadId>(thread.id()).0 }, thread.name()
169 .map(|s| s.to_owned())
170 .unwrap_or_else(||"****".to_owned()))
172 });
173
174 THREAD_NAME.with(|tname| f(&tname))
175}
176
177#[derive(Debug, Clone, Copy)]
178pub struct FixedLevel {
179 str: &'static str,
180 length: usize,
181 #[cfg(any(feature = "color"))]
182 color: Option<Color>,
183}
184
185impl FixedLevel {
186 #[inline]
187 pub fn new(level: Level) -> Self {
188 let str = match level {
189 Level::Trace => "TRACE",
190 Level::Debug => "DEBUG",
191 Level::Info => "INFO ",
192 Level::Warn => "WARN ",
193 Level::Error => "ERROR",
194 };
195
196 Self {
197 str,
198 length: 5,
199 #[cfg(any(feature = "color"))]
200 color: None,
201 }
202 }
203
204 #[inline]
205 pub fn length(mut self, length: usize) -> Self {
206 debug_assert!(length <= 5);
207 self.length = length;
208 self
209 }
210
211 #[inline]
212 #[cfg(any(feature = "color"))]
213 pub fn with_color(level: Level, color_: &ColoredLogConfig) -> Self {
214 let (str, color) = match level {
215 Level::Trace => ("TRACE", color_.trace),
216 Level::Debug => ("DEBUG", color_.debug),
217 Level::Info => ("INFO ", color_.info),
218 Level::Warn => ("WARN ", color_.warn),
219 Level::Error => ("ERROR", color_.error),
220 };
221
222 Self {
223 str,
224 length: 5,
225 color: if color_.color { Some(color) } else { None },
226 }
227 }
228
229 #[cfg(any(feature = "color"))]
230 pub fn into_colored(self) -> ColoredFixedLevel {
231 ColoredFixedLevel::new(self)
232 }
233}
234
235impl fmt::Display for FixedLevel {
236 #[inline]
237 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
238 fmt.write_str(self.as_ref())
239 }
240}
241
242impl AsRef<str> for FixedLevel {
243 #[inline]
244 fn as_ref(&self) -> &'static str {
245 unsafe { self.str.get_unchecked(0..self.length) }
246 }
247}
248
249#[cfg(any(feature = "color"))]
250use self::color::{Color, ColoredFixedLevel, ColoredLogConfig};
251#[cfg(any(feature = "color"))]
252pub mod color {
253 use super::FixedLevel;
254 use log::Level;
255 use std::{fmt, mem};
256 pub use yansi::Color;
257
258 pub struct ColoredFgWith<T> {
259 text: T,
260 color: Option<Color>,
261 }
262
263 impl<T> fmt::Display for ColoredFgWith<T>
264 where
265 T: fmt::Display,
266 {
267 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
268 if let Some(color) = self.color.as_ref() {
269 write!(f, "{}", color.paint(&self.text))
270 } else {
271 write!(f, "{}", self.text)
272 }
273 }
274 }
275
276 #[derive(Debug, Clone)]
277 pub struct ColoredLogConfig {
278 pub error: Color,
279 pub warn: Color,
280 pub info: Color,
281 pub debug: Color,
282 pub trace: Color,
283 pub color: bool,
284 }
285
286 impl Default for ColoredLogConfig {
287 fn default() -> Self {
288 Self::new()
289 }
290 }
291
292 impl ColoredLogConfig {
293 #[inline]
294 pub fn new() -> Self {
295 Self {
296 error: Color::Red,
297 warn: Color::Yellow,
298 info: Color::Green,
299 debug: Color::Cyan,
300 trace: Color::Blue,
301 color: true,
302 }
303 }
304
305 pub fn error(mut self, error: Color) -> Self {
306 self.error = error;
307 self
308 }
309
310 pub fn warn(mut self, warn: Color) -> Self {
311 self.warn = warn;
312 self
313 }
314
315 pub fn info(mut self, info: Color) -> Self {
316 self.info = info;
317 self
318 }
319
320 pub fn debug(mut self, debug: Color) -> Self {
321 self.debug = debug;
322 self
323 }
324
325 pub fn trace(mut self, trace: Color) -> Self {
326 self.trace = trace;
327 self
328 }
329
330 pub fn color(mut self, color: bool) -> Self {
331 self.color = color;
332 self
333 }
334
335 pub fn coloredfg<T>(&self, level: Level, t: T) -> ColoredFgWith<T>
336 where
337 T: ColoredFg<T>,
338 {
339 t.coloredfg(level, self)
340 }
341 }
342
343 pub trait ColoredFg<T> {
344 fn coloredfg(self, level: Level, config: &ColoredLogConfig) -> ColoredFgWith<T>;
345 }
346
347 impl<T: fmt::Display> ColoredFg<T> for T {
348 fn coloredfg(self, level: Level, config: &ColoredLogConfig) -> ColoredFgWith<Self> {
349 let color = if config.color {
350 let colored = match level {
351 Level::Error => config.error,
352 Level::Warn => config.warn,
353 Level::Info => config.info,
354 Level::Debug => config.debug,
355 Level::Trace => config.trace,
356 };
357 Some(colored)
358 } else {
359 None
360 };
361
362 ColoredFgWith { color, text: self }
363 }
364 }
365
366 #[derive(Debug, Clone, Copy)]
374 pub struct ColoredFixedLevel(FixedLevel);
375 impl ColoredFixedLevel {
376 #[inline]
377 pub fn new(fl: FixedLevel) -> Self {
378 ColoredFixedLevel(fl)
379 }
380
381 #[inline]
382 pub fn into_coloredfg(mut self) -> ColoredFgWith<Self> {
383 let color = mem::replace(&mut self.0.color, None);
384 ColoredFgWith { color, text: self }
385 }
386 }
387 impl fmt::Display for ColoredFixedLevel {
388 #[inline]
389 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
390 fmt.write_str(self.0.as_ref())
391 }
392 }
393}