diff --git a/spdlog/src/lib.rs b/spdlog/src/lib.rs index 624762c5..141ae3e5 100644 --- a/spdlog/src/lib.rs +++ b/spdlog/src/lib.rs @@ -736,8 +736,6 @@ pub fn log_crate_proxy() -> &'static LogCrateProxy { &PROXY } -static IS_TEARING_DOWN: AtomicBool = AtomicBool::new(false); - fn flush_default_logger_at_exit() { // Rust never calls `drop` for static variables. // @@ -745,9 +743,8 @@ fn flush_default_logger_at_exit() { // once at the program exit, thus we don't lose the last logs. extern "C" fn handler() { - IS_TEARING_DOWN.store(true, Ordering::SeqCst); if let Some(default_logger) = DEFAULT_LOGGER.get() { - default_logger.load().flush() + default_logger.load().flush_sinks_atexit() } } diff --git a/spdlog/src/logger.rs b/spdlog/src/logger.rs index 1b769aab..944e3ee6 100644 --- a/spdlog/src/logger.rs +++ b/spdlog/src/logger.rs @@ -497,14 +497,22 @@ impl Logger { } } - fn flush_sinks(&self) { + fn flush_sinks_with(&self, with: impl Fn(&dyn Sink) -> Result<()>) { self.sinks.iter().for_each(|sink| { - if let Err(err) = sink.flush() { + if let Err(err) = with(&**sink) { self.handle_error(err); } }); } + pub(crate) fn flush_sinks_atexit(&self) { + self.flush_sinks_with(|sink| sink.flush_atexit()); + } + + pub(crate) fn flush_sinks(&self) { + self.flush_sinks_with(|sink| sink.flush()); + } + fn handle_error(&self, err: Error) { self.error_handler.read_expect().call_internal( format!( diff --git a/spdlog/src/sink/async_sink/async_pool_sink.rs b/spdlog/src/sink/async_sink/async_pool_sink.rs index 85833337..8187e3cd 100644 --- a/spdlog/src/sink/async_sink/async_pool_sink.rs +++ b/spdlog/src/sink/async_sink/async_pool_sink.rs @@ -129,21 +129,21 @@ impl Sink for AsyncPoolSink { } fn flush(&self) -> Result<()> { - if crate::IS_TEARING_DOWN.load(Ordering::SeqCst) { - // https://github.com/SpriteOvO/spdlog-rs/issues/64 - // - // If the program is tearing down, this will be the final flush. `crossbeam` - // uses thread-local internally, which is not supported in `atexit` callback. - // This can be bypassed by flushing sinks directly on the current thread, but - // before we do that we have to destroy the thread pool to ensure that any - // pending log tasks are completed. - self.thread_pool.destroy(); - self.backend.flush() - } else { - self.assign_task(Task::Flush { - backend: self.clone_backend(), - }) - } + self.assign_task(Task::Flush { + backend: self.clone_backend(), + }) + } + + fn flush_atexit(&self) -> Result<()> { + // https://github.com/SpriteOvO/spdlog-rs/issues/64 + // + // If the program is tearing down, this will be the final flush. `crossbeam` + // uses thread-local internally, which is not supported in `atexit` callback. + // This can be bypassed by flushing sinks directly on the current thread, but + // before we do that we have to destroy the thread pool to ensure that any + // pending log tasks are completed. + self.thread_pool.destroy(); + self.backend.flush_atexit() } } @@ -258,14 +258,22 @@ impl Backend { result } - fn flush(&self) -> Result<()> { + fn flush_with(&self, with: impl Fn(&dyn Sink) -> Result<()>) -> Result<()> { let mut result = Ok(()); for sink in &self.sinks { - result = Error::push_result(result, sink.flush()); + result = Error::push_result(result, with(&**sink)); } result } + fn flush(&self) -> Result<()> { + self.flush_with(|sink| sink.flush()) + } + + fn flush_atexit(&self) -> Result<()> { + self.flush_with(|sink| sink.flush_atexit()) + } + fn handle_error(&self, err: Error) { self.prop.call_error_handler_internal("AsyncPoolSink", err) } diff --git a/spdlog/src/sink/dedup_sink.rs b/spdlog/src/sink/dedup_sink.rs index 241f73f3..21b373b1 100644 --- a/spdlog/src/sink/dedup_sink.rs +++ b/spdlog/src/sink/dedup_sink.rs @@ -152,12 +152,20 @@ impl DedupSink { }) } - fn flush_sinks(&self) -> Result<()> { + fn flush_with(&self, with: fn(&dyn Sink) -> Result<()>) -> Result<()> { #[allow(clippy::manual_try_fold)] // https://github.com/rust-lang/rust-clippy/issues/11554 self.sinks.iter().fold(Ok(()), |result, sink| { - Error::push_result(result, sink.flush()) + Error::push_result(result, with(sink.as_ref())) }) } + + fn flush_sinks(&self) -> Result<()> { + self.flush_with(|sink| sink.flush()) + } + + fn flush_sinks_atexit(&self) -> Result<()> { + self.flush_with(|sink| sink.flush_atexit()) + } } impl GetSinkProp for DedupSink { @@ -186,6 +194,10 @@ impl Sink for DedupSink { fn flush(&self) -> Result<()> { self.flush_sinks() } + + fn flush_atexit(&self) -> Result<()> { + self.flush_sinks_atexit() + } } impl Drop for DedupSink { diff --git a/spdlog/src/sink/mod.rs b/spdlog/src/sink/mod.rs index dbdc250f..fd893a1e 100644 --- a/spdlog/src/sink/mod.rs +++ b/spdlog/src/sink/mod.rs @@ -231,6 +231,13 @@ pub trait Sink: SinkPropAccess + Sync + Send { /// Flushes any buffered records. fn flush(&self) -> Result<()>; + + /// Flushes any buffered records at program exit. + /// + /// Default to call `flush()`. + fn flush_atexit(&self) -> Result<()> { + self.flush() + } } /// Container type for [`Sink`]s.