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