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.panic_hook {
185 if !builder.force_abort_on_panic {
189 std::panic::set_hook(Box::new(panic_hook_log));
190 } else {
191 std::panic::set_hook(Box::new(panic_hook_force_exit));
192 }
193 }
194 let signals = builder.rotation_signals.clone();
195 if !signals.is_empty() && !logger.signal_listener.swap(true, Ordering::SeqCst) {
196 thread::spawn(move || {
197 GLOBAL_LOGGER.get_logger().listener_for_signal(signals);
198 });
199 }
200 Ok(logger)
201 } else {
202 Ok(GLOBAL_LOGGER.get_logger())
203 }
204}
205
206pub fn get_global_logger() -> Option<&'static GlobalLogger> {
208 GLOBAL_LOGGER.try_get_logger()
209}
210
211pub struct GlobalLogger {
213 config_checksum: AtomicU64,
215 inner: Option<LoggerInner>,
218 signal_listener: AtomicBool,
219 #[cfg(feature = "tracing")]
220 tracing_inited: AtomicBool,
221}
222
223enum LoggerInnerSink {
224 Once(Vec<LogSink>),
225 Dyn(ArcSwap<Vec<LogSink>>),
227}
228
229struct LoggerInner {
230 sinks: LoggerInnerSink,
231}
232
233unsafe impl Send for LoggerInner {}
234unsafe impl Sync for LoggerInner {}
235
236impl LoggerInner {
237 #[inline]
238 fn new(dynamic: bool, sinks: Vec<LogSink>) -> Self {
239 let sinks = if dynamic {
240 LoggerInnerSink::Dyn(ArcSwap::new(Arc::new(sinks)))
241 } else {
242 LoggerInnerSink::Once(sinks)
243 };
244 Self { sinks }
245 }
246
247 #[inline]
248 fn set(&self, sinks: Vec<LogSink>) -> std::io::Result<()> {
249 match &self.sinks {
250 LoggerInnerSink::Once(_) => {
251 let e = Error::other("previous logger does not init with dynamic=true");
252 return Err(e);
253 }
254 LoggerInnerSink::Dyn(d) => {
255 d.store(Arc::new(sinks));
256 }
257 }
258 Ok(())
259 }
260}
261
262impl GlobalLogger {
263 fn listener_for_signal(&self, signals: Vec<i32>) {
264 println!("signal_listener started");
265 let mut signals = Signals::new(&signals).unwrap();
266 for __sig in signals.forever() {
267 let _ = self.reopen();
268 }
269 println!("signal_listener exit");
270 }
271
272 fn open(&self) -> std::io::Result<()> {
274 if let Some(inner) = self.inner.as_ref() {
275 match &inner.sinks {
276 LoggerInnerSink::Once(inner) => {
277 for sink in inner.iter() {
278 sink.open()?;
279 }
280 }
281 LoggerInnerSink::Dyn(inner) => {
282 let sinks = inner.load();
283 for sink in sinks.iter() {
284 sink.open()?;
285 }
286 }
287 }
288 }
289 println!("log sinks opened");
290 Ok(())
291 }
292
293 pub fn reopen(&self) -> std::io::Result<()> {
295 if let Some(inner) = self.inner.as_ref() {
296 match &inner.sinks {
297 LoggerInnerSink::Once(inner) => {
298 for sink in inner.iter() {
299 sink.reopen()?;
300 }
301 }
302 LoggerInnerSink::Dyn(inner) => {
303 let sinks = inner.load();
304 for sink in sinks.iter() {
305 sink.reopen()?;
306 }
307 }
308 }
309 }
310 println!("log sinks re-opened");
311 Ok(())
312 }
313
314 #[inline]
316 fn check_the_same(&self, builder: &Builder) -> Option<bool> {
317 if self.inner.is_some() {
318 return Some(self.config_checksum.load(Ordering::Acquire) == builder.cal_checksum());
319 }
320 None
321 }
322
323 fn reinit(&self, builder: &Builder) -> std::io::Result<()> {
325 let sinks = builder.build_sinks()?;
326 if let Some(inner) = self.inner.as_ref() {
327 inner.set(sinks)?;
328 self.config_checksum.store(builder.cal_checksum(), Ordering::Release);
329 } else {
330 unreachable!();
331 }
332 Ok(())
333 }
334
335 fn init(&mut self, builder: &Builder) -> std::io::Result<()> {
336 let sinks = builder.build_sinks()?;
337 assert!(self.inner.is_none());
338 self.inner.replace(LoggerInner::new(builder.dynamic, sinks));
339 self.config_checksum.store(builder.cal_checksum(), Ordering::Release);
340 Ok(())
341 }
342
343 #[cfg(feature = "tracing")]
344 #[inline]
345 fn init_tracing_global(&'static self) -> Result<(), Error> {
346 let dist = self.tracing_dispatch::<TracingText>()?;
347 if let Err(_) = dispatcher::set_global_default(dist) {
348 let e = Error::other("tracing global dispatcher already exists");
349 eprintln!("{:?}", e);
350 return Err(e);
351 }
352 self.tracing_inited.store(true, Ordering::SeqCst);
353 Ok(())
354 }
355
356 #[cfg(feature = "tracing")]
357 #[cfg_attr(docsrs, doc(cfg(feature = "tracing")))]
358 pub fn tracing_layer<F: TracingFormatter>(
367 &'static self,
368 ) -> std::io::Result<CaptainsLogLayer<F>> {
369 if self.tracing_inited.load(Ordering::SeqCst) {
370 let e = Error::other("global tracing dispatcher exists");
371 eprintln!("{:?}", e);
372 return Err(e);
373 }
374 return Ok(CaptainsLogLayer::<F>::new(self));
375 }
376
377 #[cfg(feature = "tracing")]
378 #[cfg_attr(docsrs, doc(cfg(feature = "tracing")))]
379 pub fn tracing_dispatch<F: TracingFormatter>(&'static self) -> std::io::Result<Dispatch> {
388 if self.tracing_inited.load(Ordering::SeqCst) {
389 let e = Error::other("global tracing dispatcher exists");
390 eprintln!("{:?}", e);
391 return Err(e);
392 }
393 return Ok(Dispatch::new(
394 tracing_subscriber::registry().with(self.tracing_layer::<F>().unwrap()),
395 ));
396 }
397}
398
399impl log::Log for GlobalLogger {
400 #[inline(always)]
401 fn enabled(&self, _m: &log::Metadata) -> bool {
402 true
403 }
404
405 #[inline(always)]
406 fn log(&self, r: &log::Record) {
407 let now = Timer::new();
408 if let Some(inner) = self.inner.as_ref() {
409 match &inner.sinks {
410 LoggerInnerSink::Once(inner) => {
411 for sink in inner.iter() {
412 sink.log(&now, r);
413 }
414 }
415 LoggerInnerSink::Dyn(inner) => {
416 let sinks = inner.load();
417 for sink in sinks.iter() {
418 sink.log(&now, r);
419 }
420 }
421 }
422 }
423 }
424
425 fn flush(&self) {
433 if let Some(inner) = self.inner.as_ref() {
434 match &inner.sinks {
435 LoggerInnerSink::Once(inner) => {
436 for sink in inner.iter() {
437 sink.flush();
438 }
439 }
440 LoggerInnerSink::Dyn(inner) => {
441 let sinks = inner.load();
442 for sink in sinks.iter() {
443 sink.flush();
444 }
445 }
446 }
447 }
448 }
449}
450
451static GLOBAL_LOGGER: GlobalLoggerStatic = GlobalLoggerStatic::new();
452
453#[doc(hidden)]
455pub fn log_panic(info: &std::panic::PanicHookInfo) {
456 let bt = Backtrace::new();
457 let mut record = log::Record::builder();
458 record.level(log::Level::Error);
459 if let Some(loc) = info.location() {
460 record.file(Some(loc.file())).line(Some(loc.line()));
461 }
462 log::logger().log(&record.args(format_args!("panic occur: {}\ntrace: {:?}", info, bt)).build());
463 eprint!("panic occur: {} at {:?}\ntrace: {:?}", info, info.location(), bt);
464}
465
466#[inline(always)]
467fn panic_hook_force_exit(info: &std::panic::PanicHookInfo) {
468 log_panic(info);
469 log::logger().flush();
470 let msg = format!("{}", info).to_string();
471 std::panic::resume_unwind(Box::new(msg));
472}
473
474#[inline(always)]
475fn panic_hook_log(info: &std::panic::PanicHookInfo) {
476 log_panic(info);
477 log::logger().flush();
478}