1#![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
260pub type Compatibility =
293 Box<dyn Fn(u8, Box<dyn Fn(&mut fmt::Formatter) -> fmt::Result + Send + Sync>)>;
294pub type Logger<C> = LoggerV2Async<C>;
296
297#[derive(Clone)]
305pub struct LoggerV2Async<C: Display + Send> {
306 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
317pub trait GenericLogger {
321 fn log_generic(&self, level: u8, message: Generic);
323 fn to_logpass(self) -> Logpass;
325 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
349pub enum Logpass {
356 Compatibility(Compatibility),
359 PassThrough(Box<dyn GenericLogger>),
362}
363
364impl Logpass {
365 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 pub fn from_compatibility(compatibility: Compatibility) -> Self {
378 Logpass::Compatibility(compatibility)
379 }
380}
381
382struct 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#[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#[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
417static 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 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 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 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 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 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 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 pub fn set_log_level(&self, level: u8) {
684 self.level.store(level, Ordering::Relaxed);
685 }
686
687 pub fn get_log_level(&self) -> u8 {
689 self.level.load(Ordering::Relaxed)
690 }
691
692 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 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 pub fn set_colorize(&self, on: bool) {
719 self.colorize.store(on, Ordering::Relaxed);
720 }
721
722 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 pub fn log(&self, level: u8, message: impl Into<C>) {
752 self.log_internal(level, message);
753 }
754
755 #[cfg(not(debug_assertions))]
759 pub fn trace(&self, _: impl Into<C>) {}
760
761 #[cfg(debug_assertions)]
765 pub fn trace(&self, message: impl Into<C>) {
766 self.log(255, message)
767 }
768
769 pub fn debug(&self, message: impl Into<C>) {
771 self.log(192, message)
772 }
773
774 pub fn info(&self, message: impl Into<C>) {
776 self.log(128, message)
777 }
778
779 pub fn warn(&self, message: impl Into<C>) {
781 self.log(64, message)
782 }
783
784 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 pub fn log(&self, level: u8, message: impl Into<C>) -> bool {
796 self.log_internal(level, message)
797 }
798
799 #[cfg(not(debug_assertions))]
803 pub fn trace(&self, _: impl Into<C>) -> bool {
804 false
805 }
806
807 #[cfg(debug_assertions)]
811 pub fn trace(&self, message: impl Into<C>) -> bool {
812 self.log(255, message)
813 }
814
815 pub fn debug(&self, message: impl Into<C>) -> bool {
817 self.log(192, message)
818 }
819
820 pub fn info(&self, message: impl Into<C>) -> bool {
822 self.log(128, message)
823 }
824
825 pub fn warn(&self, message: impl Into<C>) -> bool {
827 self.log(64, message)
828 }
829
830 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 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
859fn 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 }
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
1198pub 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
1209pub 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
1218pub 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#[cfg(test)]
1230mod tests {
1231 use super::*;
1232 use regex::Regex;
1233 use std::{fmt, io};
1234
1235 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 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 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 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 #[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 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}