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        println!("log sinks re-opened");
88        Ok(())
89    }
90
91    #[allow(dead_code)]
92    fn init(&mut self, builder: &Builder) -> std::io::Result<bool> {
93        if !builder.dynamic && self.inner.is_some() {
94            panic_or_error();
95            return Ok(false);
96        }
97        let mut sinks = Vec::new();
98        for config in &builder.sinks {
99            let logger_sink = config.build();
100            logger_sink.reopen()?;
101            sinks.push(logger_sink);
102        }
103
104        if let Some(inner) = self.inner.as_ref() {
105            inner.set(sinks);
106        } else {
107            if builder.dynamic {
108                self.inner.replace(LoggerInner::Dyn(ArcSwap::new(Arc::new(sinks))));
109            } else {
110                self.inner.replace(LoggerInner::Once(sinks));
111            }
112        }
113
114        let _ = unsafe { set_logger(transmute::<&Self, &'static Self>(self)) };
115
116        // panic hook can be set multiple times
117        if builder.continue_when_panic {
118            std::panic::set_hook(Box::new(panic_no_exit_hook));
119        } else {
120            std::panic::set_hook(Box::new(panic_and_exit_hook));
121        }
122        Ok(true)
123    }
124}
125
126impl Log for GlobalLogger {
127    #[inline(always)]
128    fn enabled(&self, _m: &Metadata) -> bool {
129        true
130    }
131
132    #[inline(always)]
133    fn log(&self, r: &Record) {
134        let now = Timer::new();
135        if let Some(inner) = self.inner.as_ref() {
136            match &inner {
137                LoggerInner::Once(inner) => {
138                    for sink in inner.iter() {
139                        sink.log(&now, r);
140                    }
141                }
142                LoggerInner::Dyn(inner) => {
143                    let sinks = inner.load();
144                    for sink in sinks.iter() {
145                        sink.log(&now, r);
146                    }
147                }
148            }
149        }
150    }
151
152    fn flush(&self) {}
153}
154
155lazy_static! {
156    // Mutex only access on init and reopen, bypassed while logging,
157    // because crate log only use const raw pointer to access GlobalLogger.
158    static ref GLOBAL_LOGGER: Mutex<GlobalLogger> = Mutex::new(GlobalLogger {
159        inner: None ,
160        signal_listener: AtomicBool::new(false),
161    });
162}
163
164/// log handle for panic hook
165#[doc(hidden)]
166pub fn log_panic(info: &std::panic::PanicHookInfo) {
167    let bt = Backtrace::new();
168    let mut record = log::Record::builder();
169    record.level(log::Level::Error);
170    if let Some(loc) = info.location() {
171        record.file(Some(loc.file())).line(Some(loc.line()));
172    }
173    log::logger().log(&record.args(format_args!("panic occur: {}\ntrace: {:?}", info, bt)).build());
174    eprint!("panic occur: {} at {:?}\ntrace: {:?}", info, info.location(), bt);
175}
176
177#[inline(always)]
178fn panic_and_exit_hook(info: &std::panic::PanicHookInfo) {
179    log_panic(info);
180    let msg = format!("{}", info).to_string();
181    std::panic::resume_unwind(Box::new(msg));
182}
183
184#[inline(always)]
185fn panic_no_exit_hook(info: &std::panic::PanicHookInfo) {
186    log_panic(info);
187    eprint!("not debug version, so don't exit process");
188}
189
190fn signal_listener(signals: Vec<i32>) {
191    let started;
192    {
193        let global_logger = GLOBAL_LOGGER.lock();
194        started = global_logger.signal_listener.swap(true, Ordering::SeqCst);
195    }
196    if started {
197        // NOTE: Once logger started to listen signal, does not support dynamic reconfigure.
198        eprintln!("signal listener already started");
199        return;
200    }
201    thread::spawn(move || {
202        let mut signals = Signals::new(&signals).unwrap();
203        for __sig in signals.forever() {
204            {
205                let mut global_logger = GLOBAL_LOGGER.lock();
206                let _ = global_logger.reopen();
207            }
208        }
209    });
210}
211
212/// Initialize global logger from Builder
213pub fn setup_log(builder: Builder) -> Result<(), ()> {
214    {
215        let mut global_logger = GLOBAL_LOGGER.lock();
216
217        match global_logger.init(&builder) {
218            Err(e) => {
219                println!("Initialize logger failed: {:?}", e);
220                return Err(());
221            }
222            Ok(false) => return Err(()),
223            Ok(true) => {}
224        }
225        set_max_level(builder.get_max_level());
226    }
227    let signals = builder.rotation_signals.clone();
228    if signals.len() > 0 {
229        signal_listener(signals);
230    }
231    Ok(())
232}