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