1use crate::log_impl::setup_log;
2use crate::{
3 formatter::{FormatRecord, TimeFormatter},
4 log_impl::{GlobalLogger, LogSink, LogSinkTrait},
5 time::Timer,
6};
7use log::{Level, LevelFilter, Record};
8use std::hash::{DefaultHasher, Hash, Hasher};
9use std::path::Path;
10
11#[derive(Default)]
14pub struct Builder {
15 pub dynamic: bool,
22
23 pub rotation_signals: Vec<i32>,
26
27 pub panic: bool,
29
30 pub continue_when_panic: bool,
32
33 pub sinks: Vec<Box<dyn SinkConfigTrait>>,
35
36 #[cfg(feature = "tracing")]
38 #[cfg_attr(docsrs, doc(cfg(feature = "tracing")))]
39 pub tracing_global: bool,
40}
41
42impl Builder {
43 pub fn new() -> Self {
44 Self::default()
45 }
46
47 #[cfg(feature = "tracing")]
49 #[cfg_attr(docsrs, doc(cfg(feature = "tracing")))]
50 #[inline]
51 pub fn tracing_global(mut self) -> Self {
52 self.tracing_global = true;
53 self
54 }
55
56 #[inline]
59 pub fn test(mut self) -> Self {
60 self.dynamic = true;
61 self.rotation_signals.clear();
62 self
63 }
64
65 #[inline]
67 pub fn signal(mut self, signal: i32) -> Self {
68 self.rotation_signals.push(signal);
69 self
70 }
71
72 #[inline]
74 pub fn add_sink<S: SinkConfigTrait>(mut self, config: S) -> Self {
75 self.sinks.push(Box::new(config));
76 self
77 }
78
79 #[inline]
81 pub fn get_max_level(&self) -> LevelFilter {
82 let mut max_level = Level::Error;
83 for sink in &self.sinks {
84 let level = sink.get_level();
85 if level > max_level {
86 max_level = level;
87 }
88 }
89 return max_level.to_level_filter();
90 }
91
92 #[inline]
94 pub(crate) fn cal_checksum(&self) -> u64 {
95 let mut hasher = Box::new(DefaultHasher::new()) as Box<dyn Hasher>;
96 self.dynamic.hash(&mut hasher);
97 self.rotation_signals.hash(&mut hasher);
98 self.panic.hash(&mut hasher);
99 self.continue_when_panic.hash(&mut hasher);
100 for sink in &self.sinks {
101 sink.write_hash(&mut hasher);
102 }
103 hasher.finish()
104 }
105
106 #[inline]
107 pub(crate) fn build_sinks(&self) -> std::io::Result<Vec<LogSink>> {
108 let mut sinks = Vec::new();
109 for config in &self.sinks {
110 let logger_sink = config.build();
111 if let Err(e) = logger_sink.open() {
112 eprintln!("failed to open log sink: {:?}", e);
113 return Err(e);
114 }
115 sinks.push(logger_sink);
116 }
117 Ok(sinks)
118 }
119
120 pub fn build(self) -> std::io::Result<&'static GlobalLogger> {
127 setup_log(self)
128 }
129}
130
131pub(crate) trait SinkConfigBuild {
132 fn build(&self) -> LogSink;
134}
135
136#[allow(private_bounds)]
137pub trait SinkConfigTrait: 'static + SinkConfigBuild {
138 fn get_level(&self) -> Level;
140 #[allow(dead_code)]
142 fn get_file_path(&self) -> Option<Box<Path>>;
143 fn write_hash(&self, hasher: &mut Box<dyn Hasher>);
145}
146
147pub type FormatFunc = fn(FormatRecord) -> String;
148
149#[derive(Clone, Hash)]
151pub struct LogFormat {
152 time_fmt: &'static str,
153 format_fn: FormatFunc,
154}
155
156impl LogFormat {
157 pub const fn new(time_fmt: &'static str, format_fn: FormatFunc) -> Self {
180 Self { time_fmt, format_fn }
181 }
182
183 #[inline(always)]
184 pub(crate) fn process(&self, now: &Timer, record: &Record) -> String {
185 let time = TimeFormatter { now, fmt_str: self.time_fmt };
186 let r = FormatRecord { record, time };
187 return (self.format_fn)(r);
188 }
189}