Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,7 @@ harness = false

[dependencies]
arc-swap = "1.6"
chrono = { version = "0.4.23", optional = true, features = [
"clock",
], default-features = false }
chrono = { version = "0.4.37", optional = true, features = ["clock"], default-features = false }
flate2 = { version = "1.0", optional = true }
zstd = { version = "0.13", optional = true }
fnv = "1.0"
Expand Down
102 changes: 58 additions & 44 deletions src/append/rolling_file/policy/compound/trigger/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use rand::Rng;
use serde::de;
#[cfg(feature = "config_parsing")]
use std::fmt;
use std::sync::RwLock;
use std::sync::{Once, RwLock};

use crate::append::rolling_file::{policy::compound::trigger::Trigger, LogFile};
#[cfg(feature = "config_parsing")]
Expand Down Expand Up @@ -47,6 +47,7 @@ pub struct TimeTriggerConfig {
pub struct TimeTrigger {
config: TimeTriggerConfig,
next_roll_time: RwLock<DateTime<Local>>,
initial: Once,
}

/// The TimeTrigger supports the following units (case insensitive):
Expand Down Expand Up @@ -179,45 +180,24 @@ impl TimeTrigger {
/// Returns a new trigger which rolls the log once it has passed the
/// specified time.
pub fn new(config: TimeTriggerConfig) -> TimeTrigger {
#[cfg(mock_time)]
let current = {
use mock_instant::thread_local::{SystemTime, UNIX_EPOCH};

let now: std::time::Duration = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system time before Unix epoch");
DateTime::from_timestamp(now.as_secs() as i64, now.subsec_nanos())
.unwrap()
.with_timezone(&Local)
};

#[cfg(not(mock_time))]
let current = Local::now();
let next_time = TimeTrigger::get_next_time(current, config.interval, config.modulate);
let next_roll_time = if config.max_random_delay > 0 {
let random_delay = rand::rng().random_range(0..config.max_random_delay);
next_time + Duration::seconds(random_delay as i64)
} else {
next_time
};
Comment thread
Dirreke marked this conversation as resolved.

TimeTrigger {
config,
next_roll_time: RwLock::new(next_roll_time),
next_roll_time: RwLock::default(),
initial: Once::new(),
}
}

fn get_next_time(
current: DateTime<Local>,
interval: TimeTriggerInterval,
modulate: bool,
) -> DateTime<Local> {
fn get_next_time(&self, current: DateTime<Local>) -> DateTime<Local> {
let interval = self.config.interval;
let modulate = self.config.modulate;

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 result;
}

if let TimeTriggerInterval::Month(n) = interval {
Expand All @@ -228,9 +208,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 result;
}

let month = current.month();
Expand Down Expand Up @@ -278,10 +259,40 @@ impl TimeTrigger {
}
panic!("Should not reach here!");
}

fn refresh_time(&self) {
#[cfg(mock_time)]
let current = {
use mock_instant::thread_local::{SystemTime, UNIX_EPOCH};

let now: std::time::Duration = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system time before Unix epoch");
DateTime::from_timestamp(now.as_secs() as i64, now.subsec_nanos())
.unwrap()
.naive_local()
.and_local_timezone(Local)
.unwrap()
};

#[cfg(not(mock_time))]
let current = Local::now();
let next_time = self.get_next_time(current);
let next_roll_time = if self.config.max_random_delay > 0 {
let random_delay = rand::rng().random_range(0..self.config.max_random_delay);
next_time + Duration::seconds(random_delay as i64)
} else {
next_time
};
*self.next_roll_time.write().unwrap() = next_roll_time;
}
}

impl Trigger for TimeTrigger {
fn trigger(&self, _file: &LogFile) -> anyhow::Result<bool> {
self.initial.call_once(|| {
self.refresh_time();
});
#[cfg(mock_time)]
let current = {
use mock_instant::thread_local::{SystemTime, UNIX_EPOCH};
Expand All @@ -291,17 +302,18 @@ impl Trigger for TimeTrigger {
.expect("system time before Unix epoch");
DateTime::from_timestamp(now.as_secs() as i64, now.subsec_nanos())
.unwrap()
.with_timezone(&Local)
.naive_local()
.and_local_timezone(Local)
.unwrap()
};

#[cfg(not(mock_time))]
let current: DateTime<Local> = Local::now();
let mut next_roll_time = self.next_roll_time.write().unwrap();
let next_roll_time = self.next_roll_time.read().unwrap();
let is_trigger = current >= *next_roll_time;
drop(next_roll_time);
if is_trigger {
let tmp = TimeTrigger::new(self.config);
let time_new = tmp.next_roll_time.read().unwrap();
*next_roll_time = *time_new;
self.refresh_time();
}
Ok(is_trigger)
}
Comment thread
gauntl3t12 marked this conversation as resolved.
Expand Down Expand Up @@ -345,9 +357,10 @@ impl Deserialize for TimeTriggerDeserializer {
#[cfg(test)]
mod test {
use super::*;
#[cfg(mock_time)]
use mock_instant::thread_local::MockClock;
use std::time::Duration;

#[cfg(mock_time)]
fn trigger_with_time_and_modulate(
interval: TimeTriggerInterval,
modulate: bool,
Expand All @@ -359,26 +372,26 @@ mod test {
path: file.path(),
len: 0,
};

let config = TimeTriggerConfig {
interval,
modulate,
max_random_delay: 0,
};

let trigger = TimeTrigger::new(config);
trigger.trigger(&logfile).unwrap();

MockClock::advance_system_time(Duration::from_millis(millis / 2));
MockClock::advance_system_time(std::time::Duration::from_millis(millis / 2));
let result1 = trigger.trigger(&logfile).unwrap();

MockClock::advance_system_time(Duration::from_millis(millis / 2));
MockClock::advance_system_time(std::time::Duration::from_millis(millis / 2));
let result2 = trigger.trigger(&logfile).unwrap();

(result1, result2)
}

#[test]
#[ignore]
#[cfg(mock_time)]
fn trigger() {
let second_in_milli = 1000;
let minute_in_milli = second_in_milli * 60;
Expand All @@ -400,14 +413,15 @@ mod test {
let modulate = false;
for (time_trigger_interval, time_in_milli) in &test_list {
dbg!(time_in_milli);
MockClock::set_system_time(Duration::from_millis(4 * day_in_milli)); // 1970/1/5 00:00:00 Monday
MockClock::set_system_time(std::time::Duration::from_millis(4 * day_in_milli)); // 1970/1/5 00:00:00 Monday
assert_eq!(
trigger_with_time_and_modulate(*time_trigger_interval, modulate, *time_in_milli),
(false, true)
);
// trigger will be aligned with units.
MockClock::set_system_time(
Duration::from_millis(4 * day_in_milli) + Duration::from_millis(time_in_milli / 2),
std::time::Duration::from_millis(4 * day_in_milli)
+ std::time::Duration::from_millis(time_in_milli / 2),
);
assert_eq!(
trigger_with_time_and_modulate(*time_trigger_interval, modulate, *time_in_milli),
Expand All @@ -427,7 +441,7 @@ mod test {
let modulate = true;
for (time_trigger_interval, time_in_milli) in &test_list {
dbg!(time_in_milli);
MockClock::set_system_time(Duration::from_millis(
MockClock::set_system_time(std::time::Duration::from_millis(
59 * day_in_milli + 2 * hour_in_milli + 2 * minute_in_milli + 2 * second_in_milli,
)); // 1970/3/1 02:02:02 Sunday
assert_eq!(
Expand Down