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 Ok(())
88 }
89
90 #[allow(dead_code)]
91 fn init(&mut self, builder: &Builder) -> std::io::Result<bool> {
92 if !builder.dynamic && self.inner.is_some() {
93 panic_or_error();
94 return Ok(false);
95 }
96 let mut sinks = Vec::new();
97 for config in &builder.sinks {
98 let logger_sink = config.build();
99 logger_sink.reopen()?;
100 sinks.push(logger_sink);
101 }
102
103 if let Some(inner) = self.inner.as_ref() {
104 inner.set(sinks);
105 } else {
106 if builder.dynamic {
107 self.inner.replace(LoggerInner::Dyn(ArcSwap::new(Arc::new(sinks))));
108 } else {
109 self.inner.replace(LoggerInner::Once(sinks));
110 }
111 }
112
113 let _ = unsafe { set_logger(transmute::<&Self, &'static Self>(self)) };
114
115 if builder.continue_when_panic {
117 std::panic::set_hook(Box::new(panic_no_exit_hook));
118 } else {
119 std::panic::set_hook(Box::new(panic_and_exit_hook));
120 }
121 Ok(true)
122 }
123}
124
125impl Log for GlobalLogger {
126 #[inline(always)]
127 fn enabled(&self, _m: &Metadata) -> bool {
128 true
129 }
130
131 #[inline(always)]
132 fn log(&self, r: &Record) {
133 let now = Timer::new();
134 if let Some(inner) = self.inner.as_ref() {
135 match &inner {
136 LoggerInner::Once(inner) => {
137 for sink in inner.iter() {
138 sink.log(&now, r);
139 }
140 }
141 LoggerInner::Dyn(inner) => {
142 let sinks = inner.load();
143 for sink in sinks.iter() {
144 sink.log(&now, r);
145 }
146 }
147 }
148 }
149 }
150
151 fn flush(&self) {}
152}
153
154lazy_static! {
155 static ref GLOBAL_LOGGER: Mutex<GlobalLogger> = Mutex::new(GlobalLogger {
158 inner: None ,
159 signal_listener: AtomicBool::new(false),
160 });
161}
162
163#[doc(hidden)]
165pub fn log_panic(info: &std::panic::PanicHookInfo) {
166 let bt = Backtrace::new();
167 let mut record = log::Record::builder();
168 record.level(log::Level::Error);
169 if let Some(loc) = info.location() {
170 record.file(Some(loc.file())).line(Some(loc.line()));
171 }
172 log::logger().log(&record.args(format_args!("panic occur: {}\ntrace: {:?}", info, bt)).build());
173 eprint!("panic occur: {} at {:?}\ntrace: {:?}", info, info.location(), bt);
174}
175
176#[inline(always)]
177fn panic_and_exit_hook(info: &std::panic::PanicHookInfo) {
178 log_panic(info);
179 std::process::exit(exitcode::IOERR);
180}
181
182#[inline(always)]
183fn panic_no_exit_hook(info: &std::panic::PanicHookInfo) {
184 log_panic(info);
185 eprint!("not debug version, so don't exit process");
186}
187
188fn signal_listener(signals: Vec<i32>) {
189 let started;
190 {
191 let global_logger = GLOBAL_LOGGER.lock();
192 started = global_logger.signal_listener.swap(true, Ordering::SeqCst);
193 }
194 if started {
195 eprintln!("signal listener already started");
197 return;
198 }
199 thread::spawn(move || {
200 let mut signals = Signals::new(&signals).unwrap();
201 for __sig in signals.forever() {
202 {
203 let mut global_logger = GLOBAL_LOGGER.lock();
204 let _ = global_logger.reopen();
205 }
206 }
207 });
208}
209
210pub fn setup_log(builder: Builder) -> Result<(), ()> {
212 {
213 let mut global_logger = GLOBAL_LOGGER.lock();
214
215 match global_logger.init(&builder) {
216 Err(e) => {
217 println!("Initialize logger failed: {:?}", e);
218 return Err(());
219 }
220 Ok(false) => return Err(()),
221 Ok(true) => {}
222 }
223 set_max_level(builder.get_max_level());
224 }
225 let signals = builder.rotation_signals.clone();
226 if signals.len() > 0 {
227 signal_listener(signals);
228 }
229 Ok(())
230}