captains_log/
log_impl.rs

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