1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use crate::*;


/// Logger is the base type that actually handle the loggings
pub struct Logger {
  pub(crate) name       : String,
  pub(crate) write_level: Level,
  pub(crate) print_level: Level,
  pub(crate) writer     : Writer,
  pub(crate) for_write  : fn(&Context) -> String,
  pub(crate) for_print  : fn(&Context) -> String,
}


impl Logger {
  /// Creating a new logger with the given name & default configurations.
  pub fn default(name: impl Into<String>) -> Self {
    Self::new::<DefaultFormatter>(name, Config::default())
  }
}


impl Logger {
  /// Creating a new logger with the given name.
  pub fn new<F: Formatter>(name: impl Into<String>, config: Config) -> Self {
    Logger {
      name       : name.into(),
      write_level: config.write_level,
      print_level: config.print_level,
      writer     : Writer::new(config),
      for_write  : F::for_write,
      for_print  : F::for_print,
    }
  }

  /// Create a new session from the logger.
  ///
  /// There are two types of session: silent & non-silent. Silent session will not print the header
  /// and footer of the session when no message is logged before the session is dropped. It's useful
  /// session that may potentially not log anything. Non-silent session will always print the header
  /// and footer of the session.
  ///
  /// Due to the uncertainty of if the session will log anything, the header will be deferred until
  /// the first log. If the session logged anything, it'll act like a non-silent session.
  #[track_caller]
  pub fn session(&self, name: &str, silent: bool) -> Session {
    Session::new(name, SessionSrc::Logger(self), silent)
  }

  /// Create a new session from the session and execute the callable with the session passed in.
  ///
  /// This can be handy for isolating the environment of each callable while also providing a common
  /// logging interface for any callable that needs to be logged.
  #[track_caller]
  pub fn session_then<F, T>(&self, name: &str, silent: bool, callable: F) -> T
  where F: FnOnce(Session) -> T
  {
    callable(self.session(name, silent))
  }
}


impl LoggableInner for Logger {
  fn log(&self, level: Level, message: &str) {
    let ctx = Context::new_message(
      Source::new(&self.name),
      level,
      message
    );

    if level >= self.print_level {
      println!("{}", (self.for_print)(&ctx));
    }

    if level >= self.write_level {
      self.writer.lock().unwrap()
        .write((self.for_write)(&ctx));
    }
  }
}


impl Loggable for Logger {
  fn root_name(&self) -> &str {
    &self.name
  }

  fn name(&self) -> &str {
    &self.name
  }

  fn path(&self) -> String {
    self.writer.lock().unwrap().path.clone()
  }

  fn write_level(&self) -> Level {
    self.write_level
  }

  fn print_level(&self) -> Level {
    self.print_level
  }
}