From a404a8609af441f7b172de5309bbc50c4fa9ec19 Mon Sep 17 00:00:00 2001 From: Asuna Date: Thu, 28 Aug 2025 17:39:28 +0800 Subject: [PATCH 1/3] Decouple properties from `Sink` into `SinkProp` and `SinkAccess` --- spdlog/Cargo.toml | 1 + spdlog/benches/spdlog-rs/pattern.rs | 4 +- spdlog/examples/05_sink.rs | 44 ++-- spdlog/src/formatter/mod.rs | 11 +- spdlog/src/formatter/null_formatter.rs | 26 +++ spdlog/src/lib.rs | 3 + spdlog/src/log_macros.rs | 33 ++- spdlog/src/periodic_worker.rs | 2 +- spdlog/src/sink/async_sink/async_pool_sink.rs | 105 ++++++---- spdlog/src/sink/dedup_sink.rs | 57 ++++-- spdlog/src/sink/file_sink.rs | 61 ++++-- spdlog/src/sink/helper.rs | 190 ------------------ spdlog/src/sink/journald_sink.rs | 70 +++++-- spdlog/src/sink/mod.rs | 164 ++++++++++++++- spdlog/src/sink/rotating_file_sink.rs | 64 ++++-- spdlog/src/sink/std_stream_sink.rs | 60 ++++-- spdlog/src/sink/win_debug_sink.rs | 62 ++++-- spdlog/src/sink/write_sink.rs | 64 ++++-- spdlog/src/sync.rs | 3 + spdlog/src/test_utils/common.rs | 54 ++--- spdlog/tests/global_async_pool_sink.rs | 33 ++- spdlog/tests/pattern.rs | 35 ++-- 22 files changed, 661 insertions(+), 485 deletions(-) create mode 100644 spdlog/src/formatter/null_formatter.rs delete mode 100644 spdlog/src/sink/helper.rs diff --git a/spdlog/Cargo.toml b/spdlog/Cargo.toml index 9a0eade..181d623 100644 --- a/spdlog/Cargo.toml +++ b/spdlog/Cargo.toml @@ -53,6 +53,7 @@ if_chain = "1.0.2" is-terminal = "0.4" log = { version = "0.4.21", optional = true, features = ["kv"] } once_cell = "1.16.0" +parking_lot = "0.12.0" serde = { version = "1.0.163", optional = true, features = ["derive"] } serde_json = { version = "1.0.120", optional = true } spdlog-internal = { version = "=0.1.0", path = "../spdlog-internal", optional = true } diff --git a/spdlog/benches/spdlog-rs/pattern.rs b/spdlog/benches/spdlog-rs/pattern.rs index efafbca..3c156c1 100644 --- a/spdlog/benches/spdlog-rs/pattern.rs +++ b/spdlog/benches/spdlog-rs/pattern.rs @@ -10,7 +10,7 @@ use spdlog::formatter::JsonFormatter; use spdlog::{ formatter::{pattern, Formatter, FormatterContext, FullFormatter, Pattern, PatternFormatter}, prelude::*, - sink::Sink, + sink::{Sink, SinkAccess}, Record, StringBuf, }; use spdlog_macros::runtime_pattern; @@ -56,7 +56,9 @@ impl Sink for BenchSink<'_, F> { fn flush(&self) -> spdlog::Result<()> { unimplemented!() } +} +impl SinkAccess for BenchSink<'_, F> { fn level_filter(&self) -> LevelFilter { LevelFilter::All } diff --git a/spdlog/examples/05_sink.rs b/spdlog/examples/05_sink.rs index d3af27b..d4f8a2d 100644 --- a/spdlog/examples/05_sink.rs +++ b/spdlog/examples/05_sink.rs @@ -1,26 +1,21 @@ -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::{Arc, Mutex}; -use atomic::{Atomic, Ordering}; use spdlog::{ - formatter::{Formatter, FormatterContext, FullFormatter}, + formatter::FormatterContext, prelude::*, - sink::Sink, - ErrorHandler, Record, StringBuf, + sink::{GetSinkProp, Sink, SinkProp}, + Record, StringBuf, }; struct CollectVecSink { - level_filter: Atomic, - formatter: RwLock>, - error_handler: Atomic>, + prop: SinkProp, collected: Mutex>, } impl CollectVecSink { fn new() -> Self { Self { - level_filter: Atomic::new(LevelFilter::All), - formatter: RwLock::new(Box::new(FullFormatter::new())), - error_handler: Atomic::new(None), + prop: SinkProp::default(), collected: Mutex::new(Vec::new()), } } @@ -30,13 +25,18 @@ impl CollectVecSink { } } +impl GetSinkProp for CollectVecSink { + fn prop(&self) -> &SinkProp { + &self.prop + } +} + impl Sink for CollectVecSink { fn log(&self, record: &Record) -> spdlog::Result<()> { let mut string_buf = StringBuf::new(); let mut ctx = FormatterContext::new(); - self.formatter - .read() - .unwrap() + self.prop + .formatter() .format(record, &mut string_buf, &mut ctx)?; self.collected.lock().unwrap().push(string_buf.to_string()); Ok(()) @@ -45,22 +45,6 @@ impl Sink for CollectVecSink { fn flush(&self) -> spdlog::Result<()> { Ok(()) } - - fn level_filter(&self) -> LevelFilter { - self.level_filter.load(Ordering::Relaxed) - } - - fn set_level_filter(&self, level_filter: LevelFilter) { - self.level_filter.store(level_filter, Ordering::Relaxed); - } - - fn set_formatter(&self, formatter: Box) { - *self.formatter.write().unwrap() = formatter; - } - - fn set_error_handler(&self, handler: Option) { - self.error_handler.store(handler, Ordering::Relaxed); - } } fn main() -> Result<(), Box> { diff --git a/spdlog/src/formatter/mod.rs b/spdlog/src/formatter/mod.rs index d8b1871..03df271 100644 --- a/spdlog/src/formatter/mod.rs +++ b/spdlog/src/formatter/mod.rs @@ -5,7 +5,7 @@ //! Each normal *Sink* owns a *Formatter*, which is used to format each log. //! //! The default formatter for most sinks is [`FullFormatter`], you can call -//! [`Sink::set_formatter`] to replace it with another formatter. +//! [`SinkAccess::set_formatter`] to replace it with another formatter. //! //! The easiest way to make a custom formatter is to build a pattern, see //! [Compile-time and runtime pattern @@ -23,7 +23,10 @@ //! - Macro [`runtime_pattern!`]: Builds a pattern at runtime. //! //! ``` -//! use spdlog::formatter::{pattern, PatternFormatter}; +//! use spdlog::{ +//! formatter::{pattern, PatternFormatter}, +//! prelude::*, +//! }; //! # use spdlog::sink::{Sink, WriteSink}; //! //! # fn main() -> Result<(), Box> { @@ -45,7 +48,7 @@ //! # Ok(()) } //! ``` //! -//! [`Sink::set_formatter`]: crate::sink::Sink::set_formatter +//! [`SinkAccess::set_formatter`]: crate::sink::SinkAccess::set_formatter //! [./examples]: https://github.com/SpriteOvO/spdlog-rs/tree/main/spdlog/examples mod full_formatter; @@ -57,6 +60,7 @@ mod journald_formatter; #[cfg(feature = "serde_json")] mod json_formatter; mod local_time_cacher; +mod null_formatter; mod pattern_formatter; use std::ops::Range; @@ -71,6 +75,7 @@ pub(crate) use journald_formatter::*; #[cfg(feature = "serde_json")] pub use json_formatter::*; pub(crate) use local_time_cacher::*; +pub(crate) use null_formatter::*; pub use pattern_formatter::*; use crate::{Record, Result, StringBuf}; diff --git a/spdlog/src/formatter/null_formatter.rs b/spdlog/src/formatter/null_formatter.rs new file mode 100644 index 0000000..e3da9c5 --- /dev/null +++ b/spdlog/src/formatter/null_formatter.rs @@ -0,0 +1,26 @@ +use std::marker::PhantomData; + +use crate::{ + formatter::{Formatter, FormatterContext}, + Record, StringBuf, +}; + +#[derive(Clone)] +pub(crate) struct UnreachableFormatter(PhantomData<()>); + +impl UnreachableFormatter { + pub(crate) fn new() -> Self { + Self(PhantomData) + } +} + +impl Formatter for UnreachableFormatter { + fn format( + &self, + _record: &Record, + _dest: &mut StringBuf, + _ctx: &mut FormatterContext, + ) -> crate::Result<()> { + unreachable!() + } +} diff --git a/spdlog/src/lib.rs b/spdlog/src/lib.rs index 9e54f8a..75dbd6c 100644 --- a/spdlog/src/lib.rs +++ b/spdlog/src/lib.rs @@ -322,6 +322,9 @@ pub use thread_pool::*; /// Contains all log macros and common types. pub mod prelude { + // Traits + pub use super::sink::SinkAccess as _; + // Types pub use super::{ critical, debug, error, info, log, trace, warn, Level, LevelFilter, Logger, LoggerBuilder, }; diff --git a/spdlog/src/log_macros.rs b/spdlog/src/log_macros.rs index d5b94bf..d442792 100644 --- a/spdlog/src/log_macros.rs +++ b/spdlog/src/log_macros.rs @@ -216,13 +216,12 @@ mod tests { }; use crate::{ - formatter::Formatter, kv::Key, prelude::*, - sink::Sink, + sink::{GetSinkProp, Sink, SinkProp}, test_utils::{self, *}, utils::RefStr, - ErrorHandler, Record, + Record, }; #[test] @@ -369,7 +368,15 @@ mod tests { #[test] fn kv_types() { - struct Asserter; + struct Asserter { + prop: SinkProp, + } + + impl GetSinkProp for Asserter { + fn prop(&self) -> &SinkProp { + &self.prop + } + } impl Sink for Asserter { fn should_log(&self, _: Level) -> bool { @@ -378,18 +385,6 @@ mod tests { fn flush(&self) -> crate::Result<()> { Ok(()) } - fn level_filter(&self) -> LevelFilter { - LevelFilter::All - } - fn set_level_filter(&self, _: LevelFilter) { - unimplemented!() - } - fn set_formatter(&self, _: Box) { - unimplemented!() - } - fn set_error_handler(&self, _: Option) { - unimplemented!() - } fn log(&self, record: &Record) -> crate::Result<()> { let kvs = record.key_values(); @@ -410,7 +405,11 @@ mod tests { } } - let asserter = test_utils::build_test_logger(|b| b.sink(Arc::new(Asserter))); + let asserter = test_utils::build_test_logger(|b| { + b.sink(Arc::new(Asserter { + prop: SinkProp::default(), + })) + }); #[cfg_attr(feature = "sval", derive(sval_derive::Value))] #[cfg_attr(feature = "serde", derive(serde::Serialize))] diff --git a/spdlog/src/periodic_worker.rs b/spdlog/src/periodic_worker.rs index e9938cd..351842d 100644 --- a/spdlog/src/periodic_worker.rs +++ b/spdlog/src/periodic_worker.rs @@ -11,7 +11,7 @@ impl PeriodicWorker { // Panic if the `interval.is_zero()` is `true`. #[allow(clippy::mutex_atomic)] #[must_use] - pub fn new(callback: impl Fn() -> bool + Send + Sync + 'static, interval: Duration) -> Self { + pub fn new(callback: impl Fn() -> bool + Send + 'static, interval: Duration) -> Self { if interval.is_zero() { panic!("PeriodicWorker: the interval cannot be zero") } diff --git a/spdlog/src/sink/async_sink/async_pool_sink.rs b/spdlog/src/sink/async_sink/async_pool_sink.rs index a622b03..0345c1f 100644 --- a/spdlog/src/sink/async_sink/async_pool_sink.rs +++ b/spdlog/src/sink/async_sink/async_pool_sink.rs @@ -1,7 +1,7 @@ use crate::{ default_error_handler, default_thread_pool, - formatter::Formatter, - sink::{helper, OverflowPolicy, Sink, Sinks}, + formatter::{Formatter, UnreachableFormatter}, + sink::{OverflowPolicy, Sink, SinkAccess, SinkProp, Sinks}, sync::*, Error, ErrorHandler, LevelFilter, Record, RecordOwned, Result, ThreadPool, }; @@ -46,7 +46,6 @@ use crate::{ /// [./examples]: https://github.com/SpriteOvO/spdlog-rs/tree/main/spdlog/examples // The names `AsyncSink` and `AsyncRuntimeSink` is reserved for future use. pub struct AsyncPoolSink { - level_filter: Atomic, overflow_policy: OverflowPolicy, thread_pool: Arc, backend: Arc, @@ -69,12 +68,17 @@ impl AsyncPoolSink { /// [thread_pool]: AsyncPoolSinkBuilder::thread_pool #[must_use] pub fn builder() -> AsyncPoolSinkBuilder { + let prop = SinkProp::default(); + // AsyncPoolSink does not have its own formatter, and we do not impl + // `GetSinkProp` for it, so there should be no way to access the + // formatter inside the `prop`. + prop.set_formatter(Box::new(UnreachableFormatter::new())); + AsyncPoolSinkBuilder { - level_filter: helper::SINK_DEFAULT_LEVEL_FILTER, + prop, overflow_policy: OverflowPolicy::Block, sinks: Sinks::new(), thread_pool: None, - error_handler: None, } } @@ -84,11 +88,6 @@ impl AsyncPoolSink { &self.backend.sinks } - /// Sets a error handler. - pub fn set_error_handler(&self, handler: Option) { - self.backend.error_handler.swap(handler, Ordering::Relaxed); - } - fn assign_task(&self, task: Task) -> Result<()> { self.thread_pool.assign_task(task, self.overflow_policy) } @@ -99,6 +98,28 @@ impl AsyncPoolSink { } } +impl SinkAccess for AsyncPoolSink { + fn level_filter(&self) -> LevelFilter { + self.backend.prop.level_filter() + } + + fn set_level_filter(&self, level_filter: LevelFilter) { + self.backend.prop.set_level_filter(level_filter); + } + + /// For [`AsyncPoolSink`], the function performs the same call to all + /// internal sinks. + fn set_formatter(&self, formatter: Box) { + for sink in &self.backend.sinks { + sink.set_formatter(formatter.clone()) + } + } + + fn set_error_handler(&self, handler: Option) { + self.backend.prop.set_error_handler(handler); + } +} + impl Sink for AsyncPoolSink { fn log(&self, record: &Record) -> Result<()> { self.assign_task(Task::Log { @@ -124,31 +145,14 @@ impl Sink for AsyncPoolSink { }) } } - - /// For [`AsyncPoolSink`], the function performs the same call to all - /// internal sinks. - fn set_formatter(&self, formatter: Box) { - for sink in &self.backend.sinks { - sink.set_formatter(formatter.clone()) - } - } - - helper::common_impl! { - @SinkCustom { - level_filter: level_filter, - formatter: None, - error_handler: backend.error_handler, - } - } } #[allow(missing_docs)] pub struct AsyncPoolSinkBuilder { - level_filter: LevelFilter, + prop: SinkProp, sinks: Sinks, overflow_policy: OverflowPolicy, thread_pool: Option>, - error_handler: Option, } impl AsyncPoolSinkBuilder { @@ -190,33 +194,56 @@ impl AsyncPoolSinkBuilder { self } + // Prop + // + + /// Specifies a log level filter. + /// + /// This parameter is **optional**. + #[must_use] + pub fn level_filter(self, level_filter: LevelFilter) -> Self { + self.prop.set_level_filter(level_filter); + self + } + + /// Specifies a formatter. + /// + /// This parameter is **optional**. + #[must_use] + pub fn formatter(self, formatter: Box) -> Self { + self.prop.set_formatter(formatter); + self + } + + /// Specifies an error handler. + /// + /// This parameter is **optional**. + #[must_use] + pub fn error_handler(self, handler: ErrorHandler) -> Self { + self.prop.set_error_handler(Some(handler)); + self + } + /// Builds a [`AsyncPoolSink`]. pub fn build(self) -> Result { let backend = Arc::new(Backend { + prop: self.prop, sinks: self.sinks.clone(), - error_handler: Atomic::new(self.error_handler), }); let thread_pool = self.thread_pool.unwrap_or_else(default_thread_pool); Ok(AsyncPoolSink { - level_filter: Atomic::new(self.level_filter), overflow_policy: self.overflow_policy, thread_pool, backend, }) } - - helper::common_impl!(@SinkBuilderCustom { - level_filter: level_filter, - formatter: None, - error_handler: error_handler, - }); } pub(crate) struct Backend { + prop: SinkProp, sinks: Sinks, - error_handler: helper::SinkErrorHandler, } impl Backend { @@ -237,8 +264,8 @@ impl Backend { } fn handle_error(&self, err: Error) { - self.error_handler - .load(Ordering::Relaxed) + self.prop + .error_handler() .unwrap_or(|err| default_error_handler("AsyncPoolSink", err))(err); } } diff --git a/spdlog/src/sink/dedup_sink.rs b/spdlog/src/sink/dedup_sink.rs index 8e375ed..eed721e 100644 --- a/spdlog/src/sink/dedup_sink.rs +++ b/spdlog/src/sink/dedup_sink.rs @@ -1,9 +1,10 @@ use std::{cmp::Ordering, convert::Infallible, sync::Arc, time::Duration}; use crate::{ - sink::{helper, Sink, Sinks}, + formatter::Formatter, + sink::{GetSinkProp, Sink, SinkProp, Sinks}, sync::*, - Error, Record, RecordOwned, Result, + Error, ErrorHandler, LevelFilter, Record, RecordOwned, Result, }; struct DedupSinkState { @@ -79,7 +80,7 @@ struct DedupSinkState { /// /// [combined sink]: index.html#combined-sink pub struct DedupSink { - common_impl: helper::CommonImpl, + prop: SinkProp, sinks: Sinks, skip_duration: Duration, state: Mutex, @@ -106,7 +107,7 @@ impl DedupSink { #[must_use] pub fn builder() -> DedupSinkBuilder<()> { DedupSinkBuilder { - common_builder_impl: helper::CommonBuilderImpl::new(), + prop: SinkProp::default(), sinks: vec![], skip_duration: (), } @@ -159,6 +160,12 @@ impl DedupSink { } } +impl GetSinkProp for DedupSink { + fn prop(&self) -> &SinkProp { + &self.prop + } +} + impl Sink for DedupSink { fn log(&self, record: &Record) -> Result<()> { let mut state = self.state.lock_expect(); @@ -179,17 +186,15 @@ impl Sink for DedupSink { fn flush(&self) -> Result<()> { self.flush_sinks() } - - helper::common_impl!(@Sink: common_impl); } impl Drop for DedupSink { fn drop(&mut self) { if let Err(err) = self.log_skipping_message(&mut self.state.lock_expect()) { - self.common_impl.non_returnable_error("DedupSink", err); + self.prop.non_returnable_error("DedupSink", err); } if let Err(err) = self.flush_sinks() { - self.common_impl.non_returnable_error("DedupSink", err); + self.prop.non_returnable_error("DedupSink", err); } } } @@ -197,7 +202,7 @@ impl Drop for DedupSink { /// # #[doc = include_str!("../include/doc/generic-builder-note.md")] pub struct DedupSinkBuilder { - common_builder_impl: helper::CommonBuilderImpl, + prop: SinkProp, sinks: Sinks, skip_duration: ArgS, } @@ -227,13 +232,41 @@ impl DedupSinkBuilder { #[must_use] pub fn skip_duration(self, duration: Duration) -> DedupSinkBuilder { DedupSinkBuilder { - common_builder_impl: self.common_builder_impl, + prop: self.prop, sinks: self.sinks, skip_duration: duration, } } - helper::common_impl!(@SinkBuilder: common_builder_impl); + // Prop + // + + /// Specifies a log level filter. + /// + /// This parameter is **optional**. + #[must_use] + pub fn level_filter(self, level_filter: LevelFilter) -> Self { + self.prop.set_level_filter(level_filter); + self + } + + /// Specifies a formatter. + /// + /// This parameter is **optional**. + #[must_use] + pub fn formatter(self, formatter: Box) -> Self { + self.prop.set_formatter(formatter); + self + } + + /// Specifies an error handler. + /// + /// This parameter is **optional**. + #[must_use] + pub fn error_handler(self, handler: ErrorHandler) -> Self { + self.prop.set_error_handler(Some(handler)); + self + } } impl DedupSinkBuilder<()> { @@ -249,7 +282,7 @@ impl DedupSinkBuilder { /// Builds a [`DedupSink`]. pub fn build(self) -> Result { Ok(DedupSink { - common_impl: helper::CommonImpl::from_builder(self.common_builder_impl), + prop: self.prop, sinks: self.sinks, skip_duration: self.skip_duration, state: Mutex::new(DedupSinkState { diff --git a/spdlog/src/sink/file_sink.rs b/spdlog/src/sink/file_sink.rs index b016f9b..e594d00 100644 --- a/spdlog/src/sink/file_sink.rs +++ b/spdlog/src/sink/file_sink.rs @@ -8,10 +8,10 @@ use std::{ }; use crate::{ - formatter::FormatterContext, - sink::{helper, Sink}, + formatter::{Formatter, FormatterContext}, + sink::{GetSinkProp, Sink, SinkProp}, sync::*, - utils, Error, Record, Result, StringBuf, + utils, Error, ErrorHandler, LevelFilter, Record, Result, StringBuf, }; /// A sink with a file as the target. @@ -28,7 +28,7 @@ use crate::{ /// [`RotatingFileSink`]: crate::sink::RotatingFileSink /// [./examples]: https://github.com/SpriteOvO/spdlog-rs/tree/main/spdlog/examples pub struct FileSink { - common_impl: helper::CommonImpl, + prop: SinkProp, file: Mutex>, } @@ -55,10 +55,10 @@ impl FileSink { #[must_use] pub fn builder() -> FileSinkBuilder<()> { FileSinkBuilder { + prop: SinkProp::default(), path: (), truncate: false, capacity: None, - common_builder_impl: helper::CommonBuilderImpl::new(), } } @@ -87,13 +87,18 @@ impl FileSink { } } +impl GetSinkProp for FileSink { + fn prop(&self) -> &SinkProp { + &self.prop + } +} + impl Sink for FileSink { fn log(&self, record: &Record) -> Result<()> { let mut string_buf = StringBuf::new(); let mut ctx = FormatterContext::new(); - self.common_impl - .formatter - .read_expect() + self.prop + .formatter() .format(record, &mut string_buf, &mut ctx)?; self.file @@ -107,14 +112,12 @@ impl Sink for FileSink { fn flush(&self) -> Result<()> { self.file.lock_expect().flush().map_err(Error::FlushBuffer) } - - helper::common_impl!(@Sink: common_impl); } impl Drop for FileSink { fn drop(&mut self) { if let Err(err) = self.file.lock_expect().flush() { - self.common_impl + self.prop .non_returnable_error("FileSink", Error::FlushBuffer(err)) } } @@ -125,7 +128,7 @@ impl Drop for FileSink { /// # #[doc = include_str!("../include/doc/generic-builder-note.md")] pub struct FileSinkBuilder { - common_builder_impl: helper::CommonBuilderImpl, + prop: SinkProp, path: ArgPath, truncate: bool, capacity: Option, @@ -141,7 +144,7 @@ impl FileSinkBuilder { P: Into, { FileSinkBuilder { - common_builder_impl: self.common_builder_impl, + prop: self.prop, path: path.into(), truncate: self.truncate, capacity: self.capacity, @@ -168,7 +171,35 @@ impl FileSinkBuilder { self } - helper::common_impl!(@SinkBuilder: common_builder_impl); + // Prop + // + + /// Specifies a log level filter. + /// + /// This parameter is **optional**. + #[must_use] + pub fn level_filter(self, level_filter: LevelFilter) -> Self { + self.prop.set_level_filter(level_filter); + self + } + + /// Specifies a formatter. + /// + /// This parameter is **optional**. + #[must_use] + pub fn formatter(self, formatter: Box) -> Self { + self.prop.set_formatter(formatter); + self + } + + /// Specifies an error handler. + /// + /// This parameter is **optional**. + #[must_use] + pub fn error_handler(self, handler: ErrorHandler) -> Self { + self.prop.set_error_handler(Some(handler)); + self + } } impl FileSinkBuilder<()> { @@ -191,7 +222,7 @@ impl FileSinkBuilder { let file = utils::open_file_bufw(self.path, self.truncate, self.capacity)?; let sink = FileSink { - common_impl: helper::CommonImpl::from_builder(self.common_builder_impl), + prop: self.prop, file: Mutex::new(file), }; diff --git a/spdlog/src/sink/helper.rs b/spdlog/src/sink/helper.rs deleted file mode 100644 index e7bb727..0000000 --- a/spdlog/src/sink/helper.rs +++ /dev/null @@ -1,190 +0,0 @@ -use cfg_if::cfg_if; - -use crate::{ - formatter::{Formatter, FullFormatter}, - prelude::*, - sync::*, - Error, ErrorHandler, -}; - -pub(crate) type SinkErrorHandler = Atomic>; - -cfg_if! { - if #[cfg(test)] { - crate::utils::const_assert!(Atomic::::is_lock_free()); - } -} - -pub(crate) const SINK_DEFAULT_LEVEL_FILTER: LevelFilter = LevelFilter::All; - -pub(crate) struct CommonImpl { - pub(crate) level_filter: Atomic, - pub(crate) formatter: RwLock>, - pub(crate) error_handler: SinkErrorHandler, -} - -impl CommonImpl { - #[must_use] - pub(crate) fn from_builder(common_builder_impl: CommonBuilderImpl) -> Self { - Self::from_builder_with_formatter(common_builder_impl, || Box::new(FullFormatter::new())) - } - - #[must_use] - pub(crate) fn from_builder_with_formatter( - common_builder_impl: CommonBuilderImpl, - fallback: impl FnOnce() -> Box, - ) -> Self { - Self { - level_filter: Atomic::new(common_builder_impl.level_filter), - formatter: RwLock::new(common_builder_impl.formatter.unwrap_or_else(fallback)), - error_handler: Atomic::new(common_builder_impl.error_handler), - } - } - - #[allow(dead_code)] - #[must_use] - pub(crate) fn with_formatter(formatter: Box) -> Self { - Self { - level_filter: Atomic::new(LevelFilter::All), - formatter: RwLock::new(formatter), - error_handler: Atomic::new(None), - } - } - - pub(crate) fn non_returnable_error(&self, from: impl AsRef, err: Error) { - match self.error_handler.load(Ordering::Relaxed) { - Some(handler) => handler(err), - None => crate::default_error_handler(from, err), - } - } -} - -pub(crate) struct CommonBuilderImpl { - pub(crate) level_filter: LevelFilter, - pub(crate) formatter: Option>, - pub(crate) error_handler: Option, -} - -impl CommonBuilderImpl { - #[must_use] - pub(crate) fn new() -> Self { - Self { - level_filter: SINK_DEFAULT_LEVEL_FILTER, - formatter: None, - error_handler: None, - } - } -} - -macro_rules! common_impl { - // Sink - - ( @Sink: $($field:ident).+ ) => { - $crate::sink::helper::common_impl!(@SinkCustom { - level_filter: $($field).+.level_filter, - formatter: $($field).+.formatter, - error_handler: $($field).+.error_handler, - }); - }; - ( @SinkCustom { - level_filter: $($level_filter:ident).+, - formatter: $($formatter:ident).+, - error_handler: $($error_handler:ident).+$(,)? - } ) => { - $crate::sink::helper::common_impl!(@SinkCustomInner@level_filter: $($level_filter).+); - $crate::sink::helper::common_impl!(@SinkCustomInner@formatter: $($formatter).+); - $crate::sink::helper::common_impl!(@SinkCustomInner@error_handler: $($error_handler).+); - }; - ( @SinkCustomInner@level_filter: None ) => {}; - ( @SinkCustomInner@level_filter: $($field:ident).+ ) => { - fn level_filter(&self) -> $crate::LevelFilter { - self.$($field).+.load($crate::sync::Ordering::Relaxed) - } - - fn set_level_filter(&self, level_filter: $crate::LevelFilter) { - self.$($field).+.store(level_filter, $crate::sync::Ordering::Relaxed); - } - }; - ( @SinkCustomInner@formatter: None ) => {}; - ( @SinkCustomInner@formatter: $($field:ident).+ ) => { - fn set_formatter(&self, formatter: Box) { - use crate::sync::RwLockExtend as _; - *self.$($field).+.write_expect() = formatter; - } - }; - ( @SinkCustomInner@error_handler: None ) => {}; - ( @SinkCustomInner@error_handler: $($field:ident).+ ) => { - fn set_error_handler(&self, handler: Option<$crate::ErrorHandler>) { - self.$($field).+.store(handler, $crate::sync::Ordering::Relaxed); - } - }; - - // SinkBuiler - - ( @SinkBuilder: $($field:ident).+ ) => { - $crate::sink::helper::common_impl!(@SinkBuilderCustomInner@level_filter: $($field).+.level_filter); - $crate::sink::helper::common_impl!(@SinkBuilderCustomInner@formatter: $($field).+.formatter); - $crate::sink::helper::common_impl!(@SinkBuilderCustomInner@error_handler: $($field).+.error_handler); - }; - ( @SinkBuilderCustom { - level_filter: $($level_filter:ident).+, - formatter: $($formatter:ident).+, - error_handler: $($error_handler:ident).+$(,)? - } ) => { - $crate::sink::helper::common_impl!(@SinkBuilderCustomInner@level_filter: $($level_filter).+); - $crate::sink::helper::common_impl!(@SinkBuilderCustomInner@formatter: $($formatter).+); - $crate::sink::helper::common_impl!(@SinkBuilderCustomInner@error_handler: $($error_handler).+); - }; - ( @SinkBuilderCustomInner@level_filter: None ) => {}; - ( @SinkBuilderCustomInner@level_filter: $($field:ident).+ ) => { - $crate::sink::helper::common_impl! { - /// Specifies a log level filter. - /// - /// This parameter is **optional**. - @SinkBuilderCustomInner@level_filter: $($field).+ - } - }; - ( $(#[$attr:meta])* @SinkBuilderCustomInner@level_filter: $($field:ident).+ ) => { - $(#[$attr])* - #[must_use] - pub fn level_filter(mut self, level_filter: $crate::LevelFilter) -> Self { - self.$($field).+ = level_filter; - self - } - }; - ( @SinkBuilderCustomInner@formatter: None ) => {}; - ( @SinkBuilderCustomInner@formatter: $($field:ident).+ ) => { - $crate::sink::helper::common_impl! { - /// Specifies a formatter. - /// - /// This parameter is **optional**. - @SinkBuilderCustomInner@formatter: $($field).+ - } - }; - ( $(#[$attr:meta])* @SinkBuilderCustomInner@formatter: $($field:ident).+ ) => { - $(#[$attr])* - #[must_use] - pub fn formatter(mut self, formatter: Box) -> Self { - self.$($field).+ = Some(formatter); - self - } - }; - ( @SinkBuilderCustomInner@error_handler: None ) => {}; - ( @SinkBuilderCustomInner@error_handler: $($field:ident).+ ) => { - $crate::sink::helper::common_impl! { - /// Specifies an error handler. - /// - /// This parameter is **optional**. - @SinkBuilderCustomInner@error_handler: $($field).+ - } - }; - ( $(#[$attr:meta])* @SinkBuilderCustomInner@error_handler: $($field:ident).+ ) => { - $(#[$attr])* - #[must_use] - pub fn error_handler(mut self, handler: $crate::ErrorHandler) -> Self { - self.$($field).+ = Some(handler); - self - } - }; -} -pub(crate) use common_impl; diff --git a/spdlog/src/sink/journald_sink.rs b/spdlog/src/sink/journald_sink.rs index 13c21eb..74fd285 100644 --- a/spdlog/src/sink/journald_sink.rs +++ b/spdlog/src/sink/journald_sink.rs @@ -1,10 +1,9 @@ use std::{io, os::raw::c_int}; use crate::{ - formatter::{FormatterContext, JournaldFormatter}, - sink::{helper, Sink}, - sync::RwLockExtend as _, - Error, Level, Record, Result, StdResult, StringBuf, + formatter::{Formatter, FormatterContext, JournaldFormatter}, + sink::{GetSinkProp, Sink, SinkProp}, + Error, ErrorHandler, Level, LevelFilter, Record, Result, StdResult, StringBuf, }; #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] @@ -89,7 +88,7 @@ fn journal_send(args: impl Iterator>) -> StdResult<(), io /// pacman -S systemd /// ``` pub struct JournaldSink { - common_impl: helper::CommonImpl, + prop: SinkProp, } impl JournaldSink { @@ -109,9 +108,16 @@ impl JournaldSink { /// [default error handler]: error/index.html#default-error-handler #[must_use] pub fn builder() -> JournaldSinkBuilder { - JournaldSinkBuilder { - common_builder_impl: helper::CommonBuilderImpl::new(), - } + let prop = SinkProp::default(); + prop.set_formatter(Box::new(JournaldFormatter::new())); + + JournaldSinkBuilder { prop } + } +} + +impl GetSinkProp for JournaldSink { + fn prop(&self) -> &SinkProp { + &self.prop } } @@ -119,9 +125,8 @@ impl Sink for JournaldSink { fn log(&self, record: &Record) -> Result<()> { let mut string_buf = StringBuf::new(); let mut ctx = FormatterContext::new(); - self.common_impl - .formatter - .read_expect() + self.prop + .formatter() .format(record, &mut string_buf, &mut ctx)?; let kvs = [ @@ -146,26 +151,49 @@ impl Sink for JournaldSink { fn flush(&self) -> Result<()> { Ok(()) } - - helper::common_impl!(@Sink: common_impl); } #[allow(missing_docs)] pub struct JournaldSinkBuilder { - common_builder_impl: helper::CommonBuilderImpl, + prop: SinkProp, } impl JournaldSinkBuilder { - helper::common_impl!(@SinkBuilder: common_builder_impl); + // Prop + // + + /// Specifies a log level filter. + /// + /// This parameter is **optional**. + #[must_use] + pub fn level_filter(self, level_filter: LevelFilter) -> Self { + self.prop.set_level_filter(level_filter); + self + } + + /// Specifies a formatter. + /// + /// This parameter is **optional**. + #[must_use] + pub fn formatter(self, formatter: Box) -> Self { + self.prop.set_formatter(formatter); + self + } + + /// Specifies an error handler. + /// + /// This parameter is **optional**. + #[must_use] + pub fn error_handler(self, handler: ErrorHandler) -> Self { + self.prop.set_error_handler(Some(handler)); + self + } + + // /// Builds a [`JournaldSink`]. pub fn build(self) -> Result { - let sink = JournaldSink { - common_impl: helper::CommonImpl::from_builder_with_formatter( - self.common_builder_impl, - || Box::new(JournaldFormatter::new()), - ), - }; + let sink = JournaldSink { prop: self.prop }; Ok(sink) } } diff --git a/spdlog/src/sink/mod.rs b/spdlog/src/sink/mod.rs index 844eb1a..df06587 100644 --- a/spdlog/src/sink/mod.rs +++ b/spdlog/src/sink/mod.rs @@ -23,7 +23,6 @@ pub(crate) mod async_sink; mod dedup_sink; mod file_sink; -mod helper; #[cfg(any( all(target_os = "linux", feature = "native", feature = "libsystemd"), all(doc, not(doctest)) @@ -35,6 +34,8 @@ mod std_stream_sink; mod win_debug_sink; mod write_sink; +use std::ops::Deref; + #[cfg(feature = "multi-thread")] pub use async_sink::*; pub use dedup_sink::*; @@ -50,22 +51,126 @@ pub use std_stream_sink::*; pub use win_debug_sink::*; pub use write_sink::*; -use crate::{formatter::Formatter, sync::*, ErrorHandler, Level, LevelFilter, Record, Result}; +use crate::{ + formatter::{Formatter, FullFormatter}, + sync::*, + Error, ErrorHandler, Level, LevelFilter, Record, Result, +}; -/// Represents a sink -pub trait Sink: Sync + Send { - /// Determines if a log message with the specified level would be logged. +pub(crate) const SINK_DEFAULT_LEVEL_FILTER: LevelFilter = LevelFilter::All; + +pub(crate) type SinkErrorHandler = Atomic>; + +cfg_if::cfg_if! { + if #[cfg(test)] { + crate::utils::const_assert!(Atomic::::is_lock_free()); + } +} + +/// Contains definitions of sink properties. +/// +/// It provides a set of common properties for sink to define. If there is no +/// special need for properties, use it directly and then implement +/// [`GetSinkProp`] for your sink, a blanket implementation will be enabled, +/// which would eliminate a lot of boilerplate code. +/// +/// If further customization of the properties is needed (e.g., using different +/// types, changing behavior), this struct is not needed. Instead, define +/// properties manually within your sink, and then implement [`SinkAccess`]. +pub struct SinkProp { + level_filter: Atomic, + formatter: RwLockMappable>, + error_handler: SinkErrorHandler, +} + +impl Default for SinkProp { + fn default() -> Self { + Self { + level_filter: Atomic::new(SINK_DEFAULT_LEVEL_FILTER), + formatter: RwLockMappable::new(Box::new(FullFormatter::new())), + error_handler: Atomic::new(None), + } + } +} + +impl SinkProp { + /// Gets the log level filter. #[must_use] - fn should_log(&self, level: Level) -> bool { - self.level_filter().test(level) + pub fn level_filter(&self) -> LevelFilter { + self.level_filter.load(Ordering::Relaxed) } - /// Logs a record. - fn log(&self, record: &Record) -> Result<()>; + /// Sets the log level filter. + pub fn set_level_filter(&self, level_filter: LevelFilter) { + self.level_filter.store(level_filter, Ordering::Relaxed) + } - /// Flushes any buffered records. - fn flush(&self) -> Result<()>; + /// Gets the formatter. + /// + /// The returned value is a lock guard, so please avoid storing it in a + /// variable with a longer lifetime. + pub fn formatter<'a>(&'a self) -> impl Deref + 'a { + RwLockMappableReadGuard::map(self.formatter.read(), |f| &**f) + } + + /// Sets the formatter. + pub fn set_formatter(&self, formatter: Box) { + *self.formatter.write() = formatter; + } + + /// Gets the error handler. + pub fn error_handler(&self) -> Option { + self.error_handler.load(Ordering::Relaxed) + } + + /// Sets a error handler. + /// + /// Most errors that occur in `Sink` will be returned as directly as + /// possible (e.g. returned to [`Logger`]), but some errors that cannot be + /// returned immediately, this function will be called. For example, + /// asynchronous errors. + /// + /// If no handler is set, [default error handler] will be used. + /// + /// [`Logger`]: crate::logger::Logger + /// [default error handler]: ../error/index.html#default-error-handler + pub fn set_error_handler(&self, handler: Option) { + self.error_handler.store(handler, Ordering::Relaxed) + } + + pub(crate) fn non_returnable_error(&self, from: impl AsRef, err: Error) { + match self.error_handler.load(Ordering::Relaxed) { + Some(handler) => handler(err), + None => crate::default_error_handler(from, err), + } + } +} + +/// Represents the getter for the [`SinkProp`] inside a sink. +/// +/// This trait is not mandatory for a sink. It enables a blanket implementation, +/// where a sink that implements this trait will automatically get the +/// [`SinkAccess`] trait implemented, which eliminates a lot of boilerplate +/// code. +pub trait GetSinkProp { + /// Gets the [`SinkProp`] from a sink. + fn prop(&self) -> &SinkProp; +} +/// Represents getters for properties of a sink. +/// +/// The use of a sink requires these properties, and this trait describes the +/// methods for getting them. +/// +/// For the common case of custom sinks, users don't need to implement this +/// trait manually, they can just store a `SinkProp` in their sink struct and +/// implement trait [`GetSinkProp`], a blanket implementation will automatically +/// implement `SinkAccess` for the sink. +/// +/// For more details on implementing custom sink, see [. /examples] directory. +/// +/// [./examples]: https://github.com/SpriteOvO/spdlog-rs/tree/main/spdlog/examples +pub trait SinkAccess { /// Gets the log level filter. #[must_use] fn level_filter(&self) -> LevelFilter; @@ -90,5 +195,42 @@ pub trait Sink: Sync + Send { fn set_error_handler(&self, handler: Option); } +impl SinkAccess for S { + fn level_filter(&self) -> LevelFilter { + self.prop().level_filter() + } + + fn set_level_filter(&self, level_filter: LevelFilter) { + self.prop().set_level_filter(level_filter); + } + + fn set_formatter(&self, formatter: Box) { + self.prop().set_formatter(formatter); + } + + fn set_error_handler(&self, handler: Option) { + self.prop().set_error_handler(handler); + } +} + +/// Represents a sink +/// +/// See [./examples] directory for how to implement a custom sink. +/// +/// [./examples]: https://github.com/SpriteOvO/spdlog-rs/tree/main/spdlog/examples +pub trait Sink: SinkAccess + Sync + Send { + /// Determines if a log message with the specified level would be logged. + #[must_use] + fn should_log(&self, level: Level) -> bool { + self.level_filter().test(level) + } + + /// Logs a record. + fn log(&self, record: &Record) -> Result<()>; + + /// Flushes any buffered records. + fn flush(&self) -> Result<()>; +} + /// Container type for [`Sink`]s. pub type Sinks = Vec>; diff --git a/spdlog/src/sink/rotating_file_sink.rs b/spdlog/src/sink/rotating_file_sink.rs index d8c4f0f..f1af594 100644 --- a/spdlog/src/sink/rotating_file_sink.rs +++ b/spdlog/src/sink/rotating_file_sink.rs @@ -16,10 +16,10 @@ use chrono::prelude::*; use crate::{ error::InvalidArgumentError, - formatter::FormatterContext, - sink::{helper, Sink}, + formatter::{Formatter, FormatterContext}, + sink::{GetSinkProp, Sink, SinkProp}, sync::*, - utils, Error, Record, Result, StringBuf, + utils, Error, ErrorHandler, LevelFilter, Record, Result, StringBuf, }; /// Rotation policies for [`RotatingFileSink`]. @@ -147,14 +147,14 @@ struct RotatorTimePointInner { /// /// [./examples]: https://github.com/SpriteOvO/spdlog-rs/tree/main/spdlog/examples pub struct RotatingFileSink { - common_impl: helper::CommonImpl, + prop: SinkProp, rotator: RotatorKind, } /// # #[doc = include_str!("../include/doc/generic-builder-note.md")] pub struct RotatingFileSinkBuilder { - common_builder_impl: helper::CommonBuilderImpl, + prop: SinkProp, base_path: ArgBP, rotation_policy: ArgRP, max_files: usize, @@ -189,7 +189,7 @@ impl RotatingFileSink { #[must_use] pub fn builder() -> RotatingFileSinkBuilder<(), ()> { RotatingFileSinkBuilder { - common_builder_impl: helper::CommonBuilderImpl::new(), + prop: SinkProp::default(), base_path: (), rotation_policy: (), max_files: 0, @@ -252,13 +252,18 @@ impl RotatingFileSink { } } +impl GetSinkProp for RotatingFileSink { + fn prop(&self) -> &SinkProp { + &self.prop + } +} + impl Sink for RotatingFileSink { fn log(&self, record: &Record) -> Result<()> { let mut string_buf = StringBuf::new(); let mut ctx = FormatterContext::new(); - self.common_impl - .formatter - .read_expect() + self.prop + .formatter() .format(record, &mut string_buf, &mut ctx)?; self.rotator.log(record, &string_buf) @@ -267,15 +272,12 @@ impl Sink for RotatingFileSink { fn flush(&self) -> Result<()> { self.rotator.flush() } - - helper::common_impl!(@Sink: common_impl); } impl Drop for RotatingFileSink { fn drop(&mut self) { if let Err(err) = self.rotator.drop_flush() { - self.common_impl - .non_returnable_error("RotatingFileSink", err) + self.prop.non_returnable_error("RotatingFileSink", err) } } } @@ -732,7 +734,7 @@ impl RotatingFileSinkBuilder { P: Into, { RotatingFileSinkBuilder { - common_builder_impl: self.common_builder_impl, + prop: self.prop, base_path: base_path.into(), rotation_policy: self.rotation_policy, max_files: self.max_files, @@ -750,7 +752,7 @@ impl RotatingFileSinkBuilder { rotation_policy: RotationPolicy, ) -> RotatingFileSinkBuilder { RotatingFileSinkBuilder { - common_builder_impl: self.common_builder_impl, + prop: self.prop, base_path: self.base_path, rotation_policy, max_files: self.max_files, @@ -797,7 +799,35 @@ impl RotatingFileSinkBuilder { self } - helper::common_impl!(@SinkBuilder: common_builder_impl); + // Prop + // + + /// Specifies a log level filter. + /// + /// This parameter is **optional**. + #[must_use] + pub fn level_filter(self, level_filter: LevelFilter) -> Self { + self.prop.set_level_filter(level_filter); + self + } + + /// Specifies a formatter. + /// + /// This parameter is **optional**. + #[must_use] + pub fn formatter(self, formatter: Box) -> Self { + self.prop.set_formatter(formatter); + self + } + + /// Specifies an error handler. + /// + /// This parameter is **optional**. + #[must_use] + pub fn error_handler(self, handler: ErrorHandler) -> Self { + self.prop.set_error_handler(Some(handler)); + self + } } impl RotatingFileSinkBuilder<(), ArgRP> { @@ -872,7 +902,7 @@ impl RotatingFileSinkBuilder { }; let res = RotatingFileSink { - common_impl: helper::CommonImpl::from_builder(self.common_builder_impl), + prop: self.prop, rotator, }; diff --git a/spdlog/src/sink/std_stream_sink.rs b/spdlog/src/sink/std_stream_sink.rs index da42fab..cde8b44 100644 --- a/spdlog/src/sink/std_stream_sink.rs +++ b/spdlog/src/sink/std_stream_sink.rs @@ -8,11 +8,10 @@ use std::{ use if_chain::if_chain; use crate::{ - formatter::FormatterContext, - sink::{helper, Sink}, - sync::RwLockExtend as _, + formatter::{Formatter, FormatterContext}, + sink::{GetSinkProp, Sink, SinkProp}, terminal_style::{LevelStyles, Style, StyleMode}, - Error, Level, Record, Result, StringBuf, + Error, ErrorHandler, Level, LevelFilter, Record, Result, StringBuf, }; /// An enum representing the available standard streams. @@ -87,7 +86,7 @@ impl_write_for_dest!(StdStreamDest, io::StderrLock<'_>>); /// /// Note that this sink always flushes the buffer once with each logging. pub struct StdStreamSink { - common_impl: helper::CommonImpl, + prop: SinkProp, dest: StdStreamDest, should_render_style: bool, level_styles: LevelStyles, @@ -114,7 +113,7 @@ impl StdStreamSink { #[must_use] pub fn builder() -> StdStreamSinkBuilder<()> { StdStreamSinkBuilder { - common_builder_impl: helper::CommonBuilderImpl::new(), + prop: SinkProp::default(), std_stream: (), style_mode: StyleMode::Auto, } @@ -160,13 +159,18 @@ impl StdStreamSink { } } +impl GetSinkProp for StdStreamSink { + fn prop(&self) -> &SinkProp { + &self.prop + } +} + impl Sink for StdStreamSink { fn log(&self, record: &Record) -> Result<()> { let mut string_buf = StringBuf::new(); let mut ctx = FormatterContext::new(); - self.common_impl - .formatter - .read_expect() + self.prop + .formatter() .format(record, &mut string_buf, &mut ctx)?; let mut dest = self.dest.lock(); @@ -203,8 +207,6 @@ impl Sink for StdStreamSink { fn flush(&self) -> Result<()> { self.dest.lock().flush().map_err(Error::FlushBuffer) } - - helper::common_impl!(@Sink: common_impl); } // -------------------------------------------------- @@ -212,7 +214,7 @@ impl Sink for StdStreamSink { /// # #[doc = include_str!("../include/doc/generic-builder-note.md")] pub struct StdStreamSinkBuilder { - common_builder_impl: helper::CommonBuilderImpl, + prop: SinkProp, std_stream: ArgSS, style_mode: StyleMode, } @@ -240,7 +242,7 @@ impl StdStreamSinkBuilder { #[must_use] pub fn std_stream(self, std_stream: StdStream) -> StdStreamSinkBuilder { StdStreamSinkBuilder { - common_builder_impl: self.common_builder_impl, + prop: self.prop, std_stream, style_mode: self.style_mode, } @@ -255,7 +257,35 @@ impl StdStreamSinkBuilder { self } - helper::common_impl!(@SinkBuilder: common_builder_impl); + // Prop + // + + /// Specifies a log level filter. + /// + /// This parameter is **optional**. + #[must_use] + pub fn level_filter(self, level_filter: LevelFilter) -> Self { + self.prop.set_level_filter(level_filter); + self + } + + /// Specifies a formatter. + /// + /// This parameter is **optional**. + #[must_use] + pub fn formatter(self, formatter: Box) -> Self { + self.prop.set_formatter(formatter); + self + } + + /// Specifies an error handler. + /// + /// This parameter is **optional**. + #[must_use] + pub fn error_handler(self, handler: ErrorHandler) -> Self { + self.prop.set_error_handler(Some(handler)); + self + } } impl StdStreamSinkBuilder<()> { @@ -271,7 +301,7 @@ impl StdStreamSinkBuilder { /// Builds a [`StdStreamSink`]. pub fn build(self) -> Result { Ok(StdStreamSink { - common_impl: helper::CommonImpl::from_builder(self.common_builder_impl), + prop: self.prop, dest: StdStreamDest::new(self.std_stream), should_render_style: StdStreamSink::should_render_style( self.style_mode, diff --git a/spdlog/src/sink/win_debug_sink.rs b/spdlog/src/sink/win_debug_sink.rs index 61aceae..e26e634 100644 --- a/spdlog/src/sink/win_debug_sink.rs +++ b/spdlog/src/sink/win_debug_sink.rs @@ -1,15 +1,14 @@ use std::{ffi::OsStr, iter::once}; use crate::{ - formatter::FormatterContext, - sink::{helper, Sink}, - sync::RwLockExtend as _, - Record, Result, StringBuf, + formatter::{Formatter, FormatterContext}, + sink::{GetSinkProp, Sink, SinkProp}, + ErrorHandler, LevelFilter, Record, Result, StringBuf, }; /// A sink with a win32 API `OutputDebugStringW` as the target. pub struct WinDebugSink { - common_impl: helper::CommonImpl, + prop: SinkProp, } impl WinDebugSink { @@ -28,7 +27,7 @@ impl WinDebugSink { #[must_use] pub fn builder() -> WinDebugSinkBuilder { WinDebugSinkBuilder { - common_builder_impl: helper::CommonBuilderImpl::new(), + prop: SinkProp::default(), } } @@ -44,6 +43,12 @@ impl WinDebugSink { } } +impl GetSinkProp for WinDebugSink { + fn prop(&self) -> &SinkProp { + &self.prop + } +} + impl Sink for WinDebugSink { fn log(&self, record: &Record) -> Result<()> { #[cfg(windows)] // https://github.com/rust-lang/rust/issues/97976 @@ -51,9 +56,8 @@ impl Sink for WinDebugSink { let mut string_buf = StringBuf::new(); let mut ctx = FormatterContext::new(); - self.common_impl - .formatter - .read_expect() + self.prop + .formatter() .format(record, &mut string_buf, &mut ctx)?; let wide: Vec = OsStr::new(&string_buf) @@ -70,23 +74,49 @@ impl Sink for WinDebugSink { fn flush(&self) -> Result<()> { Ok(()) } - - helper::common_impl!(@Sink: common_impl); } #[allow(missing_docs)] pub struct WinDebugSinkBuilder { - common_builder_impl: helper::CommonBuilderImpl, + prop: SinkProp, } impl WinDebugSinkBuilder { - helper::common_impl!(@SinkBuilder: common_builder_impl); + // Prop + // + + /// Specifies a log level filter. + /// + /// This parameter is **optional**. + #[must_use] + pub fn level_filter(self, level_filter: LevelFilter) -> Self { + self.prop.set_level_filter(level_filter); + self + } + + /// Specifies a formatter. + /// + /// This parameter is **optional**. + #[must_use] + pub fn formatter(self, formatter: Box) -> Self { + self.prop.set_formatter(formatter); + self + } + + /// Specifies an error handler. + /// + /// This parameter is **optional**. + #[must_use] + pub fn error_handler(self, handler: ErrorHandler) -> Self { + self.prop.set_error_handler(Some(handler)); + self + } + + // /// Builds a [`WinDebugSink`]. pub fn build(self) -> Result { - let sink = WinDebugSink { - common_impl: helper::CommonImpl::from_builder(self.common_builder_impl), - }; + let sink = WinDebugSink { prop: self.prop }; Ok(sink) } } diff --git a/spdlog/src/sink/write_sink.rs b/spdlog/src/sink/write_sink.rs index dc15096..5bbff2f 100644 --- a/spdlog/src/sink/write_sink.rs +++ b/spdlog/src/sink/write_sink.rs @@ -1,10 +1,10 @@ use std::{convert::Infallible, io::Write, marker::PhantomData}; use crate::{ - formatter::FormatterContext, - sink::{helper, Sink}, + formatter::{Formatter, FormatterContext}, + sink::{GetSinkProp, Sink, SinkProp}, sync::*, - Error, Record, Result, StringBuf, + Error, ErrorHandler, LevelFilter, Record, Result, StringBuf, }; /// A sink that writes log messages into an arbitrary `impl Write` object. @@ -29,7 +29,7 @@ pub struct WriteSink where W: Write + Send, { - common_impl: helper::CommonImpl, + prop: SinkProp, target: Mutex, } @@ -55,7 +55,7 @@ where #[must_use] pub fn builder() -> WriteSinkBuilder { WriteSinkBuilder { - common_builder_impl: helper::CommonBuilderImpl::new(), + prop: SinkProp::default(), target: None, _phantom: PhantomData, } @@ -95,6 +95,15 @@ where } } +impl GetSinkProp for WriteSink +where + W: Write + Send, +{ + fn prop(&self) -> &SinkProp { + &self.prop + } +} + impl Sink for WriteSink where W: Write + Send, @@ -102,9 +111,8 @@ where fn log(&self, record: &Record) -> Result<()> { let mut string_buf = StringBuf::new(); let mut ctx = FormatterContext::new(); - self.common_impl - .formatter - .read_expect() + self.prop + .formatter() .format(record, &mut string_buf, &mut ctx)?; self.lock_target() @@ -117,8 +125,6 @@ where fn flush(&self) -> Result<()> { self.lock_target().flush().map_err(Error::FlushBuffer) } - - helper::common_impl!(@Sink: common_impl); } impl Drop for WriteSink @@ -128,7 +134,7 @@ where fn drop(&mut self) { let flush_result = self.lock_target().flush().map_err(Error::FlushBuffer); if let Err(err) = flush_result { - self.common_impl.non_returnable_error("WriteSink", err) + self.prop.non_returnable_error("WriteSink", err) } } } @@ -136,7 +142,7 @@ where /// # #[doc = include_str!("../include/doc/generic-builder-note.md")] pub struct WriteSinkBuilder { - common_builder_impl: helper::CommonBuilderImpl, + prop: SinkProp, target: Option, _phantom: PhantomData, } @@ -152,13 +158,41 @@ where #[must_use] pub fn target(self, target: W) -> WriteSinkBuilder> { WriteSinkBuilder { - common_builder_impl: self.common_builder_impl, + prop: self.prop, target: Some(target), _phantom: PhantomData, } } - helper::common_impl!(@SinkBuilder: common_builder_impl); + // Prop + // + + /// Specifies a log level filter. + /// + /// This parameter is **optional**. + #[must_use] + pub fn level_filter(self, level_filter: LevelFilter) -> Self { + self.prop.set_level_filter(level_filter); + self + } + + /// Specifies a formatter. + /// + /// This parameter is **optional**. + #[must_use] + pub fn formatter(self, formatter: Box) -> Self { + self.prop.set_formatter(formatter); + self + } + + /// Specifies an error handler. + /// + /// This parameter is **optional**. + #[must_use] + pub fn error_handler(self, handler: ErrorHandler) -> Self { + self.prop.set_error_handler(Some(handler)); + self + } } impl WriteSinkBuilder @@ -180,7 +214,7 @@ where /// Builds a [`WriteSink`]. pub fn build(self) -> Result> { let sink = WriteSink { - common_impl: helper::CommonImpl::from_builder(self.common_builder_impl), + prop: self.prop, target: Mutex::new(self.target.unwrap()), }; Ok(sink) diff --git a/spdlog/src/sync.rs b/spdlog/src/sync.rs index f807c4c..610786e 100644 --- a/spdlog/src/sync.rs +++ b/spdlog/src/sync.rs @@ -6,6 +6,9 @@ pub use std::sync::{ pub use arc_swap::{ArcSwap, ArcSwapOption}; pub use once_cell::sync::{Lazy, OnceCell}; +// Mapping in std locks are not yet stablized, so we use parking_lot's ones here. +// https://github.com/rust-lang/rust/issues/117108 +pub use parking_lot::{RwLock as RwLockMappable, RwLockReadGuard as RwLockMappableReadGuard}; pub mod atomic { pub use std::sync::atomic::*; diff --git a/spdlog/src/test_utils/common.rs b/spdlog/src/test_utils/common.rs index e9478f7..84c8ab2 100644 --- a/spdlog/src/test_utils/common.rs +++ b/spdlog/src/test_utils/common.rs @@ -14,18 +14,16 @@ use std::{ time::Duration, }; -use atomic::Atomic; use spdlog::{ formatter::{Formatter, FormatterContext, Pattern, PatternFormatter}, - sink::{Sink, WriteSink, WriteSinkBuilder}, - Error, ErrorHandler, LevelFilter, Logger, LoggerBuilder, Record, RecordOwned, Result, - StringBuf, + sink::{GetSinkProp, Sink, SinkProp, WriteSink, WriteSinkBuilder}, + Error, Logger, LoggerBuilder, Record, RecordOwned, Result, StringBuf, }; ////////////////////////////////////////////////// pub struct TestSink { - level_filter: Atomic, + prop: SinkProp, log_counter: AtomicUsize, flush_counter: AtomicUsize, records: Mutex>, @@ -41,7 +39,7 @@ impl TestSink { #[must_use] pub fn with_delay(duration: Option) -> Self { Self { - level_filter: Atomic::new(LevelFilter::All), + prop: SinkProp::default(), log_counter: AtomicUsize::new(0), flush_counter: AtomicUsize::new(0), records: Mutex::new(vec![]), @@ -85,6 +83,12 @@ impl TestSink { } } +impl GetSinkProp for TestSink { + fn prop(&self) -> &SinkProp { + &self.prop + } +} + impl Sink for TestSink { fn log(&self, record: &Record) -> Result<()> { if let Some(delay) = self.delay_duration { @@ -105,22 +109,6 @@ impl Sink for TestSink { self.flush_counter.fetch_add(1, Ordering::Relaxed); Ok(()) } - - fn level_filter(&self) -> LevelFilter { - self.level_filter.load(Ordering::Relaxed) - } - - fn set_level_filter(&self, level_filter: LevelFilter) { - self.level_filter.store(level_filter, Ordering::Relaxed); - } - - fn set_formatter(&self, _formatter: Box) { - unimplemented!("no-op") - } - - fn set_error_handler(&self, _handler: Option) { - unimplemented!("no-op") - } } impl Default for TestSink { @@ -157,6 +145,12 @@ impl StringSink { } } +impl GetSinkProp for StringSink { + fn prop(&self) -> &SinkProp { + self.underlying.prop() + } +} + impl Sink for StringSink { fn log(&self, record: &Record) -> Result<()> { self.underlying.log(record) @@ -165,22 +159,6 @@ impl Sink for StringSink { fn flush(&self) -> Result<()> { self.underlying.flush() } - - fn level_filter(&self) -> LevelFilter { - self.underlying.level_filter() - } - - fn set_level_filter(&self, level_filter: LevelFilter) { - self.underlying.set_level_filter(level_filter) - } - - fn set_formatter(&self, formatter: Box) { - self.underlying.set_formatter(formatter) - } - - fn set_error_handler(&self, handler: Option) { - self.underlying.set_error_handler(handler) - } } ////////////////////////////////////////////////// diff --git a/spdlog/tests/global_async_pool_sink.rs b/spdlog/tests/global_async_pool_sink.rs index e430ac0..1e26f95 100644 --- a/spdlog/tests/global_async_pool_sink.rs +++ b/spdlog/tests/global_async_pool_sink.rs @@ -10,16 +10,23 @@ use std::{ }; use spdlog::{ - formatter::Formatter, prelude::*, - sink::{AsyncPoolSink, Sink}, - ErrorHandler, + sink::{AsyncPoolSink, GetSinkProp, Sink, SinkProp}, }; static IS_LOGGED: AtomicBool = AtomicBool::new(false); static IS_FLUSHED: AtomicBool = AtomicBool::new(false); -struct SetFlagSink; +#[derive(Default)] +struct SetFlagSink { + prop: SinkProp, +} + +impl GetSinkProp for SetFlagSink { + fn prop(&self) -> &SinkProp { + &self.prop + } +} impl Sink for SetFlagSink { fn log(&self, _record: &spdlog::Record) -> error::Result<()> { @@ -33,22 +40,6 @@ impl Sink for SetFlagSink { IS_FLUSHED.store(true, Ordering::SeqCst); Ok(()) } - - fn level_filter(&self) -> LevelFilter { - LevelFilter::All - } - - fn set_level_filter(&self, _level_filter: LevelFilter) { - unimplemented!() - } - - fn set_formatter(&self, _formatter: Box) { - unimplemented!() - } - - fn set_error_handler(&self, _handler: Option) { - unimplemented!() - } } fn run_test() { @@ -66,7 +57,7 @@ fn run_test() { let async_pool_sink = Arc::new( AsyncPoolSink::builder() - .sink(Arc::new(SetFlagSink)) + .sink(Arc::new(SetFlagSink::default())) .build() .unwrap(), ); diff --git a/spdlog/tests/pattern.rs b/spdlog/tests/pattern.rs index c4064f3..1087a53 100644 --- a/spdlog/tests/pattern.rs +++ b/spdlog/tests/pattern.rs @@ -10,9 +10,9 @@ use regex::Regex; use spdlog::formatter::runtime_pattern; use spdlog::{ error, - formatter::{pattern, Formatter, FormatterContext, Pattern, PatternFormatter}, + formatter::{pattern, FormatterContext, Pattern, PatternFormatter}, prelude::*, - sink::Sink, + sink::{GetSinkProp, Sink, SinkProp}, Error, StringBuf, __EOL, }; @@ -115,7 +115,7 @@ where } struct MockSink { - formatter: Mutex>>, + prop: SinkProp, last_msg: Mutex>)>>, } @@ -123,7 +123,7 @@ impl MockSink { #[must_use] fn new() -> Self { Self { - formatter: Mutex::new(None), + prop: SinkProp::default(), last_msg: Mutex::new(None), } } @@ -134,13 +134,18 @@ impl MockSink { } } +impl GetSinkProp for MockSink { + fn prop(&self) -> &SinkProp { + &self.prop + } +} + impl Sink for MockSink { fn log(&self, record: &spdlog::Record) -> spdlog::Result<()> { let mut buf = StringBuf::new(); - let fmt = self.formatter.lock().unwrap(); let mut ctx = FormatterContext::new(); - fmt.as_ref() - .unwrap() + self.prop + .formatter() .format(record, &mut buf, &mut ctx) .unwrap(); *self.last_msg.lock().unwrap() = Some((String::from(buf.as_str()), ctx.style_range())); @@ -150,22 +155,6 @@ impl Sink for MockSink { fn flush(&self) -> spdlog::Result<()> { Ok(()) } - - fn level_filter(&self) -> spdlog::LevelFilter { - spdlog::LevelFilter::All - } - - fn set_level_filter(&self, _level_filter: spdlog::LevelFilter) {} - - fn set_formatter(&self, formatter: Box) { - *self.formatter.lock().unwrap() = Some(formatter); - } - - fn set_error_handler(&self, _handler: Option) {} - - fn should_log(&self, _level: spdlog::Level) -> bool { - true - } } #[derive(Default, Clone)] From 10de4268c2a8a0e3bda26336d9387521d07b998c Mon Sep 17 00:00:00 2001 From: Asuna Date: Fri, 29 Aug 2025 16:07:04 +0800 Subject: [PATCH 2/3] Receive formatter parameter as generic if dyn-compatible is not required --- spdlog/benches/spdlog-rs/pattern.rs | 6 ++--- spdlog/src/formatter/pattern_formatter/mod.rs | 25 ++++--------------- .../formatter/pattern_formatter/runtime.rs | 10 ++------ spdlog/src/sink/async_sink/async_pool_sink.rs | 7 ++++-- spdlog/src/sink/dedup_sink.rs | 7 ++++-- spdlog/src/sink/file_sink.rs | 5 +++- spdlog/src/sink/journald_sink.rs | 7 ++++-- spdlog/src/sink/mod.rs | 12 +++++++-- spdlog/src/sink/rotating_file_sink.rs | 5 +++- spdlog/src/sink/std_stream_sink.rs | 5 +++- spdlog/src/sink/win_debug_sink.rs | 5 +++- spdlog/src/sink/write_sink.rs | 5 +++- spdlog/src/test_utils/common.rs | 4 +-- spdlog/tests/log_crate_proxy.rs | 8 +++--- spdlog/tests/pattern.rs | 4 +-- 15 files changed, 61 insertions(+), 54 deletions(-) diff --git a/spdlog/benches/spdlog-rs/pattern.rs b/spdlog/benches/spdlog-rs/pattern.rs index 3c156c1..13d80d5 100644 --- a/spdlog/benches/spdlog-rs/pattern.rs +++ b/spdlog/benches/spdlog-rs/pattern.rs @@ -88,12 +88,10 @@ fn bench_pattern(bencher: &mut Bencher, pattern: impl Pattern + Clone + 'static) } fn bench_full_pattern(bencher: &mut Bencher, pattern: impl Pattern + Clone + 'static) { - let full_formatter = Arc::new(StringSink::with(|b| { - b.formatter(Box::new(FullFormatter::new())) - })); + let full_formatter = Arc::new(StringSink::with(|b| b.formatter(FullFormatter::new()))); let full_pattern = Arc::new(StringSink::with(|b| { - b.formatter(Box::new(PatternFormatter::new(pattern.clone()))) + b.formatter(PatternFormatter::new(pattern.clone())) })); let combination = diff --git a/spdlog/src/formatter/pattern_formatter/mod.rs b/spdlog/src/formatter/pattern_formatter/mod.rs index 849c448..5abe30e 100644 --- a/spdlog/src/formatter/pattern_formatter/mod.rs +++ b/spdlog/src/formatter/pattern_formatter/mod.rs @@ -73,10 +73,7 @@ use crate::{ #[doc = include_str!(concat!(env!("OUT_DIR"), "/test_utils/common_for_doc_test.rs"))] /// /// let formatter = PatternFormatter::new(pattern!("[{level}] {payload}{eol}")); -/// # let (doctest, sink) = test_utils::echo_logger_from_formatter( -/// # Box::new(formatter), -/// # None -/// # ); +/// # let (doctest, sink) = test_utils::echo_logger_from_formatter(formatter, None); /// /// info!(logger: doctest, "Interesting log message"); /// # assert_eq!( @@ -98,10 +95,7 @@ use crate::{ /// # }; #[doc = include_str!(concat!(env!("OUT_DIR"), "/test_utils/common_for_doc_test.rs"))] /// let formatter = PatternFormatter::new(pattern!("[{{escaped}}] {payload}{eol}")); -/// # let (doctest, sink) = test_utils::echo_logger_from_formatter( -/// # Box::new(formatter), -/// # None -/// # ); +/// # let (doctest, sink) = test_utils::echo_logger_from_formatter(formatter, None); /// /// info!(logger: doctest, "Interesting log message"); /// # assert_eq!( @@ -127,10 +121,7 @@ use crate::{ /// # }; #[doc = include_str!(concat!(env!("OUT_DIR"), "/test_utils/common_for_doc_test.rs"))] /// let formatter = PatternFormatter::new(pattern!("{^[{level}]} {payload}{eol}")); -/// # let (doctest, sink) = test_utils::echo_logger_from_formatter( -/// # Box::new(formatter), -/// # None -/// # ); +/// # let (doctest, sink) = test_utils::echo_logger_from_formatter(formatter, None); /// /// info!(logger: doctest, "Interesting log message"); /// # assert_eq!( @@ -169,10 +160,7 @@ use crate::{ /// {$mypat} => MyPattern::default, /// ); /// let formatter = PatternFormatter::new(pat); -/// # let (doctest, sink) = test_utils::echo_logger_from_formatter( -/// # Box::new(formatter), -/// # None -/// # ); +/// # let (doctest, sink) = test_utils::echo_logger_from_formatter(formatter, None); /// /// info!(logger: doctest, "Interesting log message"); /// # assert_eq!( @@ -233,10 +221,7 @@ use crate::{ /// {$mypat} => MyPattern::new, /// ); /// let formatter = PatternFormatter::new(pat); -/// # let (doctest, sink) = test_utils::echo_logger_from_formatter( -/// # Box::new(formatter), -/// # None -/// # ); +/// # let (doctest, sink) = test_utils::echo_logger_from_formatter(formatter, None); /// /// info!(logger: doctest, "Interesting log message"); /// # assert_eq!( diff --git a/spdlog/src/formatter/pattern_formatter/runtime.rs b/spdlog/src/formatter/pattern_formatter/runtime.rs index 8de9711..6db578b 100644 --- a/spdlog/src/formatter/pattern_formatter/runtime.rs +++ b/spdlog/src/formatter/pattern_formatter/runtime.rs @@ -70,10 +70,7 @@ pub use spdlog_macros::runtime_pattern; #[doc = include_str!(concat!(env!("OUT_DIR"), "/test_utils/common_for_doc_test.rs"))] /// # fn main() -> Result<(), Box> { /// let formatter = PatternFormatter::new(runtime_pattern!("[{level}] {payload}{eol}")?); -/// # let (doctest, sink) = test_utils::echo_logger_from_formatter( -/// # Box::new(formatter), -/// # None -/// # ); +/// # let (doctest, sink) = test_utils::echo_logger_from_formatter(formatter, None); /// /// info!(logger: doctest, "Interesting log message"); /// # assert_eq!( @@ -114,10 +111,7 @@ pub use spdlog_macros::runtime_pattern; /// {$mypat2} => pat /// )? /// ); -/// # let (doctest, sink) = test_utils::echo_logger_from_formatter( -/// # Box::new(formatter), -/// # None -/// # ); +/// # let (doctest, sink) = test_utils::echo_logger_from_formatter(formatter, None); /// /// info!(logger: doctest, "Interesting log message"); /// # assert_eq!( diff --git a/spdlog/src/sink/async_sink/async_pool_sink.rs b/spdlog/src/sink/async_sink/async_pool_sink.rs index 0345c1f..d630f4b 100644 --- a/spdlog/src/sink/async_sink/async_pool_sink.rs +++ b/spdlog/src/sink/async_sink/async_pool_sink.rs @@ -72,7 +72,7 @@ impl AsyncPoolSink { // AsyncPoolSink does not have its own formatter, and we do not impl // `GetSinkProp` for it, so there should be no way to access the // formatter inside the `prop`. - prop.set_formatter(Box::new(UnreachableFormatter::new())); + prop.set_formatter(UnreachableFormatter::new()); AsyncPoolSinkBuilder { prop, @@ -210,7 +210,10 @@ impl AsyncPoolSinkBuilder { /// /// This parameter is **optional**. #[must_use] - pub fn formatter(self, formatter: Box) -> Self { + pub fn formatter(self, formatter: F) -> Self + where + F: Formatter + 'static, + { self.prop.set_formatter(formatter); self } diff --git a/spdlog/src/sink/dedup_sink.rs b/spdlog/src/sink/dedup_sink.rs index eed721e..0ec2560 100644 --- a/spdlog/src/sink/dedup_sink.rs +++ b/spdlog/src/sink/dedup_sink.rs @@ -38,7 +38,7 @@ struct DedupSinkState { /// # fn main() -> Result<(), spdlog::Error> { /// # let underlying_sink = Arc::new( /// # WriteSink::builder() -/// # .formatter(Box::new(PatternFormatter::new(pattern!("{payload}\n")))) +/// # .formatter(PatternFormatter::new(pattern!("{payload}\n"))) /// # .target(Vec::new()) /// # .build()? /// # ); @@ -254,7 +254,10 @@ impl DedupSinkBuilder { /// /// This parameter is **optional**. #[must_use] - pub fn formatter(self, formatter: Box) -> Self { + pub fn formatter(self, formatter: F) -> Self + where + F: Formatter + 'static, + { self.prop.set_formatter(formatter); self } diff --git a/spdlog/src/sink/file_sink.rs b/spdlog/src/sink/file_sink.rs index e594d00..cedf48a 100644 --- a/spdlog/src/sink/file_sink.rs +++ b/spdlog/src/sink/file_sink.rs @@ -187,7 +187,10 @@ impl FileSinkBuilder { /// /// This parameter is **optional**. #[must_use] - pub fn formatter(self, formatter: Box) -> Self { + pub fn formatter(self, formatter: F) -> Self + where + F: Formatter + 'static, + { self.prop.set_formatter(formatter); self } diff --git a/spdlog/src/sink/journald_sink.rs b/spdlog/src/sink/journald_sink.rs index 74fd285..747b49e 100644 --- a/spdlog/src/sink/journald_sink.rs +++ b/spdlog/src/sink/journald_sink.rs @@ -109,7 +109,7 @@ impl JournaldSink { #[must_use] pub fn builder() -> JournaldSinkBuilder { let prop = SinkProp::default(); - prop.set_formatter(Box::new(JournaldFormatter::new())); + prop.set_formatter(JournaldFormatter::new()); JournaldSinkBuilder { prop } } @@ -175,7 +175,10 @@ impl JournaldSinkBuilder { /// /// This parameter is **optional**. #[must_use] - pub fn formatter(self, formatter: Box) -> Self { + pub fn formatter(self, formatter: F) -> Self + where + F: Formatter + 'static, + { self.prop.set_formatter(formatter); self } diff --git a/spdlog/src/sink/mod.rs b/spdlog/src/sink/mod.rs index df06587..b9269e0 100644 --- a/spdlog/src/sink/mod.rs +++ b/spdlog/src/sink/mod.rs @@ -114,7 +114,15 @@ impl SinkProp { } /// Sets the formatter. - pub fn set_formatter(&self, formatter: Box) { + pub fn set_formatter(&self, formatter: F) + where + F: Formatter + 'static, + { + self.set_formatter_boxed(Box::new(formatter)); + } + + /// Sets the boxed formatter. + pub fn set_formatter_boxed(&self, formatter: Box) { *self.formatter.write() = formatter; } @@ -205,7 +213,7 @@ impl SinkAccess for S { } fn set_formatter(&self, formatter: Box) { - self.prop().set_formatter(formatter); + self.prop().set_formatter_boxed(formatter); } fn set_error_handler(&self, handler: Option) { diff --git a/spdlog/src/sink/rotating_file_sink.rs b/spdlog/src/sink/rotating_file_sink.rs index f1af594..eb79319 100644 --- a/spdlog/src/sink/rotating_file_sink.rs +++ b/spdlog/src/sink/rotating_file_sink.rs @@ -815,7 +815,10 @@ impl RotatingFileSinkBuilder { /// /// This parameter is **optional**. #[must_use] - pub fn formatter(self, formatter: Box) -> Self { + pub fn formatter(self, formatter: F) -> Self + where + F: Formatter + 'static, + { self.prop.set_formatter(formatter); self } diff --git a/spdlog/src/sink/std_stream_sink.rs b/spdlog/src/sink/std_stream_sink.rs index cde8b44..60cf395 100644 --- a/spdlog/src/sink/std_stream_sink.rs +++ b/spdlog/src/sink/std_stream_sink.rs @@ -273,7 +273,10 @@ impl StdStreamSinkBuilder { /// /// This parameter is **optional**. #[must_use] - pub fn formatter(self, formatter: Box) -> Self { + pub fn formatter(self, formatter: F) -> Self + where + F: Formatter + 'static, + { self.prop.set_formatter(formatter); self } diff --git a/spdlog/src/sink/win_debug_sink.rs b/spdlog/src/sink/win_debug_sink.rs index e26e634..81946b6 100644 --- a/spdlog/src/sink/win_debug_sink.rs +++ b/spdlog/src/sink/win_debug_sink.rs @@ -98,7 +98,10 @@ impl WinDebugSinkBuilder { /// /// This parameter is **optional**. #[must_use] - pub fn formatter(self, formatter: Box) -> Self { + pub fn formatter(self, formatter: F) -> Self + where + F: Formatter + 'static, + { self.prop.set_formatter(formatter); self } diff --git a/spdlog/src/sink/write_sink.rs b/spdlog/src/sink/write_sink.rs index 5bbff2f..0410ad1 100644 --- a/spdlog/src/sink/write_sink.rs +++ b/spdlog/src/sink/write_sink.rs @@ -180,7 +180,10 @@ where /// /// This parameter is **optional**. #[must_use] - pub fn formatter(self, formatter: Box) -> Self { + pub fn formatter(self, formatter: F) -> Self + where + F: Formatter + 'static, + { self.prop.set_formatter(formatter); self } diff --git a/spdlog/src/test_utils/common.rs b/spdlog/src/test_utils/common.rs index 84c8ab2..8583093 100644 --- a/spdlog/src/test_utils/common.rs +++ b/spdlog/src/test_utils/common.rs @@ -218,12 +218,12 @@ pub fn echo_logger_from_pattern( pattern: impl Pattern + Clone + 'static, name: Option<&'static str>, ) -> (Logger, Arc) { - echo_logger_from_formatter(Box::new(PatternFormatter::new(pattern)), name) + echo_logger_from_formatter(PatternFormatter::new(pattern), name) } #[must_use] pub fn echo_logger_from_formatter( - formatter: Box, + formatter: impl Formatter + 'static, name: Option<&'static str>, ) -> (Logger, Arc) { let sink = Arc::new(StringSink::with(|b| b.formatter(formatter))); diff --git a/spdlog/tests/log_crate_proxy.rs b/spdlog/tests/log_crate_proxy.rs index cac89f8..33f84af 100644 --- a/spdlog/tests/log_crate_proxy.rs +++ b/spdlog/tests/log_crate_proxy.rs @@ -16,9 +16,7 @@ static GLOBAL_LOG_CRATE_PROXY_MUTEX: Mutex<()> = Mutex::new(()); #[cfg(feature = "log")] #[test] fn test_source_location() { - let formatter = Box::new(PatternFormatter::new(pattern!( - "({module_path}::{file_name}) {payload}{eol}" - ))); + let formatter = PatternFormatter::new(pattern!("({module_path}::{file_name}) {payload}{eol}")); let sink = Arc::new(StringSink::with(|b| b.formatter(formatter))); let logger = Arc::new(build_test_logger(|b| b.sink(sink.clone()))); @@ -37,7 +35,7 @@ fn test_source_location() { #[cfg(feature = "log")] #[test] fn test_target() { - let formatter = Box::new(PatternFormatter::new(pattern!("[{logger}] {payload}{eol}"))); + let formatter = PatternFormatter::new(pattern!("[{logger}] {payload}{eol}")); let sink = Arc::new(StringSink::with(|b| b.formatter(formatter))); let logger = Arc::new(build_test_logger(|b| b.sink(sink.clone()))); @@ -53,7 +51,7 @@ fn test_target() { #[cfg(feature = "log")] #[test] fn test_kv() { - let formatter = Box::new(PatternFormatter::new(pattern!("{payload} {{ {kv} }}{eol}"))); + let formatter = PatternFormatter::new(pattern!("{payload} {{ {kv} }}{eol}")); let sink = Arc::new(StringSink::with(|b| b.formatter(formatter))); let logger = Arc::new(build_test_logger(|b| b.sink(sink.clone()))); diff --git a/spdlog/tests/pattern.rs b/spdlog/tests/pattern.rs index 1087a53..1be9a72 100644 --- a/spdlog/tests/pattern.rs +++ b/spdlog/tests/pattern.rs @@ -192,7 +192,7 @@ fn test_builtin_patterns() { #[track_caller] fn fmt(pattern: impl Pattern + Clone + 'static) -> String { let sink = Arc::new(test_utils::StringSink::with(|b| { - b.formatter(Box::new(PatternFormatter::new(pattern))) + b.formatter(PatternFormatter::new(pattern)) })); let logger = Logger::builder() @@ -518,7 +518,7 @@ fn test_different_context_thread() { use spdlog::{sink::AsyncPoolSink, ThreadPool}; - let formatter = Box::new(PatternFormatter::new(pattern!("{tid}{eol}"))); + let formatter = PatternFormatter::new(pattern!("{tid}{eol}")); let thread_pool = Arc::new(ThreadPool::builder().build().unwrap()); let buffer_sink = Arc::new(test_utils::StringSink::with(|b| b.formatter(formatter))); let sinks: [Arc; 2] = [ From 346128476e663c4ae8934a98f7d10774f9487a82 Mon Sep 17 00:00:00 2001 From: Asuna Date: Tue, 16 Sep 2025 14:12:14 +0800 Subject: [PATCH 3/3] Bump MSRV to v1.63 --- .github/workflows/ci.yml | 2 +- README.md | 2 +- spdlog-internal/Cargo.toml | 2 +- spdlog-macros/Cargo.toml | 2 +- spdlog-macros/src/normalize_forward.rs | 2 +- spdlog/Cargo.toml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c879840..0f4b472 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ env: # - src/lib.rs # - Cargo.toml # - README.md - rust_minver: 1.61.0 + rust_minver: 1.63.0 defaults: run: diff --git a/README.md b/README.md index 28a5885..58b515b 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ When updating this, also update: - Cargo.toml --> -The current minimum supported Rust version is 1.61. +The current minimum supported Rust version is 1.63. `spdlog-rs` is built against the latest Rust stable release, it is not guaranteed to build on Rust versions earlier than the minimum supported version. diff --git a/spdlog-internal/Cargo.toml b/spdlog-internal/Cargo.toml index 338f5b5..c751966 100644 --- a/spdlog-internal/Cargo.toml +++ b/spdlog-internal/Cargo.toml @@ -2,7 +2,7 @@ name = "spdlog-internal" version = "0.1.0" edition = "2021" -rust-version = "1.61" +rust-version = "1.63" description = "Internal private common code for crate \"spdlog-rs\"" repository = "https://github.com/SpriteOvO/spdlog-rs" license = "MIT OR Apache-2.0" diff --git a/spdlog-macros/Cargo.toml b/spdlog-macros/Cargo.toml index 59a00f6..7cda518 100644 --- a/spdlog-macros/Cargo.toml +++ b/spdlog-macros/Cargo.toml @@ -2,7 +2,7 @@ name = "spdlog-macros" version = "0.2.0" edition = "2021" -rust-version = "1.61" +rust-version = "1.63" description = "Macros implementation of crate \"spdlog-rs\"" repository = "https://github.com/SpriteOvO/spdlog-rs" license = "MIT OR Apache-2.0" diff --git a/spdlog-macros/src/normalize_forward.rs b/spdlog-macros/src/normalize_forward.rs index 981f167..84152c6 100644 --- a/spdlog-macros/src/normalize_forward.rs +++ b/spdlog-macros/src/normalize_forward.rs @@ -187,7 +187,7 @@ pub fn normalize(normalize: Normalize) -> syn::Result { .find_map(|allowed| { allowed .as_optional_mut() - .and_then(|allowed| (allowed.name == input_arg.name).then(|| allowed)) + .and_then(|allowed| (allowed.name == input_arg.name).then_some(allowed)) }) .ok_or_else(|| { syn::Error::new( diff --git a/spdlog/Cargo.toml b/spdlog/Cargo.toml index 181d623..026a5d4 100644 --- a/spdlog/Cargo.toml +++ b/spdlog/Cargo.toml @@ -2,7 +2,7 @@ name = "spdlog-rs" version = "0.4.3" edition = "2021" -rust-version = "1.61" +rust-version = "1.63" description = "Fast, highly configurable Rust logging crate, inspired by the C++ logging library spdlog" repository = "https://github.com/SpriteOvO/spdlog-rs" license = "MIT OR Apache-2.0"