captains_log/
log_impl.rs

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