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
30struct GlobalLogger {
32 inner: Option<LoggerInner>,
35 signal_listener: AtomicBool,
36}
37
38enum LoggerInner {
39 Once(Vec<LoggerSink>),
40 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 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 static ref GLOBAL_LOGGER: Mutex<GlobalLogger> = Mutex::new(GlobalLogger {
159 inner: None ,
160 signal_listener: AtomicBool::new(false),
161 });
162}
163
164#[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 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
212pub 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}