captains_log/
log_impl.rs

1use crate::{buf_file_impl::LogSinkBufFile, console_impl::LogSinkConsole, file_impl::LogSinkFile};
2use crate::{config::Builder, time::Timer};
3use arc_swap::ArcSwap;
4use backtrace::Backtrace;
5use lazy_static::lazy_static;
6use parking_lot::Mutex;
7use signal_hook::iterator::Signals;
8use std::mem::transmute;
9use std::sync::{
10    atomic::{AtomicBool, Ordering},
11    Arc,
12};
13use std::thread;
14
15#[enum_dispatch]
16pub(crate) trait LogSinkTrait {
17    fn reopen(&self) -> std::io::Result<()>;
18
19    fn log(&self, now: &Timer, r: &log::Record);
20
21    fn flush(&self);
22}
23
24#[enum_dispatch(LogSinkTrait)]
25pub(crate) enum LogSink {
26    File(LogSinkFile),
27    BufFile(LogSinkBufFile),
28    Console(LogSinkConsole),
29    #[cfg(feature = "syslog")]
30    Syslog(crate::syslog::LogSinkSyslog),
31}
32
33/// Global static structure to hold the logger
34struct GlobalLogger {
35    /// checksum for config comparison
36    config_checksum: u64,
37    /// Global static needs initialization when declaring,
38    /// default to be empty
39    inner: Option<LoggerInner>,
40    signal_listener: AtomicBool,
41}
42
43enum LoggerInner {
44    Once(Vec<LogSink>),
45    // using ArcSwap has more cost
46    Dyn(ArcSwap<Vec<LogSink>>),
47}
48
49#[inline(always)]
50fn panic_or_error() {
51    #[cfg(debug_assertions)]
52    {
53        panic!("GlobalLogger cannot be initialized twice on dynamic==false");
54    }
55    #[cfg(not(debug_assertions))]
56    {
57        eprintln!("GlobalLogger cannot be initialized twice on dynamic==false");
58    }
59}
60
61impl LoggerInner {
62    #[allow(dead_code)]
63    fn set(&self, sinks: Vec<LogSink>) {
64        match &self {
65            Self::Once(_) => {
66                panic_or_error();
67            }
68            Self::Dyn(d) => {
69                d.store(Arc::new(sinks));
70            }
71        }
72    }
73}
74
75impl GlobalLogger {
76    pub fn reopen(&mut self) -> std::io::Result<()> {
77        if let Some(inner) = self.inner.as_ref() {
78            match &inner {
79                LoggerInner::Once(inner) => {
80                    for sink in inner.iter() {
81                        sink.reopen()?;
82                    }
83                }
84                LoggerInner::Dyn(inner) => {
85                    let sinks = inner.load();
86                    for sink in sinks.iter() {
87                        sink.reopen()?;
88                    }
89                }
90            }
91        }
92        println!("log sinks re-opened");
93        Ok(())
94    }
95
96    #[allow(dead_code)]
97    fn init(&mut self, builder: &Builder) -> std::io::Result<bool> {
98        let new_checksum = builder.cal_checksum();
99        if self.inner.is_some() {
100            if self.config_checksum == new_checksum {
101                // Config is the same, no need to reinit
102                self.reopen()?;
103                return Ok(true);
104            }
105            if !builder.dynamic {
106                panic_or_error();
107                return Ok(false);
108            }
109        }
110        let mut sinks = Vec::new();
111        for config in &builder.sinks {
112            let logger_sink = config.build();
113            logger_sink.reopen()?;
114            sinks.push(logger_sink);
115        }
116
117        if let Some(inner) = self.inner.as_ref() {
118            inner.set(sinks);
119        } else {
120            if builder.dynamic {
121                self.inner.replace(LoggerInner::Dyn(ArcSwap::new(Arc::new(sinks))));
122            } else {
123                self.inner.replace(LoggerInner::Once(sinks));
124            }
125        }
126        self.config_checksum = new_checksum;
127
128        let _ = unsafe { log::set_logger(transmute::<&Self, &'static Self>(self)) };
129
130        // panic hook can be set multiple times
131        if builder.continue_when_panic {
132            std::panic::set_hook(Box::new(panic_no_exit_hook));
133        } else {
134            std::panic::set_hook(Box::new(panic_and_exit_hook));
135        }
136        Ok(true)
137    }
138}
139
140impl log::Log for GlobalLogger {
141    #[inline(always)]
142    fn enabled(&self, _m: &log::Metadata) -> bool {
143        true
144    }
145
146    #[inline(always)]
147    fn log(&self, r: &log::Record) {
148        let now = Timer::new();
149        if let Some(inner) = self.inner.as_ref() {
150            match &inner {
151                LoggerInner::Once(inner) => {
152                    for sink in inner.iter() {
153                        sink.log(&now, r);
154                    }
155                }
156                LoggerInner::Dyn(inner) => {
157                    let sinks = inner.load();
158                    for sink in sinks.iter() {
159                        sink.log(&now, r);
160                    }
161                }
162            }
163        }
164    }
165
166    /// Can be call manually on program shutdown (If you have a buffered log sink)
167    ///
168    /// # Example
169    ///
170    /// ``` rust
171    /// log::logger().flush();
172    /// ```
173    fn flush(&self) {
174        if let Some(inner) = self.inner.as_ref() {
175            match &inner {
176                LoggerInner::Once(inner) => {
177                    for sink in inner.iter() {
178                        sink.flush();
179                    }
180                }
181                LoggerInner::Dyn(inner) => {
182                    let sinks = inner.load();
183                    for sink in sinks.iter() {
184                        sink.flush();
185                    }
186                }
187            }
188        }
189    }
190}
191
192lazy_static! {
193    // Mutex only access on init and reopen, bypassed while logging,
194    // because crate log only use const raw pointer to access GlobalLogger.
195    static ref GLOBAL_LOGGER: Mutex<GlobalLogger> = Mutex::new(GlobalLogger {
196        config_checksum: 0,
197        inner: None ,
198        signal_listener: AtomicBool::new(false),
199    });
200}
201
202/// log handle for panic hook
203#[doc(hidden)]
204pub fn log_panic(info: &std::panic::PanicHookInfo) {
205    let bt = Backtrace::new();
206    let mut record = log::Record::builder();
207    record.level(log::Level::Error);
208    if let Some(loc) = info.location() {
209        record.file(Some(loc.file())).line(Some(loc.line()));
210    }
211    log::logger().log(&record.args(format_args!("panic occur: {}\ntrace: {:?}", info, bt)).build());
212    eprint!("panic occur: {} at {:?}\ntrace: {:?}", info, info.location(), bt);
213}
214
215#[inline(always)]
216fn panic_and_exit_hook(info: &std::panic::PanicHookInfo) {
217    log_panic(info);
218    log::logger().flush();
219    let msg = format!("{}", info).to_string();
220    std::panic::resume_unwind(Box::new(msg));
221}
222
223#[inline(always)]
224fn panic_no_exit_hook(info: &std::panic::PanicHookInfo) {
225    log_panic(info);
226    eprint!("not debug version, so don't exit process");
227    log::logger().flush();
228}
229
230fn signal_listener(signals: Vec<i32>) {
231    let started;
232    {
233        let global_logger = GLOBAL_LOGGER.lock();
234        started = global_logger.signal_listener.swap(true, Ordering::SeqCst);
235    }
236    if started {
237        // NOTE: Once logger started to listen signal, does not support dynamic reconfigure.
238        eprintln!("signal listener already started");
239        return;
240    }
241    thread::spawn(move || {
242        let mut signals = Signals::new(&signals).unwrap();
243        for __sig in signals.forever() {
244            {
245                let mut global_logger = GLOBAL_LOGGER.lock();
246                let _ = global_logger.reopen();
247            }
248        }
249    });
250}
251
252/// Initialize global logger from Builder
253pub fn setup_log(builder: Builder) -> Result<(), ()> {
254    {
255        let mut global_logger = GLOBAL_LOGGER.lock();
256
257        match global_logger.init(&builder) {
258            Err(e) => {
259                println!("Initialize logger failed: {:?}", e);
260                return Err(());
261            }
262            Ok(false) => return Err(()),
263            Ok(true) => {}
264        }
265        log::set_max_level(builder.get_max_level());
266    }
267    let signals = builder.rotation_signals.clone();
268    if signals.len() > 0 {
269        signal_listener(signals);
270    }
271    Ok(())
272}