fast_logger/
lib.rs

1//! A simple log-level based logger
2//!
3//! # General #
4//!
5//! When spawning a logger, a thread is created and a handle structure returned. This handle can be
6//! copied and shared among threads. All it does is hold some mutexes and atomic references to the
7//! logger. Actually logging pushes the data over an asynchronous channel with size limits.
8//!
9//! The logger requires a `Display` type to be provided so the logger can actually print data. The
10//! reason for this is that it cuts down on serialization cost for the caller, leaving the logger
11//! to serialize numbers into strings and perform other formatting work.
12//!
13//! # Compatibility mode #
14//!
15//! There are many loggers out there in the wild, and different libraries may use different loggers. To allow
16//! program developers to log from different sources without agreeing on a logger to use, one can
17//! interface with [Compatibility]. Logging macros work with [Compatibility].
18//!
19//! # Log Levels #
20//!
21//! Logger features two log level controls: per-context and "global" (Note: The logger has no
22//! globals, everything is local to the logger object). When logging a message, the
23//! global log level is checked, and if the current message has a lower priority than the global
24//! log level, nothing will be sent to the logger.
25//!
26//! Once the logger has received the message, it checks its internal mapping from context to log
27//! level, if the message's log level has a lower priority than the context log level, it is
28//! dropped.
29//!
30//! We normally use the helper functions `trace`, `debug`, `info`, `warn`, and `error`, which have
31//! the corresponding log levels: 255, 192, 128, 64, 0, respectively.
32//!
33//! Trace is disabled with `debug_assertions` off.
34//!
35//! Note: an `error` message has priority 0, and log levels are always unsigned, so an `error` message
36//! can never be filtered.
37//!
38//! # Example - Generic #
39//!
40//! Note that generic logging requires indirection at runtime, and may slow down your program.
41//! Still, generic logging is very desirable because it is easy to use. There are two ways to do
42//! generic logging depending on your needs:
43//!
44//! ```
45//! use fast_logger::{info, Generic, Logger};
46//!
47//! fn main() {
48//!     let logger = Logger::<Generic>::spawn("context");
49//!     info!(logger, "Message {}", "More"; "key" => "value", "three" => 3);
50//! }
51//! ```
52//! The other macros are [trace!], [debug!], [warn!], [error!], and the generic [log!].
53//!
54//! If you wish to mix this with static logging, you can do the following:
55//! ```
56//! use fast_logger::{info, Generic, Logger};
57//!
58//! enum MyMsg {
59//!     Static(&'static str),
60//!     Dynamic(Generic),
61//! }
62//!
63//! impl std::fmt::Display for MyMsg {
64//!     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
65//!         match self {
66//!             MyMsg::Static(string) => write!(f, "{}", string),
67//!             MyMsg::Dynamic(handle) => handle.fmt(f),
68//!         }
69//!     }
70//! }
71//!
72//! impl From<Generic> for MyMsg {
73//!     fn from(f: Generic) -> Self {
74//!         MyMsg::Dynamic(f)
75//!     }
76//! }
77//!
78//! fn main() {
79//!     // Setup
80//!     let logger = Logger::<MyMsg>::spawn("context");
81//!     info!(logger, "Message {}", "More"; "key" => "value", "three" => 3);
82//! }
83//! ```
84//!
85//! # Example of static logging #
86//!
87//! Here is an example of static-logging only, the macros do not work for this, as these generate
88//! a [Generic]. Anything implementing [Into] for the type of the logger will be accepted into
89//! the logging functions. This is the fast way to log, as no [Box] is used.
90//!
91//! ```
92//! use fast_logger::Logger;
93//!
94//! // You need to define your own message type
95//! enum MyMessageEnum {
96//!     SimpleMessage(&'static str)
97//! }
98//!
99//! // It needs to implement std::fmt::Display
100//! impl std::fmt::Display for MyMessageEnum {
101//!     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
102//!         match self {
103//!             MyMessageEnum::SimpleMessage(string) => write!(f, "{}", string),
104//!         }
105//!     }
106//! }
107//!
108//! fn main() {
109//!     // Setup
110//!     let logger = Logger::<MyMessageEnum>::spawn("ctx");
111//!
112//!     // Actual logging
113//!     logger.info(MyMessageEnum::SimpleMessage("Hello world!"));
114//!
115//!     // Various logging levels
116//!     logger.trace(MyMessageEnum::SimpleMessage("Hello world!"));
117//!     logger.debug(MyMessageEnum::SimpleMessage("Hello world!"));
118//!     logger.info(MyMessageEnum::SimpleMessage("Hello world!"));
119//!     logger.warn(MyMessageEnum::SimpleMessage("Hello world!"));
120//!     logger.error(MyMessageEnum::SimpleMessage("Hello world!"));
121//! }
122//! ```
123//!
124//! # Example with log levels #
125//!
126//! Here is an example where we set a context specific log level.
127//!
128//! ```
129//! use fast_logger::Logger;
130//!
131//! // You need to define your own message type
132//! enum MyMessageEnum {
133//!     SimpleMessage(&'static str)
134//! }
135//!
136//! // It needs to implement std::fmt::Display
137//! impl std::fmt::Display for MyMessageEnum {
138//!     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
139//!         match self {
140//!             MyMessageEnum::SimpleMessage(string) => write!(f, "{}", string),
141//!         }
142//!     }
143//! }
144//!
145//! fn main() {
146//!     // Setup
147//!     let logger = Logger::<MyMessageEnum>::spawn("ctx");
148//!
149//!     // Set the log level of `ctx` to 70, this filters
150//!     // All future log levels 71-255 out.
151//!     assert!(logger.set_context_specific_log_level("ctx", 70));
152//!
153//!     // This gets printed, because `warn` logs at level 64 <= 70
154//!     logger.warn(MyMessageEnum::SimpleMessage("1"));
155//!
156//!     // This gets printed, because 50 <= 70
157//!     logger.log(50, MyMessageEnum::SimpleMessage("2"));
158//!
159//!     // This does not get printed, because !(80 <= 70)
160//!     logger.log(80, MyMessageEnum::SimpleMessage("3"));
161//!
162//!     // This gets printed, because the context is different
163//!     logger.clone_with_context("ctx*").log(128, MyMessageEnum::SimpleMessage("4"));
164//! }
165//! ```
166//!
167//! # Example with just strings #
168//!
169//! If you really don't care about formatting overhead on the caller's side, you can just use a
170//! [String] as the message type.
171//!
172//! ```
173//! use fast_logger::Logger;
174//!
175//! fn main() {
176//!     // Setup
177//!     let logger = Logger::<String>::spawn("ctx");
178//!
179//!     // Set the log level of `ctx` to 70, this filters
180//!     // All future log levels 71-255 out.
181//!     assert!(logger.set_context_specific_log_level("ctx", 70));
182//!
183//!     // This gets printed, because `warn` logs at level 64 <= 70
184//!     logger.warn(format!("1"));
185//!
186//!     // This gets printed, because 50 <= 70
187//!     logger.log(50, format!("2"));
188//!
189//!     // This does not get printed, because !(80 <= 70)
190//!     logger.log(80, format!("3"));
191//!
192//!     // This gets printed, because the context is different
193//!     logger.clone_with_context("ctx*").log(128, format!("4"));
194//! }
195//! ```
196//!
197//! # Non-Copy Data #
198//!
199//! Data that is non-copy may be hard to pass to the logger, to alleviate this, there's a builtin
200//! clone directive in the macros:
201//!
202//! ```
203//! use fast_logger::{info, Generic, InDebug, Logger};
204//!
205//! #[derive(Clone, Debug)]
206//! struct MyStruct();
207//!
208//! fn main() {
209//!     let logger = Logger::<Generic>::spawn("context");
210//!     let my_struct = MyStruct();
211//!     info!(logger, "Message {}", "More"; "key" => InDebug(&my_struct); clone
212//!     my_struct);
213//!     info!(logger, "Message {}", "More"; "key" => InDebug(&my_struct));
214//! }
215//! ```
216//!
217//! # Nested Logging #
218//!
219//! It is sometimes useful to nest logging contexts, here's how to do that:
220//!
221//! ```
222//! use fast_logger::{info, Generic, Logger};
223//!
224//! fn main() {
225//!     let logger = Logger::<Generic>::spawn("context");
226//!     let submodule = logger.clone_add_context("submodule");
227//!     let mut something = submodule.clone_add_context("something");
228//!     info!(something, "This message appears in the context: context-submodule-something");
229//! }
230//! ```
231#![deny(
232    missing_docs,
233    trivial_casts,
234    trivial_numeric_casts,
235    unsafe_code,
236    unused_import_braces,
237    unused_qualifications
238)]
239
240mod log;
241mod u32map;
242
243pub use log::*;
244use u32map::*;
245
246use chrono::prelude::*;
247use colored::*;
248use crossbeam_channel::{bounded, Receiver, RecvError, Sender, TrySendError};
249use std::{
250    collections::HashMap,
251    fmt::{self, Debug, Display, LowerHex},
252    marker::Send,
253    sync::{
254        atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering},
255        Arc, Mutex,
256    },
257    thread::{self, JoinHandle},
258};
259
260/// Compatibility type when using loggers across library boundaries
261/// # Example #
262/// Here, the host as well as `MyLibrary` can both use any logger they wish as they are decoupled
263/// with respect to their loggers. The only bridge between them is the [Compatibility] type.
264///
265/// This is especially useful so a library can expose the compatibility type, while hosts of that
266/// library can pick and choose which logger to connect to it.
267/// ```
268/// use fast_logger::*;
269/// fn main() {
270///     let logger = Logger::<Generic>::spawn("tst");
271///
272///     type MyCompatibility = Box<dyn Fn(u8, Box<dyn Fn(&mut std::fmt::Formatter) -> std::fmt::Result + Send + Sync>)>;
273///     struct MyLibrary {
274///         log: Logpass,
275///     }
276///
277///     impl MyLibrary {
278///         pub fn new(log: MyCompatibility) -> Self {
279///             Self {
280///                 log: Logpass::from_compatibility(log),
281///             }
282///         }
283///         pub fn function(&mut self) {
284///             info!(self.log, "Compatibility layer");
285///         }
286///     }
287///
288///     let mut my_lib = MyLibrary::new(logger.to_compatibility());
289///     my_lib.function();
290/// }
291/// ```
292pub type Compatibility =
293    Box<dyn Fn(u8, Box<dyn Fn(&mut fmt::Formatter) -> fmt::Result + Send + Sync>)>;
294/// The logger which dependent crates should use
295pub type Logger<C> = LoggerV2Async<C>;
296
297/// The fastest logger in the west
298///
299/// A very simple logger that uses a custom structure and
300/// a logging level. No further assumptions are made about
301/// the structure or nature of the logging message, thus
302/// making it extremely cheap to send messages to the logger
303/// thread.
304#[derive(Clone)]
305pub struct LoggerV2Async<C: Display + Send> {
306    // Explicitly first so it's the first to drop
307    log_channel: Sender<(u8, u32, C)>,
308    context_specific_level: Arc<Mutex<HashMap<String, u8>>>,
309    level: Arc<AtomicU8>,
310    log_channel_full_count: Arc<AtomicUsize>,
311    thread_handle: Arc<AutoJoinHandle>,
312    colorize: Arc<AtomicBool>,
313    context_map: Arc<Mutex<U32Map<String>>>,
314    context: u32,
315}
316
317// ---
318
319/// Trait for a generic logger, allows [Logpass] to pass [Generic] to this logger
320pub trait GenericLogger {
321    /// Log a generic message, used by [Logpass]
322    fn log_generic(&self, level: u8, message: Generic);
323    /// Consume this logger to create a logpass
324    fn to_logpass(self) -> Logpass;
325    /// Turn the logger into a function that takes messages
326    ///
327    /// Useful when crossing boundaries into libraries that only take this `compatibility` type.
328    /// The type only refers to globally accessible types, so it can be used everywhere without
329    /// introducing dependencies.
330    ///
331    /// See [Compatibility] for examples.
332    fn to_compatibility(self) -> Compatibility;
333}
334
335impl<C: 'static + Display + From<Generic> + Send> GenericLogger for LoggerV2Async<C> {
336    fn log_generic(&self, level: u8, message: Generic) {
337        self.log(level, message);
338    }
339    fn to_logpass(self) -> Logpass {
340        Logpass::PassThrough(Box::new(self))
341    }
342    fn to_compatibility(self) -> Compatibility {
343        Box::new(move |level, writer| {
344            self.log_generic(level, Generic(Arc::new(writer)));
345        })
346    }
347}
348
349// ---
350
351/// A passthrough-logger
352///
353/// This structure holds a reference to another logger and passes all messages along, the messages
354/// can only be of the type [Generic].
355pub enum Logpass {
356    /// Compatibility layer case, when using a Logpass in a library so you can, see
357    /// [GenericLogger::to_compatibility].
358    Compatibility(Compatibility),
359    /// Simple passthrough layer, used with [GenericLogger::to_logpass] to decouple print type
360    /// dependencies.
361    PassThrough(Box<dyn GenericLogger>),
362}
363
364impl Logpass {
365    /// Logging function for the logpass
366    pub fn log(&self, level: u8, message: Generic) {
367        match self {
368            Logpass::Compatibility(compat) => {
369                (compat)(level, Box::new(move |f| write!(f, "{}", message)))
370            }
371            Logpass::PassThrough(passthrough) => passthrough.log_generic(level, message),
372        }
373    }
374    /// Turn a compatibility function into a [Logpass]
375    ///
376    /// See [Compatibility] for examples.
377    pub fn from_compatibility(compatibility: Compatibility) -> Self {
378        Logpass::Compatibility(compatibility)
379    }
380}
381
382// ---
383
384/// Wrapper for [JoinHandle], joins on [drop]
385struct AutoJoinHandle {
386    thread: Option<JoinHandle<()>>,
387}
388
389impl Drop for AutoJoinHandle {
390    fn drop(&mut self) {
391        self.thread.take().map(JoinHandle::join);
392    }
393}
394
395// ---
396
397/// A handle for generic logging data, used by macros
398#[derive(Clone)]
399pub struct Generic(Arc<dyn Fn(&mut fmt::Formatter) -> fmt::Result + Send + Sync>);
400
401impl Display for Generic {
402    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
403        (self.0)(f)
404    }
405}
406
407// ---
408
409/// This is an implementation detail used by macros, and should NOT be called directly!
410#[doc(hidden)]
411pub fn make_generic__(
412    arg: Arc<dyn Fn(&mut fmt::Formatter) -> fmt::Result + Send + Sync>,
413) -> Generic {
414    Generic(arg)
415}
416
417// ---
418
419static CHANNEL_SIZE: usize = 30_000;
420static DEFAULT_LEVEL: u8 = 128;
421static LOGGER_QUIT_LEVEL: u8 = 196;
422
423impl<C: 'static + Display + Send> LoggerV2Async<C> {
424    /// Create a logger object and spawn a logging thread
425    ///
426    /// The logger object is the interface to write stuff to
427    /// the logger thread. The logger thread is in the background,
428    /// waiting for messages to print out. Once all logger objects
429    /// are dropped, the thread will die.
430    ///
431    /// Typically, you only call `spawn` once in an application
432    /// since you just want a single logging thread to print stuff.
433    pub fn spawn(ctx: &'static str) -> Logger<C> {
434        let (tx, rx) = bounded(CHANNEL_SIZE);
435        let colorize = Arc::new(AtomicBool::new(false));
436        let colorize_clone = colorize.clone();
437        let full_count = Arc::new(AtomicUsize::new(0));
438        let full_count_clone = full_count.clone();
439        let level = Arc::new(AtomicU8::new(DEFAULT_LEVEL));
440        let level_clone = level.clone();
441        let ex = std::io::stdout();
442        let context_specific_level = create_context_specific_log_level(Some(ctx));
443        let context_specific_level_clone = context_specific_level.clone();
444        let context_map = create_context_map(Some(ctx));
445        let context_map_clone = context_map.clone();
446        let logger_thread = thread::Builder::new()
447            .name("logger".to_string())
448            .spawn(move || {
449                logger_thread(
450                    rx,
451                    full_count_clone,
452                    ex.lock(),
453                    context_specific_level_clone,
454                    level_clone,
455                    colorize_clone,
456                    context_map,
457                )
458            })
459            .unwrap();
460        Logger {
461            thread_handle: Arc::new(AutoJoinHandle {
462                thread: Some(logger_thread),
463            }),
464            log_channel: tx,
465            log_channel_full_count: full_count,
466            level,
467            context_specific_level,
468            colorize,
469            context_map: context_map_clone,
470            context: 1,
471        }
472    }
473
474    /// Create a logger object with a specific writer
475    ///
476    /// See `spawn` for more information regarding spawning. This function providing the logger a
477    /// writer, which makes the logger use any arbitrary writing interface.
478    pub fn spawn_with_writer<T: 'static + std::io::Write + Send>(
479        ctx: &'static str,
480        writer: T,
481    ) -> Logger<C> {
482        let (tx, rx) = bounded(CHANNEL_SIZE);
483        let colorize = Arc::new(AtomicBool::new(false));
484        let colorize_clone = colorize.clone();
485        let full_count = Arc::new(AtomicUsize::new(0));
486        let full_count_clone = full_count.clone();
487        let level = Arc::new(AtomicU8::new(DEFAULT_LEVEL));
488        let level_clone = level.clone();
489        let context_specific_level = create_context_specific_log_level(Some(ctx));
490        let context_specific_level_clone = context_specific_level.clone();
491        let context_map = create_context_map(Some(ctx));
492        let context_map_clone = context_map.clone();
493        let logger_thread = thread::Builder::new()
494            .name("logger".to_string())
495            .spawn(move || {
496                logger_thread(
497                    rx,
498                    full_count_clone,
499                    writer,
500                    context_specific_level_clone,
501                    level_clone,
502                    colorize_clone,
503                    context_map,
504                )
505            })
506            .unwrap();
507        Logger {
508            thread_handle: Arc::new(AutoJoinHandle {
509                thread: Some(logger_thread),
510            }),
511            log_channel: tx,
512            log_channel_full_count: full_count,
513            level,
514            context_specific_level,
515            colorize,
516            context_map: context_map_clone,
517            context: 1,
518        }
519    }
520
521    /// Spawn a logger that doesn't output anything
522    ///
523    /// This logger automatically sets the log level to 0, if you set the log level to something
524    /// other than that the message will be sent, but it will be completely ignored.
525    pub fn spawn_void() -> Logger<C> {
526        let (tx, rx) = bounded(CHANNEL_SIZE);
527        let colorize = Arc::new(AtomicBool::new(false));
528        let colorize_clone = colorize.clone();
529        let full_count = Arc::new(AtomicUsize::new(0));
530        let full_count_clone = full_count.clone();
531        let level = Arc::new(AtomicU8::new(0));
532        let level_clone = level.clone();
533        struct Void {}
534        impl std::io::Write for Void {
535            fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
536                Ok(buf.len())
537            }
538            fn flush(&mut self) -> std::io::Result<()> {
539                Ok(())
540            }
541        }
542        let context_specific_level = create_context_specific_log_level(Some("void"));
543        let context_specific_level_clone = context_specific_level.clone();
544
545        let context_map = create_context_map(Some("void"));
546        let context_map_clone = context_map.clone();
547
548        let logger_thread = thread::Builder::new()
549            .name("logger".to_string())
550            .spawn(move || {
551                logger_thread(
552                    rx,
553                    full_count_clone,
554                    Void {},
555                    context_specific_level_clone,
556                    level_clone,
557                    colorize_clone,
558                    context_map,
559                )
560            })
561            .unwrap();
562        Logger {
563            thread_handle: Arc::new(AutoJoinHandle {
564                thread: Some(logger_thread),
565            }),
566            log_channel: tx,
567            log_channel_full_count: full_count,
568            level,
569            context_specific_level,
570            colorize,
571            context_map: context_map_clone,
572            context: 1,
573        }
574    }
575
576    /// Create a logger object for tests
577    ///
578    /// Similar to `spawn` but sets the log level to 255 and enables colored output,
579    /// logs to `stderr`.
580    pub fn spawn_test() -> Logger<C> {
581        let (tx, rx) = bounded(CHANNEL_SIZE);
582        let colorize = Arc::new(AtomicBool::new(true));
583        let colorize_clone = colorize.clone();
584        let full_count = Arc::new(AtomicUsize::new(0));
585        let full_count_clone = full_count.clone();
586        let level = Arc::new(AtomicU8::new(255));
587        let level_clone = level.clone();
588        let ex = std::io::stderr();
589        let context_specific_level = create_context_specific_log_level(Some("test"));
590        let context_specific_level_clone = context_specific_level.clone();
591        let context_map = create_context_map(Some("test"));
592        let context_map_clone = context_map.clone();
593        let logger_thread = thread::Builder::new()
594            .name("logger".to_string())
595            .spawn(move || {
596                logger_thread(
597                    rx,
598                    full_count_clone,
599                    ex.lock(),
600                    context_specific_level_clone,
601                    level_clone,
602                    colorize_clone,
603                    context_map,
604                )
605            })
606            .unwrap();
607        Logger {
608            thread_handle: Arc::new(AutoJoinHandle {
609                thread: Some(logger_thread),
610            }),
611            log_channel: tx,
612            log_channel_full_count: full_count,
613            level,
614            context_specific_level,
615            colorize,
616            context_map: context_map_clone,
617            context: 1,
618        }
619    }
620
621    /// Clone the logger but change the context of the clone
622    pub fn clone_with_context(&self, ctx: &'static str) -> Self {
623        if let Ok(ref mut lvl) = self.context_specific_level.lock() {
624            lvl.insert(ctx.to_string(), DEFAULT_LEVEL);
625        }
626
627        let ctxval = if let Ok(ref mut ctxmap) = self.context_map.lock() {
628            ctxmap.insert(ctx.to_string()).expect("Context map is full")
629        } else {
630            panic!();
631        };
632
633        Logger {
634            thread_handle: self.thread_handle.clone(),
635            log_channel: self.log_channel.clone(),
636            log_channel_full_count: self.log_channel_full_count.clone(),
637            level: self.level.clone(),
638            context_specific_level: self.context_specific_level.clone(),
639            colorize: self.colorize.clone(),
640            context_map: self.context_map.clone(),
641            context: ctxval,
642        }
643    }
644
645    /// Clone the logger but append the context of the clone
646    pub fn clone_add_context(&self, ctx: &'static str) -> Self {
647        let newctx;
648
649        let ctxval = if let Ok(ref mut ctxmap) = self.context_map.lock() {
650            let prev_ctx = ctxmap.get(&self.context).unwrap().clone();
651            newctx = prev_ctx + "-" + ctx;
652            ctxmap.insert(newctx.clone()).expect("Context map is full")
653        } else {
654            panic!();
655        };
656
657        if let Ok(ref mut lvl) = self.context_specific_level.lock() {
658            lvl.insert(newctx.to_string(), DEFAULT_LEVEL);
659        }
660
661        Logger {
662            thread_handle: self.thread_handle.clone(),
663            log_channel: self.log_channel.clone(),
664            log_channel_full_count: self.log_channel_full_count.clone(),
665            level: self.level.clone(),
666            context_specific_level: self.context_specific_level.clone(),
667            colorize: self.colorize.clone(),
668            context_map: self.context_map.clone(),
669            context: ctxval,
670        }
671    }
672
673    /// The log-level is an 8-bit variable that is shared among
674    /// all clones of this logger. When we try logging we first
675    /// check if our message has a priority higher or equal to
676    /// this level. If it doesn't we just exit the logger function.
677    ///
678    /// Internally, we use an atomic variable to store the logging
679    /// level so all threads can check it quickly. This makes
680    /// log statements that won't trigger because of the log-level
681    /// absolutely fucking NUTS (so cheap it's basically
682    /// non-existent).
683    pub fn set_log_level(&self, level: u8) {
684        self.level.store(level, Ordering::Relaxed);
685    }
686
687    /// Retrieve the current global log level value
688    pub fn get_log_level(&self) -> u8 {
689        self.level.load(Ordering::Relaxed)
690    }
691
692    /// Sets the log level for a specific context
693    ///
694    /// Whenever the logger receives a message, it will use the context-to-level
695    /// mapping to see if the message should be logged or not.
696    /// Note that this happens after checking the global log level.
697    pub fn set_context_specific_log_level(&self, ctx: &str, level: u8) -> bool {
698        if let Ok(ref mut lvl) = self.context_specific_level.lock() {
699            if let Some(stored_level) = lvl.get_mut(ctx) {
700                *stored_level = level;
701                return true;
702            }
703        }
704        false
705    }
706
707    /// Set the log level of this logger's associated context
708    pub fn set_this_log_level(&self, level: u8) {
709        let ctx = if let Ok(ref mut ctxs) = self.context_map.lock() {
710            ctxs.get(&self.context).unwrap().clone()
711        } else {
712            panic!("Unable to acquire context map lock");
713        };
714        assert!(self.set_context_specific_log_level(&ctx, level));
715    }
716
717    /// Enable colorizing log output
718    pub fn set_colorize(&self, on: bool) {
719        self.colorize.store(on, Ordering::Relaxed);
720    }
721
722    /// Check the current colorization status
723    pub fn get_colorize(&self) -> bool {
724        self.colorize.load(Ordering::Relaxed)
725    }
726
727    fn log_internal(&self, level: u8, message: impl Into<C>) -> bool {
728        if level <= self.level.load(Ordering::Relaxed) {
729            match self
730                .log_channel
731                .try_send((level, self.context, message.into()))
732            {
733                Ok(()) => true,
734                Err(TrySendError::Full(_)) => {
735                    self.log_channel_full_count.fetch_add(1, Ordering::Relaxed);
736                    false
737                }
738                Err(TrySendError::Disconnected(_)) => false,
739            }
740        } else {
741            false
742        }
743    }
744}
745
746#[cfg(not(test))]
747impl<C: 'static + Display + Send> LoggerV2Async<C> {
748    /// Generic logging function
749    ///
750    /// Send a message with a specific log-level.
751    pub fn log(&self, level: u8, message: impl Into<C>) {
752        self.log_internal(level, message);
753    }
754
755    /// Log an error message (level 255)
756    ///
757    /// Does nothing when compiled without debug assertions
758    #[cfg(not(debug_assertions))]
759    pub fn trace(&self, _: impl Into<C>) {}
760
761    /// Log an error message (level 255)
762    ///
763    /// Does nothing when compiled without debug assertions
764    #[cfg(debug_assertions)]
765    pub fn trace(&self, message: impl Into<C>) {
766        self.log(255, message)
767    }
768
769    /// Log an error message (level 192)
770    pub fn debug(&self, message: impl Into<C>) {
771        self.log(192, message)
772    }
773
774    /// Log an error message (level 128)
775    pub fn info(&self, message: impl Into<C>) {
776        self.log(128, message)
777    }
778
779    /// Log an error message (level 64)
780    pub fn warn(&self, message: impl Into<C>) {
781        self.log(64, message)
782    }
783
784    /// Log an error message (level 0)
785    pub fn error(&self, message: impl Into<C>) {
786        self.log(0, message)
787    }
788}
789
790#[cfg(test)]
791impl<C: 'static + Display + Send> LoggerV2Async<C> {
792    /// Generic logging function
793    ///
794    /// Send a message with a specific log-level.
795    pub fn log(&self, level: u8, message: impl Into<C>) -> bool {
796        self.log_internal(level, message)
797    }
798
799    /// Log an error message (level 255)
800    ///
801    /// Does nothing when compiled without debug assertions
802    #[cfg(not(debug_assertions))]
803    pub fn trace(&self, _: impl Into<C>) -> bool {
804        false
805    }
806
807    /// Log an error message (level 255)
808    ///
809    /// Does nothing when compiled without debug assertions
810    #[cfg(debug_assertions)]
811    pub fn trace(&self, message: impl Into<C>) -> bool {
812        self.log(255, message)
813    }
814
815    /// Log an error message (level 192)
816    pub fn debug(&self, message: impl Into<C>) -> bool {
817        self.log(192, message)
818    }
819
820    /// Log an error message (level 128)
821    pub fn info(&self, message: impl Into<C>) -> bool {
822        self.log(128, message)
823    }
824
825    /// Log an error message (level 64)
826    pub fn warn(&self, message: impl Into<C>) -> bool {
827        self.log(64, message)
828    }
829
830    /// Log an error message (level 0)
831    pub fn error(&self, message: impl Into<C>) -> bool {
832        self.log(0, message)
833    }
834}
835
836impl<C: 'static + Display + Send + From<String>> LoggerV2Async<C> {
837    /// Create a writer proxy for this logger
838    ///
839    /// Can be used to de-couple the logger dependency by passing aroung a writer instead of this
840    /// logger.
841    pub fn make_writer(&self, level: u8) -> impl std::fmt::Write + '_ {
842        struct Writer<'a, C: Display + Send + From<String>> {
843            logger: &'a Logger<C>,
844            level: u8,
845        }
846        impl<'a, C: 'static + Display + Send + From<String>> std::fmt::Write for Writer<'a, C> {
847            fn write_str(&mut self, s: &str) -> Result<(), std::fmt::Error> {
848                self.logger.log(self.level, s.to_string());
849                Ok(())
850            }
851        }
852        Writer {
853            logger: self,
854            level,
855        }
856    }
857}
858
859// ---
860
861fn create_context_specific_log_level(ctx: Option<&'static str>) -> Arc<Mutex<HashMap<String, u8>>> {
862    let mut map = HashMap::new();
863    map.insert("logger".to_string(), LOGGER_QUIT_LEVEL);
864    if let Some(string) = ctx {
865        map.insert(string.to_string(), DEFAULT_LEVEL);
866    }
867    Arc::new(Mutex::new(map))
868}
869
870fn create_context_map(ctx: Option<&'static str>) -> Arc<Mutex<U32Map<String>>> {
871    let mut map = U32Map::new();
872    map.insert("logger".to_string());
873    if let Some(string) = ctx {
874        map.insert(string.to_string()).expect("Added");
875    }
876    Arc::new(Mutex::new(map))
877}
878
879fn count_digits_base_10(mut number: usize) -> usize {
880    let mut digits = 1;
881    while number >= 10 {
882        number /= 10;
883        digits += 1;
884    }
885    digits
886}
887
888fn colorize_level(level: u8) -> ColoredString {
889    if level < 64 {
890        format!("{:03}", level).red()
891    } else if level < 128 {
892        format!("{:03}", level).yellow()
893    } else if level < 192 {
894        format!("{:03}", level).green()
895    } else if level < 255 {
896        format!("{:03}", level).cyan()
897    } else if level == 255 {
898        format!("{:03}", level).magenta()
899    } else {
900        unreachable!()
901    }
902}
903
904fn do_write_nocolor<W: std::io::Write, T: Display>(
905    writer: &mut W,
906    lvl: u8,
907    ctx: &str,
908    msg: &str,
909    now: T,
910    newlines: usize,
911    last_is_line: bool,
912) -> std::io::Result<()> {
913    if newlines > 1 {
914        for (idx, line) in msg.lines().enumerate() {
915            writeln!(
916                writer,
917                "{}: {:03} {} [{:0width$}/{}]: {}",
918                now,
919                lvl,
920                ctx,
921                idx + 1,
922                newlines,
923                line,
924                width = count_digits_base_10(newlines),
925            )?;
926        }
927        if last_is_line {
928            writeln!(
929                writer,
930                "{}: {:03} {} [{}/{}]: ",
931                now, lvl, ctx, newlines, newlines
932            )?;
933        }
934    } else {
935        writeln!(writer, "{}: {:03} {}: {}", now, lvl, ctx, msg,)?;
936    }
937    Ok(())
938}
939
940#[allow(clippy::too_many_arguments)]
941fn do_write_color<W: std::io::Write, T: Display>(
942    writer: &mut W,
943    lvl: u8,
944    ctx: &str,
945    msg: &str,
946    now: T,
947    newlines: usize,
948    last_is_line: bool,
949    color_counter: &mut usize,
950) -> std::io::Result<()> {
951    *color_counter = (*color_counter + 1) % 2;
952    let color;
953    match color_counter {
954        0 => color = "blue",
955        1 => color = "magenta",
956        _ => unimplemented!(),
957    }
958    let msg_color;
959    match color_counter {
960        0 => msg_color = "bright blue",
961        1 => msg_color = "bright magenta",
962        _ => unimplemented!(),
963    }
964    let msg = msg.color(color);
965    if newlines > 1 {
966        for (idx, line) in msg.lines().enumerate() {
967            writeln!(
968                writer,
969                "{}: {} {} {}: {}",
970                now.to_string().color(color),
971                colorize_level(lvl),
972                ctx.bright_green(),
973                format!(
974                    "[{:0width$}/{}]",
975                    idx + 1,
976                    newlines,
977                    width = count_digits_base_10(newlines),
978                )
979                .bright_yellow(),
980                line.color(msg_color),
981            )?;
982        }
983        if last_is_line {
984            writeln!(
985                writer,
986                "{}: {} {} {}: ",
987                now.to_string().color(color),
988                colorize_level(lvl),
989                ctx.bright_green(),
990                format!("[{}/{}]", newlines, newlines,).bright_yellow(),
991            )?;
992        }
993    } else {
994        writeln!(
995            writer,
996            "{}: {} {}: {}",
997            now.to_string().color(color),
998            colorize_level(lvl),
999            ctx.bright_green(),
1000            msg.color(msg_color),
1001        )?;
1002    }
1003    Ok(())
1004}
1005
1006fn do_write<C: Display + Send, W: std::io::Write>(
1007    writer: &mut W,
1008    lvl: u8,
1009    ctx: &str,
1010    msg: C,
1011    colorize: bool,
1012    color_counter: &mut usize,
1013) -> std::io::Result<()> {
1014    const ITEMS: &[chrono::format::Item] = {
1015        use chrono::format::{Fixed::*, Item::*, Numeric::*, Pad::*};
1016        &[
1017            Fixed(ShortMonthName),
1018            Literal(" "),
1019            Numeric(Day, None),
1020            Literal(" "),
1021            Numeric(Year, None),
1022            Literal(" "),
1023            Numeric(Hour, Zero),
1024            Literal(":"),
1025            Numeric(Minute, Zero),
1026            Literal(":"),
1027            Numeric(Second, Zero),
1028            Fixed(Nanosecond9),
1029            Fixed(TimezoneOffset),
1030        ]
1031    };
1032    let now = Local::now().format_with_items(ITEMS.iter().cloned());
1033    let msg = format!("{}", msg);
1034
1035    let mut newlines = 1;
1036    let mut last_is_line = false;
1037    for ch in msg.chars() {
1038        if ch == '\n' {
1039            newlines += 1;
1040            last_is_line = true;
1041        } else {
1042            last_is_line = false;
1043        }
1044    }
1045
1046    if colorize {
1047        do_write_color(
1048            writer,
1049            lvl,
1050            ctx,
1051            msg.as_str(),
1052            now,
1053            newlines,
1054            last_is_line,
1055            color_counter,
1056        )
1057    } else {
1058        do_write_nocolor(writer, lvl, ctx, msg.as_str(), now, newlines, last_is_line)
1059    }
1060}
1061
1062fn logger_thread<C: Display + Send, W: std::io::Write>(
1063    rx: Receiver<(u8, u32, C)>,
1064    dropped: Arc<AtomicUsize>,
1065    mut writer: W,
1066    context_specific_level: Arc<Mutex<HashMap<String, u8>>>,
1067    global_level: Arc<AtomicU8>,
1068    colorize: Arc<AtomicBool>,
1069    contexts: Arc<Mutex<U32Map<String>>>,
1070) {
1071    let mut color_counter = 0;
1072    let mut color;
1073    'outer_loop: loop {
1074        match rx.recv() {
1075            Ok(msg) => {
1076                color = colorize.load(Ordering::Relaxed);
1077                let contexts = contexts.lock();
1078                let ctx = match contexts {
1079                    Ok(contexts) => contexts,
1080                    Err(_poison) => {
1081                        let _ = do_write(
1082                            &mut writer,
1083                            0,
1084                            "logger",
1085                            "Context map lock has been poisoned. Exiting logger",
1086                            color,
1087                            &mut color_counter,
1088                        );
1089                        break 'outer_loop;
1090                    }
1091                };
1092                let ctx = ctx.get(&msg.1).expect("Entry is not in table");
1093                let lvls = context_specific_level.lock();
1094                match lvls {
1095                    Ok(lvls) => {
1096                        if let Some(lvl) = lvls.get(ctx) {
1097                            if msg.0 <= *lvl
1098                                && do_write(
1099                                    &mut writer,
1100                                    msg.0,
1101                                    ctx,
1102                                    msg.2,
1103                                    color,
1104                                    &mut color_counter,
1105                                )
1106                                .is_err()
1107                            {
1108                                break 'outer_loop;
1109                            }
1110                        } else if do_write(
1111                            &mut writer,
1112                            msg.0,
1113                            ctx,
1114                            msg.2,
1115                            color,
1116                            &mut color_counter,
1117                        )
1118                        .is_err()
1119                        {
1120                            break 'outer_loop;
1121                        }
1122                    }
1123                    Err(_poison) => {
1124                        let _ = do_write(
1125                            &mut writer,
1126                            0,
1127                            "logger",
1128                            "Context specific level lock has been poisoned. Exiting logger",
1129                            color,
1130                            &mut color_counter,
1131                        );
1132                        break 'outer_loop;
1133                    }
1134                }
1135            }
1136            Err(error @ RecvError { .. }) => {
1137                color = colorize.load(Ordering::Relaxed);
1138                let lvls = context_specific_level.lock();
1139                match lvls {
1140                    Ok(lvls) => {
1141                        if let Some(lvl) = lvls.get("logger") {
1142                            if LOGGER_QUIT_LEVEL <= *lvl
1143                                && LOGGER_QUIT_LEVEL <= global_level.load(Ordering::Relaxed)
1144                            {
1145                                let _ = do_write(
1146                                    &mut writer,
1147                                    LOGGER_QUIT_LEVEL,
1148                                    "logger",
1149                                    format!(
1150                                        "Unable to receive message. Exiting logger, reason={}",
1151                                        error
1152                                    ),
1153                                    color,
1154                                    &mut color_counter,
1155                                );
1156                            }
1157                        } else if LOGGER_QUIT_LEVEL <= global_level.load(Ordering::Relaxed) {
1158                            let _ = do_write(
1159                                &mut writer,
1160                                LOGGER_QUIT_LEVEL,
1161                                "logger",
1162                                format!(
1163                                    "Unable to receive message. Exiting logger, reason={}",
1164                                    error
1165                                ),
1166                                color,
1167                                &mut color_counter,
1168                            );
1169                        }
1170                    }
1171                    Err(_poison) => {
1172                        // Do nothing
1173                    }
1174                }
1175                break 'outer_loop;
1176            }
1177        }
1178        let dropped_messages = dropped.swap(0, Ordering::Relaxed);
1179        if dropped_messages > 0
1180            && do_write(
1181                &mut writer,
1182                64,
1183                "logger",
1184                format!(
1185                    "Logger dropped messages due to channel overflow, count={}",
1186                    dropped_messages
1187                ),
1188                color,
1189                &mut color_counter,
1190            )
1191            .is_err()
1192        {
1193            break 'outer_loop;
1194        }
1195    }
1196}
1197
1198// ---
1199
1200/// Print the value of the key-value pair as debug
1201pub struct InDebug<'a, T: Debug>(pub &'a T);
1202
1203impl<'a, T: Debug> Display for InDebug<'a, T> {
1204    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1205        self.0.fmt(f)
1206    }
1207}
1208
1209/// Print the value of the key-value pair as debug-pretty
1210pub struct InDebugPretty<'a, T: Debug>(pub &'a T);
1211
1212impl<'a, T: Debug> Display for InDebugPretty<'a, T> {
1213    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1214        write!(f, "{:#?}", self.0)
1215    }
1216}
1217
1218/// Print the value of the key-value pair as hexadecimal
1219pub struct InHex<'a, T: LowerHex>(pub &'a T);
1220
1221impl<'a, T: LowerHex> Display for InHex<'a, T> {
1222    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1223        self.0.fmt(f)
1224    }
1225}
1226
1227// ---
1228
1229#[cfg(test)]
1230mod tests {
1231    use super::*;
1232    use regex::Regex;
1233    use std::{fmt, io};
1234
1235    // ---
1236
1237    fn remove_time(line: &str) -> String {
1238        let regex = Regex::new(
1239            r"^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{1,2} \d+ \d{2}:\d{2}:\d{2}.\d{9}(\+|-)\d{4}: (.*)",
1240        )
1241        .unwrap();
1242        let x = regex.captures_iter(&line).next().unwrap()[3].to_string();
1243        x
1244    }
1245
1246    fn read_messages_without_date(delegate: fn(&Logger<Log>)) -> Vec<String> {
1247        let store = Arc::new(Mutex::new(vec![]));
1248        let writer = Store {
1249            store: store.clone(),
1250        };
1251        let logger = Logger::<Log>::spawn_with_writer("tst", writer);
1252        delegate(&logger);
1253        drop(logger);
1254        let string = String::from_utf8(store.lock().unwrap().to_vec()).unwrap();
1255        string.lines().map(remove_time).collect()
1256    }
1257
1258    // ---
1259
1260    enum Log {
1261        Static(&'static str),
1262        Generic(Generic),
1263    }
1264    impl Display for Log {
1265        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1266            match self {
1267                Log::Static(str) => write!(f, "{}", str),
1268                Log::Generic(generic) => generic.fmt(f),
1269            }
1270        }
1271    }
1272    impl From<Generic> for Log {
1273        fn from(generic: Generic) -> Self {
1274            Log::Generic(generic)
1275        }
1276    }
1277
1278    // ---
1279
1280    struct Void {}
1281    impl io::Write for Void {
1282        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1283            Ok(buf.len())
1284        }
1285        fn flush(&mut self) -> io::Result<()> {
1286            Ok(())
1287        }
1288    }
1289
1290    // ---
1291
1292    struct Store {
1293        pub store: Arc<Mutex<Vec<u8>>>,
1294    }
1295    impl io::Write for Store {
1296        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1297            let mut vector = self.store.lock().unwrap();
1298            vector.extend(buf);
1299            Ok(buf.len())
1300        }
1301        fn flush(&mut self) -> io::Result<()> {
1302            Ok(())
1303        }
1304    }
1305
1306    // ---
1307
1308    #[test]
1309    fn send_simple_string() {
1310        use std::fmt::Write;
1311        let logger = Logger::<String>::spawn("tst");
1312        assert_eq!(true, logger.info("Message"));
1313        let mut writer = logger.make_writer(128);
1314        write!(writer, "Message 2").unwrap();
1315        drop(writer);
1316    }
1317
1318    #[test]
1319    fn send_successful_message() {
1320        let logger = Logger::<Log>::spawn("tst");
1321        assert_eq!(true, logger.info(Log::Static("Message")));
1322    }
1323
1324    #[test]
1325    fn trace_is_disabled_by_default() {
1326        let logger = Logger::<Log>::spawn("tst");
1327        assert_eq!(false, logger.trace(Log::Static("Message")));
1328    }
1329
1330    #[test]
1331    fn debug_is_disabled_by_default() {
1332        let logger = Logger::<Log>::spawn("tst");
1333        assert_eq!(false, logger.debug(Log::Static("Message")));
1334    }
1335
1336    #[test]
1337    fn info_is_enabled_by_default() {
1338        let logger = Logger::<Log>::spawn("tst");
1339        assert_eq!(true, logger.info(Log::Static("Message")));
1340    }
1341
1342    #[test]
1343    fn warn_is_enabled_by_default() {
1344        let logger = Logger::<Log>::spawn("tst");
1345        assert_eq!(true, logger.warn(Log::Static("Message")));
1346    }
1347
1348    #[test]
1349    fn error_is_enabled_by_default() {
1350        let logger = Logger::<Log>::spawn("tst");
1351        assert_eq!(true, logger.error(Log::Static("Message")));
1352    }
1353
1354    #[test]
1355    fn custom_writer() {
1356        let writer = Void {};
1357        let logger = Logger::<Log>::spawn_with_writer("tst", writer);
1358        assert_eq!(true, logger.error(Log::Static("Message")));
1359    }
1360
1361    #[test]
1362    fn count_digits() {
1363        for i in 0..10 {
1364            assert_eq!(1, count_digits_base_10(i));
1365        }
1366        for i in 10..100 {
1367            assert_eq!(2, count_digits_base_10(i));
1368        }
1369        for i in &[100usize, 123, 321, 980] {
1370            assert_eq!(3, count_digits_base_10(*i));
1371        }
1372        assert_eq!(4, count_digits_base_10(1248));
1373        assert_eq!(10, count_digits_base_10(01329583467));
1374    }
1375
1376    #[test]
1377    fn ensure_proper_message_format() {
1378        let store = Arc::new(Mutex::new(vec![]));
1379        let writer = Store {
1380            store: store.clone(),
1381        };
1382        let logger = Logger::<Log>::spawn_with_writer("tst", writer);
1383        assert_eq!(true, logger.error(Log::Static("Message")));
1384        assert_eq!(true, logger.error(Log::Static("Second message")));
1385        let regex = Regex::new(
1386            r"^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{1,2} \d+ \d{2}:\d{2}:\d{2}.\d{9}(\+|-)\d{4}: 000 tst: Message\n",
1387        )
1388        .unwrap();
1389        drop(logger);
1390        assert!(regex.is_match(&String::from_utf8(store.lock().unwrap().to_vec()).unwrap()));
1391    }
1392
1393    #[test]
1394    fn ensure_ending_message_when_exit_1() {
1395        let store = Arc::new(Mutex::new(vec![]));
1396        let writer = Store {
1397            store: store.clone(),
1398        };
1399        let logger = Logger::<Log>::spawn_with_writer("tst", writer);
1400        logger.set_log_level(LOGGER_QUIT_LEVEL);
1401        let regex = Regex::new(
1402            r"^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{1,2} \d+ \d{2}:\d{2}:\d{2}.\d{9}(\+|-)\d{4}: 196 logger: Unable to receive message. Exiting logger, reason=receiving on an empty and disconnected channel\n$",
1403        )
1404        .unwrap();
1405        drop(logger);
1406        assert!(regex.is_match(&String::from_utf8(store.lock().unwrap().to_vec()).unwrap()));
1407    }
1408
1409    #[test]
1410    fn ensure_ending_message_when_exit_2() {
1411        let store = Arc::new(Mutex::new(vec![]));
1412        let writer = Store {
1413            store: store.clone(),
1414        };
1415        let logger = Logger::<Log>::spawn_with_writer("tst", writer);
1416        let regex = Regex::new(r"^$").unwrap();
1417        drop(logger);
1418        assert!(regex.is_match(&String::from_utf8(store.lock().unwrap().to_vec()).unwrap()));
1419    }
1420
1421    #[test]
1422    fn ensure_ending_message_when_exit_3() {
1423        let store = Arc::new(Mutex::new(vec![]));
1424        let writer = Store {
1425            store: store.clone(),
1426        };
1427        let logger = Logger::<Log>::spawn_with_writer("tst", writer);
1428        logger.set_log_level(LOGGER_QUIT_LEVEL);
1429        assert!(logger.set_context_specific_log_level("logger", 195));
1430        let regex = Regex::new(r"^$").unwrap();
1431        drop(logger);
1432        assert!(regex.is_match(&String::from_utf8(store.lock().unwrap().to_vec()).unwrap()));
1433    }
1434
1435    #[test]
1436    fn spawn_void() {
1437        let logger = Logger::<Log>::spawn_void();
1438        assert_eq!(0, logger.get_log_level());
1439        assert_eq!(true, logger.error(Log::Static("Message\n")));
1440        assert_eq!(false, logger.warn(Log::Static("Message\n")));
1441        assert_eq!(false, logger.info(Log::Static("Message\n")));
1442        assert_eq!(false, logger.debug(Log::Static("Message\n")));
1443        assert_eq!(false, logger.trace(Log::Static("Message\n")));
1444    }
1445
1446    #[test]
1447    fn ensure_proper_message_format_line_ending_with_newline() {
1448        let store = Arc::new(Mutex::new(vec![]));
1449        let writer = Store {
1450            store: store.clone(),
1451        };
1452        let logger = Logger::<Log>::spawn_with_writer("tst", writer);
1453        assert_eq!(true, logger.error(Log::Static("Message\n")));
1454        let regex = Regex::new(
1455            r"^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{1,2} \d+ \d{2}:\d{2}:\d{2}.\d{9}(\+|-)\d{4}: 000 tst \[1/2\]: Message
1456(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{1,2} \d+ \d{2}:\d{2}:\d{2}.\d{9}(\+|-)\d{4}: 000 tst \[2/2\]: \n",
1457        )
1458        .unwrap();
1459        drop(logger);
1460        assert!(
1461            regex.is_match(&String::from_utf8(store.lock().unwrap().to_vec()).unwrap()),
1462            "{}",
1463            String::from_utf8(store.lock().unwrap().to_vec()).unwrap()
1464        );
1465    }
1466
1467    #[test]
1468    fn nested_contexts() {
1469        let lines = read_messages_without_date(|lgr| {
1470            let client = lgr.clone_with_context("client");
1471            let laminar = client.clone_add_context("laminar");
1472            // WARNING: Racy if setting the log level after sending a message, so we use error here
1473            // which guarantees arrival
1474            error!(laminar, "Hello world");
1475            let vxdraw = laminar.clone_add_context("vxdraw");
1476            warn!(vxdraw, "Initializing something {}", "Something");
1477            laminar.set_this_log_level(0);
1478            warn!(vxdraw, "A warning");
1479            warn!(laminar, "Another warning");
1480        });
1481        assert_eq!(3, lines.len());
1482        assert_eq!("000 client-laminar: Hello world", lines[0]);
1483        assert_eq!(
1484            "064 client-laminar-vxdraw: Initializing something Something",
1485            lines[1]
1486        );
1487        assert_eq!("064 client-laminar-vxdraw: A warning", lines[2]);
1488    }
1489
1490    #[test]
1491    fn multistuff() {
1492        let lines = read_messages_without_date(|lgr| {
1493            trace!(lgr.clone_with_context("tracing"), "Message 1");
1494            debug!(lgr.clone_with_context("debugging"), "Message 2");
1495            info!(lgr.clone_with_context("infoing"), "Message 3");
1496            warn!(lgr.clone_with_context("warning"), "Message 4");
1497            error!(lgr.clone_with_context("erroring"), "Message 5");
1498            log!(123, lgr.clone_with_context("logging"), "Message 6");
1499            assert!(!lgr.set_context_specific_log_level("overdebug", 191));
1500            log!(
1501                191,
1502                lgr.clone_with_context("overdebug"),
1503                "This gets filtered"
1504            );
1505            lgr.set_log_level(191);
1506            let overdebug = lgr.clone_with_context("overdebug");
1507            assert!(lgr.set_context_specific_log_level("overdebug", 191));
1508            log!(191, overdebug, "Just above debugging worked");
1509        });
1510        assert_eq!(5, lines.len());
1511        assert_eq!("128 infoing: Message 3", lines[0]);
1512        assert_eq!("064 warning: Message 4", lines[1]);
1513        assert_eq!("000 erroring: Message 5", lines[2]);
1514        assert_eq!("123 logging: Message 6", lines[3]);
1515        assert_eq!("191 overdebug: Just above debugging worked", lines[4]);
1516    }
1517
1518    #[test]
1519    fn multiple_lines_count_correctly() {
1520        let store = Arc::new(Mutex::new(vec![]));
1521        let writer = Store {
1522            store: store.clone(),
1523        };
1524        let logger = Logger::<Log>::spawn_with_writer("tst", writer);
1525        assert_eq!(true, logger.error(Log::Static("Message\nPart\n2")));
1526        let regex = Regex::new(
1527            r#"^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{1,2} \d+ \d{2}:\d{2}:\d{2}.\d{9}(\+|-)\d{4}: 000 tst \[1/3\]: Message\n(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{1,2} \d+ \d{2}:\d{2}:\d{2}.\d{9}(\+|-)\d{4}: 000 tst \[2/3\]: Part\n(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{1,2} \d+ \d{2}:\d{2}:\d{2}.\d{9}(\+|-)\d{4}: 000 tst \[3/3\]: 2\n"#,
1528        )
1529        .unwrap();
1530        drop(logger);
1531        assert!(
1532            regex.is_match(&String::from_utf8(store.lock().unwrap().to_vec()).unwrap()),
1533            "{}",
1534            String::from_utf8(store.lock().unwrap().to_vec()).unwrap()
1535        );
1536    }
1537
1538    #[test]
1539    fn multiple_lines_count_correctly_trailing() {
1540        let store = Arc::new(Mutex::new(vec![]));
1541        let writer = Store {
1542            store: store.clone(),
1543        };
1544        let logger = Logger::<Log>::spawn_with_writer("tst", writer);
1545        assert_eq!(true, logger.error(Log::Static("Message\nPart\n2\n")));
1546        let regex = Regex::new(
1547            r#"^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{1,2} \d+ \d{2}:\d{2}:\d{2}.\d{9}(\+|-)\d{4}: 000 tst \[1/4\]: Message\n(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{1,2} \d+ \d{2}:\d{2}:\d{2}.\d{9}(\+|-)\d{4}: 000 tst \[2/4\]: Part\n(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{1,2} \d+ \d{2}:\d{2}:\d{2}.\d{9}(\+|-)\d{4}: 000 tst \[3/4\]: 2\n(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{1,2} \d+ \d{2}:\d{2}:\d{2}.\d{9}(\+|-)\d{4}: 000 tst \[4/4\]: \n"#,
1548        )
1549        .unwrap();
1550        drop(logger);
1551        assert!(
1552            regex.is_match(&String::from_utf8(store.lock().unwrap().to_vec()).unwrap()),
1553            "{}",
1554            String::from_utf8(store.lock().unwrap().to_vec()).unwrap()
1555        );
1556    }
1557
1558    #[test]
1559    fn generic() {
1560        let logger = Logger::<Generic>::spawn("tst");
1561        log!(123, logger, "lorem {}", "ipsum"; "dolor" => "sit", "amet" => 1234);
1562        trace!(logger, "lorem {}", "ipsum"; "a" => "b");
1563        debug!(logger, "lorem {}", "ipsum"; "a" => "b");
1564        info!(logger, "lorem {}", "ipsum"; "a" => "b");
1565        warn!(logger, "lorem {}", "ipsum"; "a" => "b");
1566        error!(logger, "lorem {}", "ipsum"; "a" => "b");
1567
1568        let ipsum = 1;
1569        info!(logger, "lorem {}", ipsum; "dolor" => "sit");
1570    }
1571
1572    #[test]
1573    fn custom_writer_with_generic() {
1574        let logger = Logger::<Log>::spawn("tst");
1575        assert_eq!(true, logger.error(Log::Static("Message")));
1576        assert_eq!(true, error!(logger, "Message"));
1577    }
1578
1579    #[rustfmt::skip]
1580    #[test]
1581    fn ensure_all_macro_variants_can_be_used() {
1582        let logger = Logger::<Log>::spawn("tst");
1583
1584        assert_eq!(false, trace!(logger, "Message"));
1585        assert_eq!(false, trace!(logger, "Message",));
1586        assert_eq!(false, trace!(logger, "Message {}", "argument"));
1587        assert_eq!(false, trace!(logger, "Message {}", "argument",));
1588        assert_eq!(false, trace!(logger, "Message";));
1589        assert_eq!(false, trace!(logger, "Message"; "a" => "b"));
1590        assert_eq!(false, trace!(logger, "Message"; "a" => "b",));
1591        assert_eq!(false, trace!(logger, "Message",;));
1592        assert_eq!(false, trace!(logger, "Message",; "a" => "b"));
1593        assert_eq!(false, trace!(logger, "Message",; "a" => "b",));
1594        assert_eq!(false, trace!(logger, "Message {}", "argument";));
1595        assert_eq!(false, trace!(logger, "Message {}", "argument"; "a" => "b"));
1596        assert_eq!(false, trace!(logger, "Message {}", "argument"; "a" => "b",));
1597        assert_eq!(false, trace!(logger, "Message {}", "argument",;));
1598        assert_eq!(false, trace!(logger, "Message {}", "argument",; "a" => "b"));
1599        assert_eq!(false, trace!(logger, "Message {}", "argument",; "a" => "b",));
1600
1601        assert_eq!(false, debug!(logger, "Message"));
1602        assert_eq!(false, debug!(logger, "Message",));
1603        assert_eq!(false, debug!(logger, "Message {}", "argument"));
1604        assert_eq!(false, debug!(logger, "Message {}", "argument",));
1605        assert_eq!(false, debug!(logger, "Message";));
1606        assert_eq!(false, debug!(logger, "Message"; "a" => "b"));
1607        assert_eq!(false, debug!(logger, "Message"; "a" => "b",));
1608        assert_eq!(false, debug!(logger, "Message",;));
1609        assert_eq!(false, debug!(logger, "Message",; "a" => "b"));
1610        assert_eq!(false, debug!(logger, "Message",; "a" => "b",));
1611        assert_eq!(false, debug!(logger, "Message {}", "argument";));
1612        assert_eq!(false, debug!(logger, "Message {}", "argument"; "a" => "b"));
1613        assert_eq!(false, debug!(logger, "Message {}", "argument"; "a" => "b",));
1614        assert_eq!(false, debug!(logger, "Message {}", "argument",;));
1615        assert_eq!(false, debug!(logger, "Message {}", "argument",; "a" => "b"));
1616        assert_eq!(false, debug!(logger, "Message {}", "argument",; "a" => "b",));
1617
1618        assert_eq!(true, info!(logger, "Message"));
1619        assert_eq!(true, info!(logger, "Message",));
1620        assert_eq!(true, info!(logger, "Message {}", "argument"));
1621        assert_eq!(true, info!(logger, "Message {}", "argument",));
1622        assert_eq!(true, info!(logger, "Message";));
1623        assert_eq!(true, info!(logger, "Message"; "a" => "b"));
1624        assert_eq!(true, info!(logger, "Message"; "a" => "b",));
1625        assert_eq!(true, info!(logger, "Message",;));
1626        assert_eq!(true, info!(logger, "Message",; "a" => "b"));
1627        assert_eq!(true, info!(logger, "Message",; "a" => "b",));
1628        assert_eq!(true, info!(logger, "Message {}", "argument";));
1629        assert_eq!(true, info!(logger, "Message {}", "argument"; "a" => "b"));
1630        assert_eq!(true, info!(logger, "Message {}", "argument"; "a" => "b",));
1631        assert_eq!(true, info!(logger, "Message {}", "argument",;));
1632        assert_eq!(true, info!(logger, "Message {}", "argument",; "a" => "b"));
1633        assert_eq!(true, info!(logger, "Message {}", "argument",; "a" => "b",));
1634
1635        assert_eq!(true, warn!(logger, "Message"));
1636        assert_eq!(true, warn!(logger, "Message",));
1637        assert_eq!(true, warn!(logger, "Message {}", "argument"));
1638        assert_eq!(true, warn!(logger, "Message {}", "argument",));
1639        assert_eq!(true, warn!(logger, "Message";));
1640        assert_eq!(true, warn!(logger, "Message"; "a" => "b"));
1641        assert_eq!(true, warn!(logger, "Message"; "a" => "b",));
1642        assert_eq!(true, warn!(logger, "Message",;));
1643        assert_eq!(true, warn!(logger, "Message",; "a" => "b"));
1644        assert_eq!(true, warn!(logger, "Message",; "a" => "b",));
1645        assert_eq!(true, warn!(logger, "Message {}", "argument";));
1646        assert_eq!(true, warn!(logger, "Message {}", "argument"; "a" => "b"));
1647        assert_eq!(true, warn!(logger, "Message {}", "argument"; "a" => "b",));
1648        assert_eq!(true, warn!(logger, "Message {}", "argument",;));
1649        assert_eq!(true, warn!(logger, "Message {}", "argument",; "a" => "b"));
1650        assert_eq!(true, warn!(logger, "Message {}", "argument",; "a" => "b",));
1651
1652        assert_eq!(true, error!(logger, "Message"));
1653        assert_eq!(true, error!(logger, "Message",));
1654        assert_eq!(true, error!(logger, "Message {}", "argument"));
1655        assert_eq!(true, error!(logger, "Message {}", "argument",));
1656        assert_eq!(true, error!(logger, "Message";));
1657        assert_eq!(true, error!(logger, "Message"; "a" => "b"));
1658        assert_eq!(true, error!(logger, "Message"; "a" => "b",));
1659        assert_eq!(true, error!(logger, "Message",;));
1660        assert_eq!(true, error!(logger, "Message",; "a" => "b"));
1661        assert_eq!(true, error!(logger, "Message",; "a" => "b",));
1662        assert_eq!(true, error!(logger, "Message {}", "argument";));
1663        assert_eq!(true, error!(logger, "Message {}", "argument"; "a" => "b"));
1664        assert_eq!(true, error!(logger, "Message {}", "argument"; "a" => "b",));
1665        assert_eq!(true, error!(logger, "Message {}", "argument",;));
1666        assert_eq!(true, error!(logger, "Message {}", "argument",; "a" => "b"));
1667        assert_eq!(true, error!(logger, "Message {}", "argument",; "a" => "b",));
1668
1669        let value = 123;
1670        assert_eq!(false, trace!(logger, "Message {}", value;; clone value));
1671        assert_eq!(false, debug!(logger, "Message {}", value;; clone value));
1672        assert_eq!(true, info!(logger, "Message {}", value;; clone value));
1673        assert_eq!(true, warn!(logger, "Message {}", value;; clone value));
1674        assert_eq!(true, error!(logger, "Message {}", value;; clone value));
1675        assert_eq!(true, log!(128, logger, "Message {}", value;; clone value));
1676    }
1677
1678    #[test]
1679    fn colorize() {
1680        let logger = Logger::<Log>::spawn("tst");
1681        logger.set_log_level(255);
1682        logger.set_colorize(true);
1683        logger.trace(Log::Static("A trace message"));
1684        logger.debug(Log::Static("A debug message"));
1685        logger.info(Log::Static("An info message"));
1686        logger.warn(Log::Static("A warning message"));
1687        logger.error(Log::Static("An error message"));
1688
1689        logger.info(Log::Static("On\nmultiple\nlines\n"));
1690    }
1691
1692    #[test]
1693    fn test_spawn_test() {
1694        let logger = Logger::<Log>::spawn_test();
1695
1696        assert_eq!(255, logger.get_log_level());
1697
1698        assert_eq!(true, logger.get_colorize());
1699
1700        #[cfg(debug_assertions)]
1701        assert_eq!(true, logger.trace(Log::Static("A trace message")));
1702        #[cfg(not(debug_assertions))]
1703        assert_eq!(false, logger.trace(Log::Static("A trace message")));
1704
1705        assert_eq!(true, logger.debug(Log::Static("A debug message")));
1706        assert_eq!(true, logger.info(Log::Static("An info message")));
1707        assert_eq!(true, logger.warn(Log::Static("A warning message")));
1708        assert_eq!(true, logger.error(Log::Static("An error message")));
1709    }
1710
1711    #[test]
1712    fn using_indebug() {
1713        let logger = Logger::<Log>::spawn("tst");
1714        #[derive(Clone)]
1715        struct Value {}
1716        impl Debug for Value {
1717            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1718                write!(f, "Debug Value")
1719            }
1720        }
1721        let value = Value {};
1722        info!(logger, "Message"; "value" => InDebug(&value); clone value);
1723        info!(logger, "Message"; "value" => InDebugPretty(&value); clone value);
1724    }
1725
1726    #[test]
1727    fn using_inhex() {
1728        let store = Arc::new(Mutex::new(vec![]));
1729        let writer = Store {
1730            store: store.clone(),
1731        };
1732        let logger = Logger::<Log>::spawn_with_writer("tst", writer);
1733        logger.set_log_level(128);
1734
1735        info!(logger, "Message"; "value" => InHex(&!127u32));
1736
1737        drop(logger);
1738        assert_eq!(
1739            "128 tst: Message, value=ffffff80",
1740            remove_time(&String::from_utf8(store.lock().unwrap().to_vec()).unwrap()),
1741        );
1742    }
1743
1744    #[test]
1745    fn indebug() {
1746        assert_eq!("[1, 2, 3]", format!("{}", InDebug(&[1, 2, 3])));
1747        assert_eq!(
1748            "[\n    1,\n    2,\n    3,\n]",
1749            format!("{}", InDebugPretty(&[1, 2, 3]))
1750        );
1751    }
1752
1753    #[test]
1754    fn inhex() {
1755        assert_eq!("ffffff80", format!("{}", InHex(&!127u32)));
1756    }
1757
1758    #[test]
1759    fn logpass() {
1760        let logger = Logger::<Log>::spawn("tst").to_logpass();
1761        info!(logger, "Message");
1762    }
1763
1764    #[test]
1765    fn compatibility_layer() {
1766        let logger = Logger::<Log>::spawn("tst");
1767        struct MyLibrary {
1768            log: Logpass,
1769        }
1770        impl MyLibrary {
1771            pub fn new(log: Compatibility) -> Self {
1772                Self {
1773                    log: Logpass::from_compatibility(log),
1774                }
1775            }
1776            pub fn function(&mut self) {
1777                info!(self.log, "Compatibility layer");
1778            }
1779        }
1780
1781        let mut my_lib = MyLibrary::new(logger.to_compatibility());
1782        my_lib.function();
1783    }
1784}