1use crate::log_impl::setup_log;
2use crate::{
3 console_impl::LoggerSinkConsole,
4 file_impl::LoggerSinkFile,
5 formatter::{FormatRecord, TimeFormatter},
6 log_impl::LoggerSink,
7 time::Timer,
8};
9use log::{Level, LevelFilter, Record};
10use std::path::Path;
11
12#[derive(Default)]
16pub struct Builder {
17 pub dynamic: bool,
24
25 pub rotation_signals: Vec<i32>,
28
29 pub panic: bool,
31
32 pub continue_when_panic: bool,
34
35 pub sinks: Vec<Box<dyn SinkConfigTrait>>,
37}
38
39impl Builder {
40 pub fn new() -> Self {
41 Self::default()
42 }
43
44 pub fn signal(mut self, signal: i32) -> Self {
46 self.rotation_signals.push(signal);
47 self
48 }
49
50 pub fn file(mut self, config: LogFile) -> Self {
52 self.sinks.push(Box::new(config));
53 self
54 }
55
56 pub fn console(mut self, config: LogConsole) -> Self {
58 self.sinks.push(Box::new(config));
59 self
60 }
61
62 pub fn get_max_level(&self) -> LevelFilter {
64 let mut max_level = Level::Error;
65 for sink in &self.sinks {
66 let level = sink.get_level();
67 if level > max_level {
68 max_level = level;
69 }
70 }
71 return max_level.to_level_filter();
72 }
73
74 pub fn build(self) -> Result<(), ()> {
77 setup_log(self)
78 }
79}
80
81pub trait SinkConfigTrait {
82 fn get_level(&self) -> Level;
83 fn get_file_path(&self) -> Option<Box<Path>>;
85 fn build(&self) -> LoggerSink;
86}
87
88pub type FormatFunc = fn(FormatRecord) -> String;
89
90#[derive(Clone)]
91pub struct LogFormat {
93 time_fmt: String,
94 format_fn: FormatFunc,
95}
96
97impl LogFormat {
98 pub fn new(time_fmt: &str, format_fn: FormatFunc) -> Self {
121 Self { time_fmt: time_fmt.to_string(), format_fn }
122 }
123
124 #[inline(always)]
125 pub(crate) fn process(&self, now: &Timer, record: &Record) -> String {
126 let time = TimeFormatter { now, fmt_str: &self.time_fmt };
127 let r = FormatRecord { record, time };
128 return (self.format_fn)(r);
129 }
130}
131
132pub struct LogFile {
134 pub dir: String,
136
137 pub level: Level,
139
140 pub name: String,
142
143 pub format: LogFormat,
144
145 pub file_path: Box<Path>,
147}
148
149impl LogFile {
150 pub fn new(dir: &str, name: &str, level: Level, format: LogFormat) -> Self {
151 let file_path = Path::new(dir).join(Path::new(name)).into_boxed_path();
152 Self { dir: dir.to_string(), name: name.to_string(), level, format, file_path }
153 }
154}
155
156impl SinkConfigTrait for LogFile {
157 fn get_level(&self) -> Level {
158 self.level
159 }
160
161 fn get_file_path(&self) -> Option<Box<Path>> {
162 Some(self.file_path.clone())
163 }
164
165 fn build(&self) -> LoggerSink {
166 LoggerSink::File(LoggerSinkFile::new(self))
167 }
168}
169
170#[derive(Copy, Clone, Debug)]
171#[repr(u8)]
172pub enum ConsoleTarget {
173 Stdout = 1,
174 Stderr = 2,
175}
176
177pub struct LogConsole {
178 pub target: ConsoleTarget,
179
180 pub level: Level,
182
183 pub format: LogFormat,
184}
185
186impl LogConsole {
187 pub fn new(target: ConsoleTarget, level: Level, format: LogFormat) -> Self {
188 Self { target, level, format }
189 }
190}
191
192impl SinkConfigTrait for LogConsole {
193 fn get_level(&self) -> Level {
194 self.level
195 }
196
197 fn get_file_path(&self) -> Option<Box<Path>> {
198 None
199 }
200
201 fn build(&self) -> LoggerSink {
202 LoggerSink::Console(LoggerSinkConsole::new(self))
203 }
204}