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#[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>>, }
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#[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
117pub 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}