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 signal_hook::iterator::Signals;
6use std::cell::UnsafeCell;
7use std::mem::transmute;
8use std::sync::{
9    atomic::{AtomicBool, AtomicU64, Ordering},
10    Arc,
11};
12use std::thread;
13
14#[enum_dispatch]
15pub(crate) trait LogSinkTrait {
16    fn open(&self) -> std::io::Result<()>;
17
18    fn reopen(&self) -> std::io::Result<()>;
19
20    fn log(&self, now: &Timer, r: &log::Record);
21
22    fn flush(&self);
23}
24
25#[enum_dispatch(LogSinkTrait)]
26pub enum LogSink {
27    File(LogSinkFile),
28    BufFile(LogSinkBufFile),
29    Console(LogSinkConsole),
30    #[cfg(feature = "syslog")]
31    Syslog(crate::syslog::LogSinkSyslog),
32    #[cfg(feature = "ringfile")]
33    RingFile(crate::ring::LogSinkRingFile),
34}
35
36struct GlobalLoggerConainer {
37    logger: UnsafeCell<GlobalLogger>,
38    lock: AtomicBool,
39}
40
41struct GlobalLoggerGuard<'a>(&'a GlobalLoggerConainer);
42
43impl Drop for GlobalLoggerGuard<'_> {
44    fn drop(&mut self) {
45        self.0.unlock();
46    }
47}
48
49impl GlobalLoggerConainer {
50    const fn new() -> Self {
51        Self {
52            logger: UnsafeCell::new(GlobalLogger {
53                config_checksum: AtomicU64::new(0),
54                inner: None,
55                signal_listener: AtomicBool::new(false),
56            }),
57            lock: AtomicBool::new(false),
58        }
59    }
60
61    fn get_logger_mut(&self) -> &mut GlobalLogger {
62        unsafe { transmute(self.logger.get()) }
63    }
64
65    fn get_logger(&self) -> &GlobalLogger {
66        unsafe { transmute(self.logger.get()) }
67    }
68
69    fn lock<'a>(&'a self) -> GlobalLoggerGuard<'a> {
70        while self
71            .lock
72            .compare_exchange_weak(false, true, Ordering::SeqCst, Ordering::Relaxed)
73            .is_err()
74        {
75            // Normally this does not contend, if your test does not run concurrently.
76            std::thread::yield_now();
77        }
78        GlobalLoggerGuard(self)
79    }
80
81    fn unlock(&self) {
82        self.lock.store(false, Ordering::SeqCst);
83    }
84
85    /// Return Ok(false) when reinit, Ok(true) when first init, Err for error
86    fn try_setup(&self, builder: &Builder) -> Result<bool, ()> {
87        let _guard = self.lock();
88        let res = { self.get_logger().check_the_same(builder) };
89        match res {
90            Some(true) => {
91                if let Err(e) = self.get_logger().open() {
92                    eprintln!("failed to open log sink: {:?}", e);
93                    return Err(());
94                }
95                return Ok(false);
96            }
97            Some(false) => {
98                if !builder.dynamic {
99                    panic_or_error();
100                    return Err(());
101                }
102                let res = self.get_logger().reinit(builder);
103                res?;
104                // reset the log level
105                log::set_max_level(builder.get_max_level());
106                return Ok(false);
107            }
108            None => {
109                let res = { self.get_logger_mut().init(builder) };
110                res?;
111                return Ok(true);
112            }
113        }
114    }
115}
116
117unsafe impl Send for GlobalLoggerConainer {}
118unsafe impl Sync for GlobalLoggerConainer {}
119
120/// Initialize global logger from Builder
121pub fn setup_log(builder: Builder) -> Result<(), ()> {
122    if let Ok(true) = GLOBAL_LOGGER.try_setup(&builder) {
123        let logger = GLOBAL_LOGGER.get_logger();
124        // Set logger can only be called once
125        if let Err(e) = log::set_logger(logger) {
126            eprintln!("log::set_logger return error: {:?}", e);
127            return Err(());
128        }
129        log::set_max_level(builder.get_max_level());
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        let signals = builder.rotation_signals.clone();
137        if signals.len() > 0 {
138            if false == logger.signal_listener.swap(true, Ordering::SeqCst) {
139                thread::spawn(move || {
140                    GLOBAL_LOGGER.get_logger().listener_for_signal(signals);
141                });
142            }
143        }
144    }
145    Ok(())
146}
147
148/// Global static structure to hold the logger
149struct GlobalLogger {
150    /// checksum for config comparison
151    config_checksum: AtomicU64,
152    /// Global static needs initialization when declaring,
153    /// default to be empty
154    inner: Option<LoggerInner>,
155    signal_listener: AtomicBool,
156}
157
158enum LoggerInner {
159    Once(Vec<LogSink>),
160    // using ArcSwap has more cost
161    Dyn(ArcSwap<Vec<LogSink>>),
162}
163
164#[inline(always)]
165fn panic_or_error() {
166    #[cfg(debug_assertions)]
167    {
168        panic!("GlobalLogger cannot be initialized twice on dynamic==false");
169    }
170    #[cfg(not(debug_assertions))]
171    {
172        eprintln!("GlobalLogger cannot be initialized twice on dynamic==false");
173    }
174}
175
176impl LoggerInner {
177    #[allow(dead_code)]
178    fn set(&self, sinks: Vec<LogSink>) {
179        match &self {
180            Self::Once(_) => {
181                panic_or_error();
182            }
183            Self::Dyn(d) => {
184                d.store(Arc::new(sinks));
185            }
186        }
187    }
188}
189
190impl GlobalLogger {
191    fn listener_for_signal(&self, signals: Vec<i32>) {
192        println!("signal_listener started");
193        let mut signals = Signals::new(&signals).unwrap();
194        for __sig in signals.forever() {
195            let _ = self.reopen();
196        }
197        println!("signal_listener exit");
198    }
199
200    /// On program/test Initialize
201    fn open(&self) -> std::io::Result<()> {
202        if let Some(inner) = self.inner.as_ref() {
203            match &inner {
204                LoggerInner::Once(inner) => {
205                    for sink in inner.iter() {
206                        sink.open()?;
207                    }
208                }
209                LoggerInner::Dyn(inner) => {
210                    let sinks = inner.load();
211                    for sink in sinks.iter() {
212                        sink.open()?;
213                    }
214                }
215            }
216        }
217        println!("log sinks opened");
218        Ok(())
219    }
220
221    /// On signal to reopen file.
222    pub fn reopen(&self) -> std::io::Result<()> {
223        if let Some(inner) = self.inner.as_ref() {
224            match &inner {
225                LoggerInner::Once(inner) => {
226                    for sink in inner.iter() {
227                        sink.reopen()?;
228                    }
229                }
230                LoggerInner::Dyn(inner) => {
231                    let sinks = inner.load();
232                    for sink in sinks.iter() {
233                        sink.reopen()?;
234                    }
235                }
236            }
237        }
238        println!("log sinks re-opened");
239        Ok(())
240    }
241
242    /// Return Some(true) to skip, Some(false) to reinit, None to init
243    fn check_the_same(&self, builder: &Builder) -> Option<bool> {
244        if self.inner.is_some() {
245            return Some(self.config_checksum.load(Ordering::Acquire) == builder.cal_checksum());
246        }
247        None
248    }
249
250    /// Re-configure the logger sink
251    fn reinit(&self, builder: &Builder) -> Result<(), ()> {
252        let sinks = builder.build_sinks()?;
253        if let Some(inner) = self.inner.as_ref() {
254            inner.set(sinks);
255            self.config_checksum.store(builder.cal_checksum(), Ordering::Release);
256        } else {
257            unreachable!();
258        }
259        Ok(())
260    }
261
262    #[allow(dead_code)]
263    fn init(&mut self, builder: &Builder) -> Result<(), ()> {
264        let sinks = builder.build_sinks()?;
265        assert!(self.inner.is_none());
266        if builder.dynamic {
267            self.inner.replace(LoggerInner::Dyn(ArcSwap::new(Arc::new(sinks))));
268        } else {
269            self.inner.replace(LoggerInner::Once(sinks));
270        }
271        self.config_checksum.store(builder.cal_checksum(), Ordering::Release);
272        Ok(())
273    }
274}
275
276impl log::Log for GlobalLogger {
277    #[inline(always)]
278    fn enabled(&self, _m: &log::Metadata) -> bool {
279        true
280    }
281
282    #[inline(always)]
283    fn log(&self, r: &log::Record) {
284        let now = Timer::new();
285        if let Some(inner) = self.inner.as_ref() {
286            match &inner {
287                LoggerInner::Once(inner) => {
288                    for sink in inner.iter() {
289                        sink.log(&now, r);
290                    }
291                }
292                LoggerInner::Dyn(inner) => {
293                    let sinks = inner.load();
294                    for sink in sinks.iter() {
295                        sink.log(&now, r);
296                    }
297                }
298            }
299        }
300    }
301
302    /// Can be call manually on program shutdown (If you have a buffered log sink)
303    ///
304    /// # Example
305    ///
306    /// ``` rust
307    /// log::logger().flush();
308    /// ```
309    fn flush(&self) {
310        if let Some(inner) = self.inner.as_ref() {
311            match &inner {
312                LoggerInner::Once(inner) => {
313                    for sink in inner.iter() {
314                        sink.flush();
315                    }
316                }
317                LoggerInner::Dyn(inner) => {
318                    let sinks = inner.load();
319                    for sink in sinks.iter() {
320                        sink.flush();
321                    }
322                }
323            }
324        }
325    }
326}
327
328static GLOBAL_LOGGER: GlobalLoggerConainer = GlobalLoggerConainer::new();
329
330/// log handle for panic hook
331#[doc(hidden)]
332pub fn log_panic(info: &std::panic::PanicHookInfo) {
333    let bt = Backtrace::new();
334    let mut record = log::Record::builder();
335    record.level(log::Level::Error);
336    if let Some(loc) = info.location() {
337        record.file(Some(loc.file())).line(Some(loc.line()));
338    }
339    log::logger().log(&record.args(format_args!("panic occur: {}\ntrace: {:?}", info, bt)).build());
340    eprint!("panic occur: {} at {:?}\ntrace: {:?}", info, info.location(), bt);
341}
342
343#[inline(always)]
344fn panic_and_exit_hook(info: &std::panic::PanicHookInfo) {
345    log_panic(info);
346    log::logger().flush();
347    let msg = format!("{}", info).to_string();
348    std::panic::resume_unwind(Box::new(msg));
349}
350
351#[inline(always)]
352fn panic_no_exit_hook(info: &std::panic::PanicHookInfo) {
353    log_panic(info);
354    eprint!("not debug version, so don't exit process");
355    log::logger().flush();
356}