captains_log/
log_impl.rs

1use std::thread;
2use backtrace::Backtrace;
3use lazy_static::lazy_static;
4use log::*;
5use parking_lot::Mutex;
6use signal_hook::iterator::Signals;
7use crate::{
8    config::Builder,
9    file_impl::LoggerSinkFile,
10    time::Timer,
11};
12
13/*
14 * This is logger that support multi-thread appending write without lock.
15 * Because Log trait prevent internal mutability, File has Sync & Send but need mut to write, and
16 * RefCell has !Send.  So the only way to achieve lock-free is to use unsafe libc call.
17*/
18
19#[enum_dispatch]
20pub(crate) trait LoggerSinkTrait {
21
22    fn reopen(&self) -> std::io::Result<()>;
23
24    fn log(&self, now: &Timer, r: &Record);
25}
26
27#[enum_dispatch(LoggerSinkTrait)]
28pub enum LoggerSink{
29    File(LoggerSinkFile),
30}
31
32pub struct GlobalLogger {
33    sinks: Option<Vec<LoggerSink>>, // Global static needs initialization when declaring, so we give it a wrapper struct with empty internal
34}
35
36
37impl GlobalLogger {
38    pub fn reopen(&mut self) -> std::io::Result<()> {
39        if let Some(sinks) = self.sinks.as_ref() {
40            for sink in sinks {
41                sink.reopen()?;
42            }
43        }
44        Ok(())
45    }
46
47    fn init(&mut self, builder: &Builder) -> std::io::Result<bool> {
48        if !builder.force || self.sinks.is_some() {
49            return Ok(false);
50        }
51        let mut sinks = Vec::new();
52        for config in &builder.sinks {
53            let logger_sink = config.build();
54            logger_sink.reopen()?;
55            sinks.push(logger_sink);
56        }
57        self.sinks.replace(sinks);
58
59        let _ = unsafe { set_logger(std::mem::transmute::<&Self, &'static Self>(self)) };
60        Ok(true)
61    }
62}
63
64impl Log for GlobalLogger {
65    fn enabled(&self, _m: &Metadata) -> bool {
66        true
67    }
68
69    fn log(&self, r: &Record) {
70        let now = Timer::new();
71        if let Some(sinks) = self.sinks.as_ref() {
72            for sink in sinks {
73                sink.log(&now, r);
74            }
75        }
76    }
77
78    fn flush(&self) {}
79}
80
81lazy_static! {
82    static ref GLOBAL_LOGGER: Mutex<GlobalLogger> = Mutex::new(GlobalLogger { sinks: None });
83}
84
85/// log handle for panic hook
86#[doc(hidden)]
87pub fn log_panic(info: &std::panic::PanicHookInfo) {
88    let bt = Backtrace::new();
89    let mut record = log::Record::builder();
90    record.level(log::Level::Error);
91    if let Some(loc) = info.location() {
92        record.file(Some(loc.file())).line(Some(loc.line()));
93    }
94    log::logger().log(
95        &record
96            .args(format_args!("panic occur: {}\ntrace: {:?}", info, bt))
97            .build(),
98    );
99    eprint!(
100        "panic occur: {} at {:?}\ntrace: {:?}",
101        info,
102        info.location(),
103        bt
104    );
105}
106
107fn panic_and_exit_hook(info: &std::panic::PanicHookInfo) {
108    log_panic(info);
109    std::process::exit(exitcode::IOERR);
110}
111
112fn panic_no_exit_hook(info: &std::panic::PanicHookInfo) {
113    log_panic(info);
114    eprint!("not debug version, so don't exit process");
115}
116
117/// Initialize global logger from Builder
118pub fn setup_log(builder: Builder) {
119    {
120        let mut global_logger = GLOBAL_LOGGER.lock();
121
122        match global_logger.init(&builder) {
123            Err(e) => {
124                println!("Initialize logger failed: {:?}", e);
125                return;
126            }
127            Ok(false) => return,
128            Ok(true) => {}
129        }
130        set_max_level(builder.get_max_level());
131        if builder.continue_when_panic {
132            std::panic::set_hook(Box::new(panic_no_exit_hook));
133        } else {
134            std::panic::set_hook(Box::new(panic_and_exit_hook));
135        }
136    }
137    if builder.rotation_signals.len() > 0 {
138        let signals = builder.rotation_signals.clone();
139        thread::spawn(move || {
140            let mut signals = Signals::new(&signals).unwrap();
141            for __sig in signals.forever() {
142                let mut global_logger = GLOBAL_LOGGER.lock();
143                let _ = global_logger.reopen();
144            }
145        });
146    }
147}