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 Arc,
11 atomic::{AtomicBool, Ordering},
12};
13use std::thread;
14
15#[enum_dispatch]
16pub(crate) trait LogSinkTrait {
17 fn reopen(&self) -> std::io::Result<()>;
18
19 fn log(&self, now: &Timer, r: &log::Record);
20
21 fn flush(&self);
22}
23
24#[enum_dispatch(LogSinkTrait)]
25pub(crate) enum LogSink {
26 File(LogSinkFile),
27 BufFile(LogSinkBufFile),
28 Console(LogSinkConsole),
29}
30
31struct GlobalLogger {
33 config_checksum: u64,
35 inner: Option<LoggerInner>,
38 signal_listener: AtomicBool,
39}
40
41enum LoggerInner {
42 Once(Vec<LogSink>),
43 Dyn(ArcSwap<Vec<LogSink>>),
45}
46
47#[inline(always)]
48fn panic_or_error() {
49 #[cfg(debug_assertions)]
50 {
51 panic!("GlobalLogger cannot be initialized twice on dynamic==false");
52 }
53 #[cfg(not(debug_assertions))]
54 {
55 eprintln!("GlobalLogger cannot be initialized twice on dynamic==false");
56 }
57}
58
59impl LoggerInner {
60 #[allow(dead_code)]
61 fn set(&self, sinks: Vec<LogSink>) {
62 match &self {
63 Self::Once(_) => {
64 panic_or_error();
65 }
66 Self::Dyn(d) => {
67 d.store(Arc::new(sinks));
68 }
69 }
70 }
71}
72
73impl GlobalLogger {
74 pub fn reopen(&mut self) -> std::io::Result<()> {
75 if let Some(inner) = self.inner.as_ref() {
76 match &inner {
77 LoggerInner::Once(inner) => {
78 for sink in inner.iter() {
79 sink.reopen()?;
80 }
81 }
82 LoggerInner::Dyn(inner) => {
83 let sinks = inner.load();
84 for sink in sinks.iter() {
85 sink.reopen()?;
86 }
87 }
88 }
89 }
90 println!("log sinks re-opened");
91 Ok(())
92 }
93
94 #[allow(dead_code)]
95 fn init(&mut self, builder: &Builder) -> std::io::Result<bool> {
96 let new_checksum = builder.cal_checksum();
97 if self.inner.is_some() {
98 if self.config_checksum == new_checksum {
99 self.reopen()?;
101 return Ok(true);
102 }
103 if !builder.dynamic {
104 panic_or_error();
105 return Ok(false);
106 }
107 }
108 let mut sinks = Vec::new();
109 for config in &builder.sinks {
110 let logger_sink = config.build();
111 logger_sink.reopen()?;
112 sinks.push(logger_sink);
113 }
114
115 if let Some(inner) = self.inner.as_ref() {
116 inner.set(sinks);
117 } else {
118 if builder.dynamic {
119 self.inner.replace(LoggerInner::Dyn(ArcSwap::new(Arc::new(sinks))));
120 } else {
121 self.inner.replace(LoggerInner::Once(sinks));
122 }
123 }
124 self.config_checksum = new_checksum;
125
126 let _ = unsafe { log::set_logger(transmute::<&Self, &'static Self>(self)) };
127
128 if builder.continue_when_panic {
130 std::panic::set_hook(Box::new(panic_no_exit_hook));
131 } else {
132 std::panic::set_hook(Box::new(panic_and_exit_hook));
133 }
134 Ok(true)
135 }
136}
137
138impl log::Log for GlobalLogger {
139 #[inline(always)]
140 fn enabled(&self, _m: &log::Metadata) -> bool {
141 true
142 }
143
144 #[inline(always)]
145 fn log(&self, r: &log::Record) {
146 let now = Timer::new();
147 if let Some(inner) = self.inner.as_ref() {
148 match &inner {
149 LoggerInner::Once(inner) => {
150 for sink in inner.iter() {
151 sink.log(&now, r);
152 }
153 }
154 LoggerInner::Dyn(inner) => {
155 let sinks = inner.load();
156 for sink in sinks.iter() {
157 sink.log(&now, r);
158 }
159 }
160 }
161 }
162 }
163
164 fn flush(&self) {
172 if let Some(inner) = self.inner.as_ref() {
173 match &inner {
174 LoggerInner::Once(inner) => {
175 for sink in inner.iter() {
176 sink.flush();
177 }
178 }
179 LoggerInner::Dyn(inner) => {
180 let sinks = inner.load();
181 for sink in sinks.iter() {
182 sink.flush();
183 }
184 }
185 }
186 }
187 }
188}
189
190lazy_static! {
191 static ref GLOBAL_LOGGER: Mutex<GlobalLogger> = Mutex::new(GlobalLogger {
194 config_checksum: 0,
195 inner: None ,
196 signal_listener: AtomicBool::new(false),
197 });
198}
199
200#[doc(hidden)]
202pub fn log_panic(info: &std::panic::PanicHookInfo) {
203 let bt = Backtrace::new();
204 let mut record = log::Record::builder();
205 record.level(log::Level::Error);
206 if let Some(loc) = info.location() {
207 record.file(Some(loc.file())).line(Some(loc.line()));
208 }
209 log::logger().log(&record.args(format_args!("panic occur: {}\ntrace: {:?}", info, bt)).build());
210 eprint!("panic occur: {} at {:?}\ntrace: {:?}", info, info.location(), bt);
211}
212
213#[inline(always)]
214fn panic_and_exit_hook(info: &std::panic::PanicHookInfo) {
215 log_panic(info);
216 log::logger().flush();
217 let msg = format!("{}", info).to_string();
218 std::panic::resume_unwind(Box::new(msg));
219}
220
221#[inline(always)]
222fn panic_no_exit_hook(info: &std::panic::PanicHookInfo) {
223 log_panic(info);
224 eprint!("not debug version, so don't exit process");
225 log::logger().flush();
226}
227
228fn signal_listener(signals: Vec<i32>) {
229 let started;
230 {
231 let global_logger = GLOBAL_LOGGER.lock();
232 started = global_logger.signal_listener.swap(true, Ordering::SeqCst);
233 }
234 if started {
235 eprintln!("signal listener already started");
237 return;
238 }
239 thread::spawn(move || {
240 let mut signals = Signals::new(&signals).unwrap();
241 for __sig in signals.forever() {
242 {
243 let mut global_logger = GLOBAL_LOGGER.lock();
244 let _ = global_logger.reopen();
245 }
246 }
247 });
248}
249
250pub fn setup_log(builder: Builder) -> Result<(), ()> {
252 {
253 let mut global_logger = GLOBAL_LOGGER.lock();
254
255 match global_logger.init(&builder) {
256 Err(e) => {
257 println!("Initialize logger failed: {:?}", e);
258 return Err(());
259 }
260 Ok(false) => return Err(()),
261 Ok(true) => {}
262 }
263 log::set_max_level(builder.get_max_level());
264 }
265 let signals = builder.rotation_signals.clone();
266 if signals.len() > 0 {
267 signal_listener(signals);
268 }
269 Ok(())
270}