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 lazy_static::lazy_static;
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 LogSinkTrait {
17    fn open(&self) -> std::io::Result<()>;
18
19    fn reopen(&self) -> std::io::Result<()>;
20
21    fn log(&self, now: &Timer, r: &log::Record);
22
23    fn flush(&self);
24}
25
26#[enum_dispatch(LogSinkTrait)]
27pub enum LogSink {
28    File(LogSinkFile),
29    BufFile(LogSinkBufFile),
30    Console(LogSinkConsole),
31    #[cfg(feature = "syslog")]
32    Syslog(crate::syslog::LogSinkSyslog),
33    #[cfg(feature = "ringfile")]
34    RingFile(crate::ring::LogSinkRingFile),
35}
36
37/// Global static structure to hold the logger
38struct GlobalLogger {
39    /// checksum for config comparison
40    config_checksum: u64,
41    /// Global static needs initialization when declaring,
42    /// default to be empty
43    inner: Option<LoggerInner>,
44    signal_listener: AtomicBool,
45}
46
47enum LoggerInner {
48    Once(Vec<LogSink>),
49    // using ArcSwap has more cost
50    Dyn(ArcSwap<Vec<LogSink>>),
51}
52
53#[inline(always)]
54fn panic_or_error() {
55    #[cfg(debug_assertions)]
56    {
57        panic!("GlobalLogger cannot be initialized twice on dynamic==false");
58    }
59    #[cfg(not(debug_assertions))]
60    {
61        eprintln!("GlobalLogger cannot be initialized twice on dynamic==false");
62    }
63}
64
65impl LoggerInner {
66    #[allow(dead_code)]
67    fn set(&self, sinks: Vec<LogSink>) {
68        match &self {
69            Self::Once(_) => {
70                panic_or_error();
71            }
72            Self::Dyn(d) => {
73                d.store(Arc::new(sinks));
74            }
75        }
76    }
77}
78
79impl GlobalLogger {
80    fn open(&mut self) -> std::io::Result<()> {
81        if let Some(inner) = self.inner.as_ref() {
82            match &inner {
83                LoggerInner::Once(inner) => {
84                    for sink in inner.iter() {
85                        sink.open()?;
86                    }
87                }
88                LoggerInner::Dyn(inner) => {
89                    let sinks = inner.load();
90                    for sink in sinks.iter() {
91                        sink.open()?;
92                    }
93                }
94            }
95        }
96        println!("log sinks opened");
97        Ok(())
98    }
99
100    pub fn reopen(&mut self) -> std::io::Result<()> {
101        if let Some(inner) = self.inner.as_ref() {
102            match &inner {
103                LoggerInner::Once(inner) => {
104                    for sink in inner.iter() {
105                        sink.reopen()?;
106                    }
107                }
108                LoggerInner::Dyn(inner) => {
109                    let sinks = inner.load();
110                    for sink in sinks.iter() {
111                        sink.reopen()?;
112                    }
113                }
114            }
115        }
116        println!("log sinks re-opened");
117        Ok(())
118    }
119
120    #[allow(dead_code)]
121    fn init(&mut self, builder: &Builder) -> std::io::Result<bool> {
122        let new_checksum = builder.cal_checksum();
123        if self.inner.is_some() {
124            if self.config_checksum == new_checksum {
125                // Config is the same, no need to reinit
126                self.open()?;
127                return Ok(true);
128            }
129            if !builder.dynamic {
130                panic_or_error();
131                return Ok(false);
132            }
133        }
134        let mut sinks = Vec::new();
135        for config in &builder.sinks {
136            let logger_sink = config.build();
137            logger_sink.open()?;
138            sinks.push(logger_sink);
139        }
140
141        if let Some(inner) = self.inner.as_ref() {
142            inner.set(sinks);
143        } else {
144            if builder.dynamic {
145                self.inner.replace(LoggerInner::Dyn(ArcSwap::new(Arc::new(sinks))));
146            } else {
147                self.inner.replace(LoggerInner::Once(sinks));
148            }
149        }
150        self.config_checksum = new_checksum;
151
152        let _ = unsafe { log::set_logger(transmute::<&Self, &'static Self>(self)) };
153
154        // panic hook can be set multiple times
155        if builder.continue_when_panic {
156            std::panic::set_hook(Box::new(panic_no_exit_hook));
157        } else {
158            std::panic::set_hook(Box::new(panic_and_exit_hook));
159        }
160        Ok(true)
161    }
162}
163
164impl log::Log for GlobalLogger {
165    #[inline(always)]
166    fn enabled(&self, _m: &log::Metadata) -> bool {
167        true
168    }
169
170    #[inline(always)]
171    fn log(&self, r: &log::Record) {
172        let now = Timer::new();
173        if let Some(inner) = self.inner.as_ref() {
174            match &inner {
175                LoggerInner::Once(inner) => {
176                    for sink in inner.iter() {
177                        sink.log(&now, r);
178                    }
179                }
180                LoggerInner::Dyn(inner) => {
181                    let sinks = inner.load();
182                    for sink in sinks.iter() {
183                        sink.log(&now, r);
184                    }
185                }
186            }
187        }
188    }
189
190    /// Can be call manually on program shutdown (If you have a buffered log sink)
191    ///
192    /// # Example
193    ///
194    /// ``` rust
195    /// log::logger().flush();
196    /// ```
197    fn flush(&self) {
198        if let Some(inner) = self.inner.as_ref() {
199            match &inner {
200                LoggerInner::Once(inner) => {
201                    for sink in inner.iter() {
202                        sink.flush();
203                    }
204                }
205                LoggerInner::Dyn(inner) => {
206                    let sinks = inner.load();
207                    for sink in sinks.iter() {
208                        sink.flush();
209                    }
210                }
211            }
212        }
213    }
214}
215
216lazy_static! {
217    // Mutex only access on init and reopen, bypassed while logging,
218    // because crate log only use const raw pointer to access GlobalLogger.
219    static ref GLOBAL_LOGGER: Mutex<GlobalLogger> = Mutex::new(GlobalLogger {
220        config_checksum: 0,
221        inner: None ,
222        signal_listener: AtomicBool::new(false),
223    });
224}
225
226/// log handle for panic hook
227#[doc(hidden)]
228pub fn log_panic(info: &std::panic::PanicHookInfo) {
229    let bt = Backtrace::new();
230    let mut record = log::Record::builder();
231    record.level(log::Level::Error);
232    if let Some(loc) = info.location() {
233        record.file(Some(loc.file())).line(Some(loc.line()));
234    }
235    log::logger().log(&record.args(format_args!("panic occur: {}\ntrace: {:?}", info, bt)).build());
236    eprint!("panic occur: {} at {:?}\ntrace: {:?}", info, info.location(), bt);
237}
238
239#[inline(always)]
240fn panic_and_exit_hook(info: &std::panic::PanicHookInfo) {
241    log_panic(info);
242    log::logger().flush();
243    let msg = format!("{}", info).to_string();
244    std::panic::resume_unwind(Box::new(msg));
245}
246
247#[inline(always)]
248fn panic_no_exit_hook(info: &std::panic::PanicHookInfo) {
249    log_panic(info);
250    eprint!("not debug version, so don't exit process");
251    log::logger().flush();
252}
253
254fn signal_listener(signals: Vec<i32>) {
255    let started;
256    {
257        let global_logger = GLOBAL_LOGGER.lock();
258        started = global_logger.signal_listener.swap(true, Ordering::SeqCst);
259    }
260    if started {
261        // NOTE: Once logger started to listen signal, does not support dynamic reconfigure.
262        eprintln!("signal listener already started");
263        return;
264    }
265    thread::spawn(move || {
266        let mut signals = Signals::new(&signals).unwrap();
267        for __sig in signals.forever() {
268            {
269                let mut global_logger = GLOBAL_LOGGER.lock();
270                let _ = global_logger.reopen();
271            }
272        }
273    });
274}
275
276/// Initialize global logger from Builder
277pub fn setup_log(builder: Builder) -> Result<(), ()> {
278    {
279        let mut global_logger = GLOBAL_LOGGER.lock();
280
281        match global_logger.init(&builder) {
282            Err(e) => {
283                println!("Initialize logger failed: {:?}", e);
284                return Err(());
285            }
286            Ok(false) => return Err(()),
287            Ok(true) => {}
288        }
289        log::set_max_level(builder.get_max_level());
290    }
291    let signals = builder.rotation_signals.clone();
292    if signals.len() > 0 {
293        signal_listener(signals);
294    }
295    Ok(())
296}