From 0b8f04523e72c9c87bfe96c3cfd7b33e541de0c5 Mon Sep 17 00:00:00 2001 From: Dirreke Date: Sun, 10 Mar 2024 12:09:23 +0800 Subject: [PATCH] Update time.rs --- .../policy/compound/trigger/time.rs | 80 ++++++++++++++----- 1 file changed, 61 insertions(+), 19 deletions(-) diff --git a/src/append/rolling_file/policy/compound/trigger/time.rs b/src/append/rolling_file/policy/compound/trigger/time.rs index ffad356f..e058ee09 100644 --- a/src/append/rolling_file/policy/compound/trigger/time.rs +++ b/src/append/rolling_file/policy/compound/trigger/time.rs @@ -2,8 +2,6 @@ //! //! Requires the `time_trigger` feature. -#[cfg(test)] -use chrono::NaiveDateTime; use chrono::{DateTime, Datelike, Duration, Local, TimeZone, Timelike}; #[cfg(test)] use mock_instant::{SystemTime, UNIX_EPOCH}; @@ -13,6 +11,7 @@ use serde::de; #[cfg(feature = "config_parsing")] use std::fmt; use std::sync::{Once, RwLock}; +use thiserror::Error; use crate::append::rolling_file::{policy::compound::trigger::Trigger, LogFile}; #[cfg(feature = "config_parsing")] @@ -73,6 +72,12 @@ impl Default for TimeTriggerInterval { } } +#[derive(Debug, Error)] +enum TimeTrigerError { + #[error("too large interval in time trigger {0:?}")] + TooLargeInterval(TimeTriggerInterval), +} + #[cfg(feature = "config_parsing")] impl<'de> serde::Deserialize<'de> for TimeTriggerInterval { fn deserialize(d: D) -> Result @@ -193,13 +198,14 @@ impl TimeTrigger { current: DateTime, interval: TimeTriggerInterval, modulate: bool, - ) -> DateTime { + ) -> anyhow::Result> { let year = current.year(); if let TimeTriggerInterval::Year(n) = interval { let n = n as i32; let increment = if modulate { n - year % n } else { n }; let year_new = year + increment; - return Local.with_ymd_and_hms(year_new, 1, 1, 0, 0, 0).unwrap(); + let result = Local.with_ymd_and_hms(year_new, 1, 1, 0, 0, 0).unwrap(); + return Ok(result); } if let TimeTriggerInterval::Month(n) = interval { @@ -210,9 +216,10 @@ impl TimeTrigger { let num_months_new = num_months + increment; let year_new = (num_months_new / 12) as i32; let month_new = (num_months_new) % 12 + 1; - return Local + let result = Local .with_ymd_and_hms(year_new, month_new, 1, 0, 0, 0) .unwrap(); + return Ok(result); } let month = current.month(); @@ -222,14 +229,21 @@ impl TimeTrigger { let weekday = current.weekday().num_days_from_monday() as i64; // Monday is the first day of the week let time = Local.with_ymd_and_hms(year, month, day, 0, 0, 0).unwrap(); let increment = if modulate { n - week0 % n } else { n }; - return time + Duration::weeks(increment) - Duration::days(weekday); + let result = time + + Duration::try_weeks(increment) + .ok_or(TimeTrigerError::TooLargeInterval(interval))? + - Duration::try_days(weekday).unwrap(); + return Ok(result); } if let TimeTriggerInterval::Day(n) = interval { let ordinal0 = current.ordinal0() as i64; let time = Local.with_ymd_and_hms(year, month, day, 0, 0, 0).unwrap(); let increment = if modulate { n - ordinal0 % n } else { n }; - return time + Duration::days(increment); + let result = time + + Duration::try_days(increment) + .ok_or(TimeTrigerError::TooLargeInterval(interval))?; + return Ok(result); } let hour = current.hour(); @@ -238,7 +252,10 @@ impl TimeTrigger { .with_ymd_and_hms(year, month, day, hour, 0, 0) .unwrap(); let increment = if modulate { n - (hour as i64) % n } else { n }; - return time + Duration::hours(increment); + let result = time + + Duration::try_hours(increment) + .ok_or(TimeTrigerError::TooLargeInterval(interval))?; + return Ok(result); } let min = current.minute(); @@ -247,7 +264,10 @@ impl TimeTrigger { .with_ymd_and_hms(year, month, day, hour, min, 0) .unwrap(); let increment = if modulate { n - (min as i64) % n } else { n }; - return time + Duration::minutes(increment); + let result = time + + Duration::try_minutes(increment) + .ok_or(TimeTrigerError::TooLargeInterval(interval))?; + return Ok(result); } let sec = current.second(); @@ -256,48 +276,56 @@ impl TimeTrigger { .with_ymd_and_hms(year, month, day, hour, min, sec) .unwrap(); let increment = if modulate { n - (sec as i64) % n } else { n }; - return time + Duration::seconds(increment); + let result = time + + Duration::try_seconds(increment) + .ok_or(TimeTrigerError::TooLargeInterval(interval))?; + return Ok(result); } panic!("Should not reach here!"); } - fn refresh_time(&self) { + fn refresh_time(&self) -> anyhow::Result<()> { #[cfg(test)] let current = { let now: std::time::Duration = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("system time before Unix epoch"); - NaiveDateTime::from_timestamp_opt(now.as_secs() as i64, now.subsec_nanos()) + DateTime::from_timestamp(now.as_secs() as i64, now.subsec_nanos()) .unwrap() + .naive_local() .and_local_timezone(Local) .unwrap() }; #[cfg(not(test))] let current = Local::now(); - let next_time = TimeTrigger::get_next_time(current, self.interval, self.modulate); + let next_time = TimeTrigger::get_next_time(current, self.interval, self.modulate)?; let next_roll_time = if self.max_random_delay > 0 { let random_delay = rand::thread_rng().gen_range(0..self.max_random_delay); - next_time + Duration::seconds(random_delay as i64) + next_time + + Duration::try_seconds(random_delay as i64) + .unwrap_or(Duration::try_milliseconds(i64::MAX).unwrap()) } else { next_time }; *self.next_roll_time.write().unwrap() = next_roll_time; + Ok(()) } } impl Trigger for TimeTrigger { fn trigger(&self, _file: &LogFile) -> anyhow::Result { - self.initial.call_once(|| { - self.refresh_time(); - }); + let mut result = anyhow::Result::Ok(()); + self.initial.call_once(|| result = self.refresh_time()); + result?; #[cfg(test)] let current = { let now = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("system time before Unix epoch"); - NaiveDateTime::from_timestamp_opt(now.as_secs() as i64, now.subsec_nanos()) + DateTime::from_timestamp(now.as_secs() as i64, now.subsec_nanos()) .unwrap() + .naive_local() .and_local_timezone(Local) .unwrap() }; @@ -308,7 +336,7 @@ impl Trigger for TimeTrigger { let is_trigger = current >= *next_roll_time; drop(next_roll_time); if is_trigger { - self.refresh_time(); + self.refresh_time()?; } Ok(is_trigger) } @@ -494,6 +522,20 @@ mod test { assert_eq!(interval, TimeTriggerInterval::Second(1)); } + #[test] + fn trigger_large_interval() { + let interval = TimeTriggerInterval::Second(i64::MAX); + let file = tempfile::tempdir().unwrap(); + let logfile = LogFile { + writer: &mut None, + path: file.path(), + len: 0, + }; + + let trigger = TimeTrigger::new(interval, false, 0); + trigger.trigger(&logfile).unwrap_err(); + } + #[test] fn pre_process() { let trigger = TimeTrigger::new(TimeTriggerInterval::Minute(2), true, 0);