diff --git a/spdlog-macros/src/lib.rs b/spdlog-macros/src/lib.rs index cc1eec0d..c9d60fd6 100644 --- a/spdlog-macros/src/lib.rs +++ b/spdlog-macros/src/lib.rs @@ -26,6 +26,13 @@ pub fn runtime_pattern(input: TokenStream) -> TokenStream { into_or_error(pattern::runtime_pattern_impl(runtime_pattern)) } +#[proc_macro] +pub fn runtime_pattern_disabled(_: TokenStream) -> TokenStream { + panic!( + "macro `runtime_pattern` required to enable crate feature `runtime-pattern` for spdlog-rs" + ); +} + fn into_or_error(result: Result) -> TokenStream { match result { Ok(stream) => stream.into(), diff --git a/spdlog/src/formatter/pattern_formatter/mod.rs b/spdlog/src/formatter/pattern_formatter/mod.rs index 3f82ddf6..5cd607a0 100644 --- a/spdlog/src/formatter/pattern_formatter/mod.rs +++ b/spdlog/src/formatter/pattern_formatter/mod.rs @@ -342,6 +342,10 @@ use crate::{ /// [`FullFormatter`]: crate::formatter::FullFormatter pub use ::spdlog_macros::pattern; +// Emit a compile error if the feature is not enabled. +#[cfg(not(feature = "runtime-pattern"))] +pub use ::spdlog_macros::runtime_pattern_disabled as runtime_pattern; + /// Formats logs according to a specified pattern. #[derive(Clone)] pub struct PatternFormatter

{ diff --git a/spdlog/src/sink/rotating_file_sink.rs b/spdlog/src/sink/rotating_file_sink.rs index ea7dc015..e3c1a495 100644 --- a/spdlog/src/sink/rotating_file_sink.rs +++ b/spdlog/src/sink/rotating_file_sink.rs @@ -112,6 +112,7 @@ struct RotatorTimePoint { base_path: PathBuf, time_point: TimePoint, max_files: usize, + time_format_path: bool, inner: SpinMutex, } @@ -158,6 +159,7 @@ pub struct RotatingFileSinkBuilder { rotation_policy: ArgRP, max_files: usize, rotate_on_open: bool, + time_format_path: bool, } impl RotatingFileSink { @@ -173,6 +175,7 @@ impl RotatingFileSink { /// | [rotation_policy] | *must be specified* | /// | [max_files] | `0` | /// | [rotate_on_open] | `false` | + /// | [time_format_path]| `false` | /// /// [level_filter]: RotatingFileSinkBuilder::level_filter /// [formatter]: RotatingFileSinkBuilder::formatter @@ -182,6 +185,7 @@ impl RotatingFileSink { /// [rotation_policy]: RotatingFileSinkBuilder::rotation_policy /// [max_files]: RotatingFileSinkBuilder::max_files /// [rotate_on_open]: RotatingFileSinkBuilder::rotate_on_open + /// [time_format_path]: RotatingFileSinkBuilder::time_format_path #[must_use] pub fn builder() -> RotatingFileSinkBuilder<(), ()> { RotatingFileSinkBuilder { @@ -190,6 +194,7 @@ impl RotatingFileSink { rotation_policy: (), max_files: 0, rotate_on_open: false, + time_format_path: false, } } @@ -481,10 +486,12 @@ impl RotatorTimePoint { base_path: PathBuf, time_point: TimePoint, max_files: usize, + time_format_path: bool, truncate: bool, ) -> Result { let now = override_now.unwrap_or_else(SystemTime::now); - let file_path = Self::calc_file_path(base_path.as_path(), time_point, now); + let file_path = + Self::calc_file_path(base_path.as_path(), time_point, now, time_format_path); let file = utils::open_file(file_path, truncate)?; let inner = RotatorTimePointInner { @@ -497,6 +504,7 @@ impl RotatorTimePoint { base_path, time_point, max_files, + time_format_path, inner: SpinMutex::new(inner), }; @@ -510,7 +518,12 @@ impl RotatorTimePoint { let mut file_paths = LinkedList::new(); for _ in 0..max_files { - let file_path = Self::calc_file_path(&self.base_path, self.time_point, now); + let file_path = Self::calc_file_path( + &self.base_path, + self.time_point, + now, + self.time_format_path, + ); if !file_path.exists() { break; @@ -586,57 +599,65 @@ impl RotatorTimePoint { base_path: impl AsRef, time_point: TimePoint, system_time: SystemTime, + time_format_path: bool, ) -> PathBuf { let base_path = base_path.as_ref(); let local_time: DateTime = system_time.into(); - let mut file_name = base_path - .file_stem() - .map(|s| s.to_owned()) - .unwrap_or_else(|| OsString::from("")); - - let externsion = base_path.extension(); - - match time_point { - TimePoint::Daily { .. } => { - // append y-m-d - file_name.push(format!( - "_{}-{:02}-{:02}", - local_time.year(), - local_time.month(), - local_time.day() - )); - } - TimePoint::Hourly => { - // append y-m-d_h - file_name.push(format!( - "_{}-{:02}-{:02}_{:02}", - local_time.year(), - local_time.month(), - local_time.day(), - local_time.hour() - )); + if time_format_path { + local_time + .format(&base_path.to_string_lossy()) + .to_string() + .into() + } else { + let mut file_name = base_path + .file_stem() + .map(|s| s.to_owned()) + .unwrap_or_else(|| OsString::from("")); + + let externsion = base_path.extension(); + + match time_point { + TimePoint::Daily { .. } => { + // append y-m-d + file_name.push(format!( + "_{}-{:02}-{:02}", + local_time.year(), + local_time.month(), + local_time.day() + )); + } + TimePoint::Hourly => { + // append y-m-d_h + file_name.push(format!( + "_{}-{:02}-{:02}_{:02}", + local_time.year(), + local_time.month(), + local_time.day(), + local_time.hour() + )); + } + TimePoint::Period { .. } => { + // append y-m-d_h-m + file_name.push(format!( + "_{}-{:02}-{:02}_{:02}-{:02}", + local_time.year(), + local_time.month(), + local_time.day(), + local_time.hour(), + local_time.minute() + )); + } } - TimePoint::Period { .. } => { - // append y-m-d_h-m - file_name.push(format!( - "_{}-{:02}-{:02}_{:02}-{:02}", - local_time.year(), - local_time.month(), - local_time.day(), - local_time.hour(), - local_time.minute() - )); + + let mut path = base_path.to_owned(); + path.set_file_name(file_name); + if let Some(externsion) = externsion { + path.set_extension(externsion); } - } - let mut path = base_path.to_owned(); - path.set_file_name(file_name); - if let Some(externsion) = externsion { - path.set_extension(externsion); + path } - - path } } @@ -653,6 +674,7 @@ impl Rotator for RotatorTimePoint { &self.base_path, self.time_point, record_time, + self.time_format_path, )); inner.file = BufWriter::new(utils::open_file(file_path.as_ref().unwrap(), true)?); inner.rotation_time_point = @@ -728,6 +750,7 @@ impl RotatingFileSinkBuilder { rotation_policy: self.rotation_policy, max_files: self.max_files, rotate_on_open: self.rotate_on_open, + time_format_path: self.time_format_path, } } @@ -745,6 +768,7 @@ impl RotatingFileSinkBuilder { rotation_policy, max_files: self.max_files, rotate_on_open: self.rotate_on_open, + time_format_path: self.time_format_path, } } @@ -777,6 +801,17 @@ impl RotatingFileSinkBuilder { self } + /// Specifies whether the base path provided should be formatted with time, + /// rather than appending the time to the file name. Has no impact for + /// [`RotationPolicy::FileSize`]. + /// + /// This parameter is **optional**. + #[must_use] + pub fn time_format_path(mut self, time_format_path: bool) -> Self { + self.time_format_path = time_format_path; + self + } + helper::common_impl!(@SinkBuilder: common_builder_impl); } @@ -828,6 +863,7 @@ impl RotatingFileSinkBuilder { self.base_path, TimePoint::Daily { hour, minute }, self.max_files, + self.time_format_path, self.rotate_on_open, )?) } @@ -836,6 +872,7 @@ impl RotatingFileSinkBuilder { self.base_path, TimePoint::Hourly, self.max_files, + self.time_format_path, self.rotate_on_open, )?), RotationPolicy::Period(duration) => RotatorKind::TimePoint(RotatorTimePoint::new( @@ -843,6 +880,7 @@ impl RotatingFileSinkBuilder { self.base_path, TimePoint::Period(duration), self.max_files, + self.time_format_path, self.rotate_on_open, )?), }; @@ -1120,29 +1158,36 @@ mod tests { fn calc_file_path() { let system_time = Local.with_ymd_and_hms(2012, 3, 4, 5, 6, 7).unwrap().into(); - let calc_daily = |base_path| { + let calc_daily = |base_path, time_format_path| { RotatorTimePoint::calc_file_path( base_path, TimePoint::Daily { hour: 8, minute: 9 }, system_time, + time_format_path, ) .to_str() .unwrap() .to_string() }; - let calc_hourly = |base_path| { - RotatorTimePoint::calc_file_path(base_path, TimePoint::Hourly, system_time) - .to_str() - .unwrap() - .to_string() + let calc_hourly = |base_path, time_format_path| { + RotatorTimePoint::calc_file_path( + base_path, + TimePoint::Hourly, + system_time, + time_format_path, + ) + .to_str() + .unwrap() + .to_string() }; - let calc_period = |base_path| { + let calc_period = |base_path, time_format_path| { RotatorTimePoint::calc_file_path( base_path, TimePoint::Period(10 * MINUTE_1), system_time, + time_format_path, ) .to_str() .unwrap() @@ -1151,30 +1196,54 @@ mod tests { #[cfg(not(windows))] let run = || { - assert_eq!(calc_daily("/tmp/test.log"), "/tmp/test_2012-03-04.log"); - assert_eq!(calc_daily("/tmp/test"), "/tmp/test_2012-03-04"); + assert_eq!( + calc_daily("/tmp/test.log", false), + "/tmp/test_2012-03-04.log" + ); + assert_eq!(calc_daily("/tmp/test", false), "/tmp/test_2012-03-04"); + assert_eq!( + calc_daily("/tmp/%Y/%m/%d/test.log", true), + "/tmp/2012/03/04/test.log" + ); - assert_eq!(calc_hourly("/tmp/test.log"), "/tmp/test_2012-03-04_05.log"); - assert_eq!(calc_hourly("/tmp/test"), "/tmp/test_2012-03-04_05"); + assert_eq!( + calc_hourly("/tmp/test.log", false), + "/tmp/test_2012-03-04_05.log" + ); + assert_eq!(calc_hourly("/tmp/test", false), "/tmp/test_2012-03-04_05"); + assert_eq!( + calc_hourly("/tmp/%Y/%m/%d/test_%H.log", true), + "/tmp/2012/03/04/test_05.log" + ); assert_eq!( - calc_period("/tmp/test.log"), + calc_period("/tmp/test.log", false), "/tmp/test_2012-03-04_05-06.log" ); - assert_eq!(calc_period("/tmp/test"), "/tmp/test_2012-03-04_05-06"); + assert_eq!( + calc_period("/tmp/test", false), + "/tmp/test_2012-03-04_05-06" + ); + assert_eq!( + calc_period("/tmp/%Y/%m/%d/test_%H-%M.log", true), + "/tmp/2012/03/04/test_05-06.log" + ); }; #[cfg(windows)] #[rustfmt::skip] let run = || { - assert_eq!(calc_daily("D:\\tmp\\test.txt"), "D:\\tmp\\test_2012-03-04.txt"); - assert_eq!(calc_daily("D:\\tmp\\test"), "D:\\tmp\\test_2012-03-04"); + assert_eq!(calc_daily("D:\\tmp\\test.txt", false), "D:\\tmp\\test_2012-03-04.txt"); + assert_eq!(calc_daily("D:\\tmp\\test", false), "D:\\tmp\\test_2012-03-04"); + assert_eq!(calc_daily("D:\\tmp\\%Y\\%m\\%d\\test.txt", true), "D:\\tmp\\2012\\03\\04\\test.txt"); - assert_eq!(calc_hourly("D:\\tmp\\test.txt"), "D:\\tmp\\test_2012-03-04_05.txt"); - assert_eq!(calc_hourly("D:\\tmp\\test"), "D:\\tmp\\test_2012-03-04_05"); + assert_eq!(calc_hourly("D:\\tmp\\test.txt", false), "D:\\tmp\\test_2012-03-04_05.txt"); + assert_eq!(calc_hourly("D:\\tmp\\test", false), "D:\\tmp\\test_2012-03-04_05"); + assert_eq!(calc_hourly("D:\\tmp\\%Y\\%m\\%d\\test_%H.txt", true), "D:\\tmp\\2012\\03\\04\\test_05.txt"); - assert_eq!(calc_period("D:\\tmp\\test.txt"), "D:\\tmp\\test_2012-03-04_05-06.txt"); - assert_eq!(calc_period("D:\\tmp\\test"), "D:\\tmp\\test_2012-03-04_05-06"); + assert_eq!(calc_period("D:\\tmp\\test.txt", false), "D:\\tmp\\test_2012-03-04_05-06.txt"); + assert_eq!(calc_period("D:\\tmp\\test", false), "D:\\tmp\\test_2012-03-04_05-06"); + assert_eq!(calc_period("D:\\tmp\\%Y\\%m\\%d\\test_%H-%M.txt", true), "D:\\tmp\\2012\\03\\04\\test_05-06.txt"); }; run(); diff --git a/spdlog/tests/compile_fail.rs b/spdlog/tests/compile_fail.rs index 3abdc250..17c691e4 100644 --- a/spdlog/tests/compile_fail.rs +++ b/spdlog/tests/compile_fail.rs @@ -5,4 +5,6 @@ fn compile_fail() { t.compile_fail("tests/compile_fail/pattern_macro_*.rs"); #[cfg(feature = "runtime-pattern")] t.compile_fail("tests/compile_fail/pattern_runtime_macro_*.rs"); + #[cfg(not(feature = "runtime-pattern"))] + t.compile_fail("tests/compile_fail/pattern_runtime_disabled.rs"); } diff --git a/spdlog/tests/compile_fail/pattern_runtime_disabled.rs b/spdlog/tests/compile_fail/pattern_runtime_disabled.rs new file mode 100644 index 00000000..261bae95 --- /dev/null +++ b/spdlog/tests/compile_fail/pattern_runtime_disabled.rs @@ -0,0 +1,7 @@ +use spdlog::formatter::runtime_pattern; + +fn runtime_pattern() { + runtime_pattern!("{logger}"); +} + +fn main() {} diff --git a/spdlog/tests/compile_fail/pattern_runtime_disabled.stderr b/spdlog/tests/compile_fail/pattern_runtime_disabled.stderr new file mode 100644 index 00000000..46fee96e --- /dev/null +++ b/spdlog/tests/compile_fail/pattern_runtime_disabled.stderr @@ -0,0 +1,7 @@ +error: proc macro panicked + --> tests/compile_fail/pattern_runtime_disabled.rs:4:5 + | +4 | runtime_pattern!("{logger}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: macro `runtime_pattern` required to enable crate feature `runtime-pattern` for spdlog-rs