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 signal_hook::iterator::Signals;
6use std::cell::UnsafeCell;
7use std::io::Error;
8use std::mem::transmute;
9use std::sync::{
10 atomic::{AtomicBool, AtomicU64, Ordering},
11 Arc,
12};
13use std::thread;
14
15#[cfg(feature = "tracing")]
16use crate::tracing_bridge::{CaptainsLogLayer, TracingFormatter, TracingText};
17#[cfg(feature = "tracing")]
18use tracing::{dispatcher, Dispatch};
19#[cfg(feature = "tracing")]
20use tracing_subscriber::prelude::*;
21
22#[enum_dispatch]
23pub(crate) trait LogSinkTrait {
24 fn open(&self) -> std::io::Result<()>;
25
26 fn reopen(&self) -> std::io::Result<()>;
27
28 fn log(&self, now: &Timer, r: &log::Record);
29
30 fn flush(&self);
31}
32
33#[enum_dispatch(LogSinkTrait)]
34pub enum LogSink {
35 File(LogSinkFile),
36 BufFile(LogSinkBufFile),
37 Console(LogSinkConsole),
38 #[cfg(feature = "syslog")]
39 Syslog(crate::syslog::LogSinkSyslog),
40 #[cfg(feature = "ringfile")]
41 RingFile(crate::ringfile::LogSinkRingFile),
42}
43
44struct GlobalLoggerStatic {
45 logger: UnsafeCell<GlobalLogger>,
46 lock: AtomicBool,
47 inited: AtomicBool,
48}
49
50struct GlobalLoggerGuard<'a>(&'a GlobalLoggerStatic);
51
52impl Drop for GlobalLoggerGuard<'_> {
53 fn drop(&mut self) {
54 self.0.unlock();
55 }
56}
57
58impl GlobalLoggerStatic {
59 const fn new() -> Self {
60 Self {
61 logger: UnsafeCell::new(GlobalLogger {
62 config_checksum: AtomicU64::new(0),
63 inner: None,
64 signal_listener: AtomicBool::new(false),
65 #[cfg(feature = "tracing")]
66 tracing_inited: AtomicBool::new(false),
67 }),
68 lock: AtomicBool::new(false),
69 inited: AtomicBool::new(false),
70 }
71 }
72
73 #[inline(always)]
74 fn get_logger_mut(&self) -> &mut GlobalLogger {
75 unsafe { transmute(self.logger.get()) }
76 }
77
78 #[inline(always)]
80 fn get_logger(&'static self) -> &'static GlobalLogger {
81 unsafe { transmute(self.logger.get()) }
82 }
83
84 #[inline(always)]
86 fn try_get_logger(&'static self) -> Option<&'static GlobalLogger> {
87 let logger = self.get_logger();
88 if self.inited.load(Ordering::SeqCst) {
89 return Some(logger);
90 } else {
91 None
92 }
93 }
94
95 #[inline]
96 fn lock<'a>(&'a self) -> GlobalLoggerGuard<'a> {
97 while self
98 .lock
99 .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
100 .is_err()
101 {
102 std::thread::yield_now();
104 }
105 GlobalLoggerGuard(self)
106 }
107
108 fn unlock(&self) {
109 self.lock.store(false, Ordering::SeqCst);
110 }
111
112 fn try_setup(&'static self, builder: &Builder) -> Result<bool, Error> {
114 let _guard = self.lock();
115 let res = { self.get_logger().check_the_same(builder) };
116 match res {
117 Some(true) => {
118 if let Err(e) = self.get_logger().open() {
120 eprintln!("failed to open log sink: {:?}", e);
121 return Err(e);
122 }
123 log::set_max_level(builder.get_max_level());
125 return Ok(false);
126 }
127 Some(false) => {
128 if !builder.dynamic {
130 let e = Error::other("log config differs but dynamic=false");
131 eprintln!("{:?}", e);
132 return Err(e);
133 }
134 let logger = self.get_logger();
135 if let Err(e) = logger.reinit(builder) {
136 eprintln!("{:?}", e);
137 return Err(e);
138 }
139 log::set_max_level(builder.get_max_level());
141 #[cfg(feature = "tracing")]
142 {
143 if builder.tracing_global {
144 logger.init_tracing_global()?;
145 }
146 }
147 return Ok(false);
148 }
149 None => {
150 let res = { self.get_logger_mut().init(builder) };
152 res?;
153 #[cfg(feature = "tracing")]
154 {
155 let logger = self.get_logger();
156 if builder.tracing_global {
157 logger.init_tracing_global()?;
158 }
159 }
160 self.inited.store(true, Ordering::SeqCst);
161 return Ok(true);
162 }
163 }
164 }
165}
166
167unsafe impl Send for GlobalLoggerStatic {}
168unsafe impl Sync for GlobalLoggerStatic {}
169
170pub fn setup_log(builder: Builder) -> Result<&'static GlobalLogger, Error> {
176 if GLOBAL_LOGGER.try_setup(&builder)? {
177 let logger = GLOBAL_LOGGER.get_logger();
178 if let Err(e) = log::set_logger(logger) {
180 eprintln!("log::set_logger return error: {:?}", e);
181 return Err(Error::other(format!("log::set_logger() failed: {:?}", e)));
182 }
183 log::set_max_level(builder.get_max_level());
184 if builder.continue_when_panic {
186 std::panic::set_hook(Box::new(panic_no_exit_hook));
187 } else {
188 std::panic::set_hook(Box::new(panic_and_exit_hook));
189 }
190 let signals = builder.rotation_signals.clone();
191 if signals.len() > 0 {
192 if false == logger.signal_listener.swap(true, Ordering::SeqCst) {
193 thread::spawn(move || {
194 GLOBAL_LOGGER.get_logger().listener_for_signal(signals);
195 });
196 }
197 }
198 Ok(logger)
199 } else {
200 Ok(GLOBAL_LOGGER.get_logger())
201 }
202}
203
204pub fn get_global_logger() -> Option<&'static GlobalLogger> {
206 GLOBAL_LOGGER.try_get_logger()
207}
208
209pub struct GlobalLogger {
211 config_checksum: AtomicU64,
213 inner: Option<LoggerInner>,
216 signal_listener: AtomicBool,
217 #[cfg(feature = "tracing")]
218 tracing_inited: AtomicBool,
219}
220
221enum LoggerInnerSink {
222 Once(Vec<LogSink>),
223 Dyn(ArcSwap<Vec<LogSink>>),
225}
226
227struct LoggerInner {
228 sinks: LoggerInnerSink,
229}
230
231unsafe impl Send for LoggerInner {}
232unsafe impl Sync for LoggerInner {}
233
234impl LoggerInner {
235 #[inline]
236 fn new(dynamic: bool, sinks: Vec<LogSink>) -> Self {
237 let sinks = if dynamic {
238 LoggerInnerSink::Dyn(ArcSwap::new(Arc::new(sinks)))
239 } else {
240 LoggerInnerSink::Once(sinks)
241 };
242 Self { sinks }
243 }
244
245 #[inline]
246 fn set(&self, sinks: Vec<LogSink>) -> std::io::Result<()> {
247 match &self.sinks {
248 LoggerInnerSink::Once(_) => {
249 let e = Error::other("previous logger does not init with dynamic=true");
250 return Err(e);
251 }
252 LoggerInnerSink::Dyn(d) => {
253 d.store(Arc::new(sinks));
254 }
255 }
256 Ok(())
257 }
258}
259
260impl GlobalLogger {
261 fn listener_for_signal(&self, signals: Vec<i32>) {
262 println!("signal_listener started");
263 let mut signals = Signals::new(&signals).unwrap();
264 for __sig in signals.forever() {
265 let _ = self.reopen();
266 }
267 println!("signal_listener exit");
268 }
269
270 fn open(&self) -> std::io::Result<()> {
272 if let Some(inner) = self.inner.as_ref() {
273 match &inner.sinks {
274 LoggerInnerSink::Once(inner) => {
275 for sink in inner.iter() {
276 sink.open()?;
277 }
278 }
279 LoggerInnerSink::Dyn(inner) => {
280 let sinks = inner.load();
281 for sink in sinks.iter() {
282 sink.open()?;
283 }
284 }
285 }
286 }
287 println!("log sinks opened");
288 Ok(())
289 }
290
291 pub fn reopen(&self) -> std::io::Result<()> {
293 if let Some(inner) = self.inner.as_ref() {
294 match &inner.sinks {
295 LoggerInnerSink::Once(inner) => {
296 for sink in inner.iter() {
297 sink.reopen()?;
298 }
299 }
300 LoggerInnerSink::Dyn(inner) => {
301 let sinks = inner.load();
302 for sink in sinks.iter() {
303 sink.reopen()?;
304 }
305 }
306 }
307 }
308 println!("log sinks re-opened");
309 Ok(())
310 }
311
312 #[inline]
314 fn check_the_same(&self, builder: &Builder) -> Option<bool> {
315 if self.inner.is_some() {
316 return Some(self.config_checksum.load(Ordering::Acquire) == builder.cal_checksum());
317 }
318 None
319 }
320
321 fn reinit(&self, builder: &Builder) -> std::io::Result<()> {
323 let sinks = builder.build_sinks()?;
324 if let Some(inner) = self.inner.as_ref() {
325 inner.set(sinks)?;
326 self.config_checksum.store(builder.cal_checksum(), Ordering::Release);
327 } else {
328 unreachable!();
329 }
330 Ok(())
331 }
332
333 fn init(&mut self, builder: &Builder) -> std::io::Result<()> {
334 let sinks = builder.build_sinks()?;
335 assert!(self.inner.is_none());
336 self.inner.replace(LoggerInner::new(builder.dynamic, sinks));
337 self.config_checksum.store(builder.cal_checksum(), Ordering::Release);
338 Ok(())
339 }
340
341 #[cfg(feature = "tracing")]
342 #[inline]
343 fn init_tracing_global(&'static self) -> Result<(), Error> {
344 let dist = self.tracing_dispatch::<TracingText>()?;
345 if let Err(_) = dispatcher::set_global_default(dist) {
346 let e = Error::other("tracing global dispatcher already exists");
347 eprintln!("{:?}", e);
348 return Err(e);
349 }
350 self.tracing_inited.store(true, Ordering::SeqCst);
351 Ok(())
352 }
353
354 #[cfg(feature = "tracing")]
355 #[cfg_attr(docsrs, doc(cfg(feature = "tracing")))]
356 pub fn tracing_layer<F: TracingFormatter>(
365 &'static self,
366 ) -> std::io::Result<CaptainsLogLayer<F>> {
367 if self.tracing_inited.load(Ordering::SeqCst) {
368 let e = Error::other("global tracing dispatcher exists");
369 eprintln!("{:?}", e);
370 return Err(e);
371 }
372 return Ok(CaptainsLogLayer::<F>::new(self));
373 }
374
375 #[cfg(feature = "tracing")]
376 #[cfg_attr(docsrs, doc(cfg(feature = "tracing")))]
377 pub fn tracing_dispatch<F: TracingFormatter>(&'static self) -> std::io::Result<Dispatch> {
386 if self.tracing_inited.load(Ordering::SeqCst) {
387 let e = Error::other("global tracing dispatcher exists");
388 eprintln!("{:?}", e);
389 return Err(e);
390 }
391 return Ok(Dispatch::new(
392 tracing_subscriber::registry().with(self.tracing_layer::<F>().unwrap()),
393 ));
394 }
395}
396
397impl log::Log for GlobalLogger {
398 #[inline(always)]
399 fn enabled(&self, _m: &log::Metadata) -> bool {
400 true
401 }
402
403 #[inline(always)]
404 fn log(&self, r: &log::Record) {
405 let now = Timer::new();
406 if let Some(inner) = self.inner.as_ref() {
407 match &inner.sinks {
408 LoggerInnerSink::Once(inner) => {
409 for sink in inner.iter() {
410 sink.log(&now, r);
411 }
412 }
413 LoggerInnerSink::Dyn(inner) => {
414 let sinks = inner.load();
415 for sink in sinks.iter() {
416 sink.log(&now, r);
417 }
418 }
419 }
420 }
421 }
422
423 fn flush(&self) {
431 if let Some(inner) = self.inner.as_ref() {
432 match &inner.sinks {
433 LoggerInnerSink::Once(inner) => {
434 for sink in inner.iter() {
435 sink.flush();
436 }
437 }
438 LoggerInnerSink::Dyn(inner) => {
439 let sinks = inner.load();
440 for sink in sinks.iter() {
441 sink.flush();
442 }
443 }
444 }
445 }
446 }
447}
448
449static GLOBAL_LOGGER: GlobalLoggerStatic = GlobalLoggerStatic::new();
450
451#[doc(hidden)]
453pub fn log_panic(info: &std::panic::PanicHookInfo) {
454 let bt = Backtrace::new();
455 let mut record = log::Record::builder();
456 record.level(log::Level::Error);
457 if let Some(loc) = info.location() {
458 record.file(Some(loc.file())).line(Some(loc.line()));
459 }
460 log::logger().log(&record.args(format_args!("panic occur: {}\ntrace: {:?}", info, bt)).build());
461 eprint!("panic occur: {} at {:?}\ntrace: {:?}", info, info.location(), bt);
462}
463
464#[inline(always)]
465fn panic_and_exit_hook(info: &std::panic::PanicHookInfo) {
466 log_panic(info);
467 log::logger().flush();
468 let msg = format!("{}", info).to_string();
469 std::panic::resume_unwind(Box::new(msg));
470}
471
472#[inline(always)]
473fn panic_no_exit_hook(info: &std::panic::PanicHookInfo) {
474 log_panic(info);
475 eprint!("not debug version, so don't exit process");
476 log::logger().flush();
477}