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 return Ok(false);
124 }
125 Some(false) => {
126 if !builder.dynamic {
128 let e = Error::other("log config differs but dynamic=false");
129 eprintln!("{:?}", e);
130 return Err(e);
131 }
132 let logger = self.get_logger();
133 if let Err(e) = logger.reinit(builder) {
134 eprintln!("{:?}", e);
135 return Err(e);
136 }
137 log::set_max_level(builder.get_max_level());
139 #[cfg(feature = "tracing")]
140 {
141 if builder.tracing_global {
142 logger.init_tracing_global()?;
143 }
144 }
145 return Ok(false);
146 }
147 None => {
148 let res = { self.get_logger_mut().init(builder) };
150 res?;
151 #[cfg(feature = "tracing")]
152 {
153 let logger = self.get_logger();
154 if builder.tracing_global {
155 logger.init_tracing_global()?;
156 }
157 }
158 self.inited.store(true, Ordering::SeqCst);
159 return Ok(true);
160 }
161 }
162 }
163}
164
165unsafe impl Send for GlobalLoggerStatic {}
166unsafe impl Sync for GlobalLoggerStatic {}
167
168pub fn setup_log(builder: Builder) -> Result<&'static GlobalLogger, Error> {
174 if GLOBAL_LOGGER.try_setup(&builder)? {
175 let logger = GLOBAL_LOGGER.get_logger();
176 if let Err(e) = log::set_logger(logger) {
178 eprintln!("log::set_logger return error: {:?}", e);
179 return Err(Error::other(format!("log::set_logger() failed: {:?}", e)));
180 }
181 log::set_max_level(builder.get_max_level());
182 if builder.continue_when_panic {
184 std::panic::set_hook(Box::new(panic_no_exit_hook));
185 } else {
186 std::panic::set_hook(Box::new(panic_and_exit_hook));
187 }
188 let signals = builder.rotation_signals.clone();
189 if signals.len() > 0 {
190 if false == logger.signal_listener.swap(true, Ordering::SeqCst) {
191 thread::spawn(move || {
192 GLOBAL_LOGGER.get_logger().listener_for_signal(signals);
193 });
194 }
195 }
196 Ok(logger)
197 } else {
198 Ok(GLOBAL_LOGGER.get_logger())
199 }
200}
201
202pub fn get_global_logger() -> Option<&'static GlobalLogger> {
204 GLOBAL_LOGGER.try_get_logger()
205}
206
207pub struct GlobalLogger {
209 config_checksum: AtomicU64,
211 inner: Option<LoggerInner>,
214 signal_listener: AtomicBool,
215 #[cfg(feature = "tracing")]
216 tracing_inited: AtomicBool,
217}
218
219enum LoggerInnerSink {
220 Once(Vec<LogSink>),
221 Dyn(ArcSwap<Vec<LogSink>>),
223}
224
225struct LoggerInner {
226 sinks: LoggerInnerSink,
227}
228
229unsafe impl Send for LoggerInner {}
230unsafe impl Sync for LoggerInner {}
231
232impl LoggerInner {
233 #[inline]
234 fn new(dynamic: bool, sinks: Vec<LogSink>) -> Self {
235 let sinks = if dynamic {
236 LoggerInnerSink::Dyn(ArcSwap::new(Arc::new(sinks)))
237 } else {
238 LoggerInnerSink::Once(sinks)
239 };
240 Self { sinks }
241 }
242
243 #[inline]
244 fn set(&self, sinks: Vec<LogSink>) -> std::io::Result<()> {
245 match &self.sinks {
246 LoggerInnerSink::Once(_) => {
247 let e = Error::other("previous logger does not init with dynamic=true");
248 return Err(e);
249 }
250 LoggerInnerSink::Dyn(d) => {
251 d.store(Arc::new(sinks));
252 }
253 }
254 Ok(())
255 }
256}
257
258impl GlobalLogger {
259 fn listener_for_signal(&self, signals: Vec<i32>) {
260 println!("signal_listener started");
261 let mut signals = Signals::new(&signals).unwrap();
262 for __sig in signals.forever() {
263 let _ = self.reopen();
264 }
265 println!("signal_listener exit");
266 }
267
268 fn open(&self) -> std::io::Result<()> {
270 if let Some(inner) = self.inner.as_ref() {
271 match &inner.sinks {
272 LoggerInnerSink::Once(inner) => {
273 for sink in inner.iter() {
274 sink.open()?;
275 }
276 }
277 LoggerInnerSink::Dyn(inner) => {
278 let sinks = inner.load();
279 for sink in sinks.iter() {
280 sink.open()?;
281 }
282 }
283 }
284 }
285 println!("log sinks opened");
286 Ok(())
287 }
288
289 pub fn reopen(&self) -> std::io::Result<()> {
291 if let Some(inner) = self.inner.as_ref() {
292 match &inner.sinks {
293 LoggerInnerSink::Once(inner) => {
294 for sink in inner.iter() {
295 sink.reopen()?;
296 }
297 }
298 LoggerInnerSink::Dyn(inner) => {
299 let sinks = inner.load();
300 for sink in sinks.iter() {
301 sink.reopen()?;
302 }
303 }
304 }
305 }
306 println!("log sinks re-opened");
307 Ok(())
308 }
309
310 #[inline]
312 fn check_the_same(&self, builder: &Builder) -> Option<bool> {
313 if self.inner.is_some() {
314 return Some(self.config_checksum.load(Ordering::Acquire) == builder.cal_checksum());
315 }
316 None
317 }
318
319 fn reinit(&self, builder: &Builder) -> std::io::Result<()> {
321 let sinks = builder.build_sinks()?;
322 if let Some(inner) = self.inner.as_ref() {
323 inner.set(sinks)?;
324 self.config_checksum.store(builder.cal_checksum(), Ordering::Release);
325 } else {
326 unreachable!();
327 }
328 Ok(())
329 }
330
331 fn init(&mut self, builder: &Builder) -> std::io::Result<()> {
332 let sinks = builder.build_sinks()?;
333 assert!(self.inner.is_none());
334 self.inner.replace(LoggerInner::new(builder.dynamic, sinks));
335 self.config_checksum.store(builder.cal_checksum(), Ordering::Release);
336 Ok(())
337 }
338
339 #[cfg(feature = "tracing")]
340 #[inline]
341 fn init_tracing_global(&'static self) -> Result<(), Error> {
342 let dist = self.tracing_dispatch::<TracingText>()?;
343 if let Err(_) = dispatcher::set_global_default(dist) {
344 let e = Error::other("tracing global dispatcher already exists");
345 eprintln!("{:?}", e);
346 return Err(e);
347 }
348 self.tracing_inited.store(true, Ordering::SeqCst);
349 Ok(())
350 }
351
352 #[cfg(feature = "tracing")]
353 #[cfg_attr(docsrs, doc(cfg(feature = "tracing")))]
354 pub fn tracing_layer<F: TracingFormatter>(
363 &'static self,
364 ) -> std::io::Result<CaptainsLogLayer<F>> {
365 if self.tracing_inited.load(Ordering::SeqCst) {
366 let e = Error::other("global tracing dispatcher exists");
367 eprintln!("{:?}", e);
368 return Err(e);
369 }
370 return Ok(CaptainsLogLayer::<F>::new(self));
371 }
372
373 #[cfg(feature = "tracing")]
374 #[cfg_attr(docsrs, doc(cfg(feature = "tracing")))]
375 pub fn tracing_dispatch<F: TracingFormatter>(&'static self) -> std::io::Result<Dispatch> {
384 if self.tracing_inited.load(Ordering::SeqCst) {
385 let e = Error::other("global tracing dispatcher exists");
386 eprintln!("{:?}", e);
387 return Err(e);
388 }
389 return Ok(Dispatch::new(
390 tracing_subscriber::registry().with(self.tracing_layer::<F>().unwrap()),
391 ));
392 }
393}
394
395impl log::Log for GlobalLogger {
396 #[inline(always)]
397 fn enabled(&self, _m: &log::Metadata) -> bool {
398 true
399 }
400
401 #[inline(always)]
402 fn log(&self, r: &log::Record) {
403 let now = Timer::new();
404 if let Some(inner) = self.inner.as_ref() {
405 match &inner.sinks {
406 LoggerInnerSink::Once(inner) => {
407 for sink in inner.iter() {
408 sink.log(&now, r);
409 }
410 }
411 LoggerInnerSink::Dyn(inner) => {
412 let sinks = inner.load();
413 for sink in sinks.iter() {
414 sink.log(&now, r);
415 }
416 }
417 }
418 }
419 }
420
421 fn flush(&self) {
429 if let Some(inner) = self.inner.as_ref() {
430 match &inner.sinks {
431 LoggerInnerSink::Once(inner) => {
432 for sink in inner.iter() {
433 sink.flush();
434 }
435 }
436 LoggerInnerSink::Dyn(inner) => {
437 let sinks = inner.load();
438 for sink in sinks.iter() {
439 sink.flush();
440 }
441 }
442 }
443 }
444 }
445}
446
447static GLOBAL_LOGGER: GlobalLoggerStatic = GlobalLoggerStatic::new();
448
449#[doc(hidden)]
451pub fn log_panic(info: &std::panic::PanicHookInfo) {
452 let bt = Backtrace::new();
453 let mut record = log::Record::builder();
454 record.level(log::Level::Error);
455 if let Some(loc) = info.location() {
456 record.file(Some(loc.file())).line(Some(loc.line()));
457 }
458 log::logger().log(&record.args(format_args!("panic occur: {}\ntrace: {:?}", info, bt)).build());
459 eprint!("panic occur: {} at {:?}\ntrace: {:?}", info, info.location(), bt);
460}
461
462#[inline(always)]
463fn panic_and_exit_hook(info: &std::panic::PanicHookInfo) {
464 log_panic(info);
465 log::logger().flush();
466 let msg = format!("{}", info).to_string();
467 std::panic::resume_unwind(Box::new(msg));
468}
469
470#[inline(always)]
471fn panic_no_exit_hook(info: &std::panic::PanicHookInfo) {
472 log_panic(info);
473 eprint!("not debug version, so don't exit process");
474 log::logger().flush();
475}