From f688e38ccd6dffc11ae8c9d4495fe28b74ffde9e Mon Sep 17 00:00:00 2001 From: Dirreck Date: Tue, 2 Apr 2024 00:57:29 +0800 Subject: [PATCH] Add trigger `onstartup` (#343) --- Cargo.toml | 2 + docs/Configuration.md | 12 +- .../policy/compound/trigger/mod.rs | 3 + .../policy/compound/trigger/onstartup.rs | 119 ++++++++++++++++++ src/config/raw.rs | 8 ++ src/lib.rs | 1 + 6 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 src/append/rolling_file/policy/compound/trigger/onstartup.rs diff --git a/Cargo.toml b/Cargo.toml index 9f648269..fbc77abd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ delete_roller = [] fixed_window_roller = [] size_trigger = [] time_trigger = ["rand"] +onstartup_trigger = [] json_encoder = ["serde", "serde_json", "chrono", "log-mdc", "log/serde", "thread-id"] pattern_encoder = ["chrono", "log-mdc", "thread-id"] ansi_writer = [] @@ -43,6 +44,7 @@ all_components = [ "fixed_window_roller", "size_trigger", "time_trigger", + "onstartup_trigger", "json_encoder", "pattern_encoder", "threshold_filter" diff --git a/docs/Configuration.md b/docs/Configuration.md index 5ec2ce65..a7dd94a3 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -172,7 +172,7 @@ The new component is the _policy_ field. A policy must have the _kind_ field lik other components, the default (and only supported) policy is `kind: compound`. The _trigger_ field is used to dictate when the log file should be rolled. It -supports two types: `size`, and `time`. +supports three types: `size`, `time` and `onstartup`. For `size`, it require a _limit_ field. The _limit_ field is a string which defines the maximum file size prior to a rolling of the file. The limit field requires one of the following @@ -233,6 +233,16 @@ trigger: max_random_delay: 0 ``` +For `onstartup`, it has an optional field, _min_size_. It indicates the minimum size the file must have to roll over. A size of zero will cause a roll over no matter what the file size is. The default value is 1, which will prevent rolling over an empty file. + +i.e. + +```yml +trigger: + kind: onstartup + min_size: 1 +``` + The _roller_ field supports two types: delete, and fixed_window. The delete roller does not take any other configuration fields. The fixed_window roller supports three fields: pattern, base, and count. The most current log file will diff --git a/src/append/rolling_file/policy/compound/trigger/mod.rs b/src/append/rolling_file/policy/compound/trigger/mod.rs index 3d61e0da..bc99271c 100644 --- a/src/append/rolling_file/policy/compound/trigger/mod.rs +++ b/src/append/rolling_file/policy/compound/trigger/mod.rs @@ -12,6 +12,9 @@ pub mod size; #[cfg(feature = "time_trigger")] pub mod time; +#[cfg(feature = "onstartup_trigger")] +pub mod onstartup; + /// A trait which identifies if the active log file should be rolled over. pub trait Trigger: fmt::Debug + Send + Sync + 'static { /// Determines if the active log file should be rolled over. diff --git a/src/append/rolling_file/policy/compound/trigger/onstartup.rs b/src/append/rolling_file/policy/compound/trigger/onstartup.rs new file mode 100644 index 00000000..6934d5ec --- /dev/null +++ b/src/append/rolling_file/policy/compound/trigger/onstartup.rs @@ -0,0 +1,119 @@ +//! The OnStartUp trigger. +//! +//! Requires the `onstartup_trigger` feature. + +use std::sync::Once; + +use crate::append::rolling_file::{policy::compound::trigger::Trigger, LogFile}; + +#[cfg(feature = "config_parsing")] +use crate::config::{Deserialize, Deserializers}; + +/// Configuration for the onstartup trigger. +#[cfg(feature = "config_parsing")] +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub struct OnStartUpTriggerConfig { + #[serde(default = "default_min_size")] + min_size: u64, +} + +#[cfg(feature = "config_parsing")] +fn default_min_size() -> u64 { + 1 +} + +/// A trigger which rolls the log on startup. +#[derive(Debug)] +pub struct OnStartUpTrigger { + min_size: u64, + initial: Once, +} + +impl OnStartUpTrigger { + /// Returns a new trigger which rolls the log on startup. + pub fn new(min_size: u64) -> OnStartUpTrigger { + OnStartUpTrigger { + min_size, + initial: Once::new(), + } + } +} + +impl Trigger for OnStartUpTrigger { + fn trigger(&self, file: &LogFile) -> anyhow::Result { + let mut result = false; + self.initial.call_once(|| { + if file.len_estimate() >= self.min_size { + result = true; + } + }); + Ok(result) + } + + fn is_pre_process(&self) -> bool { + true + } +} + +/// A deserializer for the `OnStartUpTrigger`. +/// +/// # Configuration +/// +/// ```yaml +/// kind: onstartup +/// +/// ``` +#[cfg(feature = "config_parsing")] +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] +pub struct OnStartUpTriggerDeserializer; + +#[cfg(feature = "config_parsing")] +impl Deserialize for OnStartUpTriggerDeserializer { + type Trait = dyn Trigger; + + type Config = OnStartUpTriggerConfig; + + fn deserialize( + &self, + config: OnStartUpTriggerConfig, + _: &Deserializers, + ) -> anyhow::Result> { + Ok(Box::new(OnStartUpTrigger::new(config.min_size))) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn pre_process() { + let trigger = OnStartUpTrigger::new(0); + assert!(trigger.is_pre_process()); + } + + fn trigger_with_file_size(file_size: u64) -> (bool, bool) { + let file = tempfile::tempdir().unwrap(); + let logfile = LogFile { + writer: &mut None, + path: file.path(), + len: file_size, + }; + + let trigger = OnStartUpTrigger::new(1); // default + let result1 = trigger.trigger(&logfile).unwrap(); + let result2 = trigger.trigger(&logfile).unwrap(); + (result1, result2) + } + + #[test] + fn trigger() { + // When the file size < min_size, the trigger should return false. + assert_eq!(trigger_with_file_size(0), (false, false)); + // When the file size == min_size, the trigger should return true for the first time. + assert_eq!(trigger_with_file_size(1), (true, false)); + // When the file size >= min_size, the trigger should return true for the first time. + assert_eq!(trigger_with_file_size(2), (true, false)); + } +} diff --git a/src/config/raw.rs b/src/config/raw.rs index 9868b495..51c0bd42 100644 --- a/src/config/raw.rs +++ b/src/config/raw.rs @@ -219,6 +219,12 @@ impl Default for Deserializers { append::rolling_file::policy::compound::trigger::time::TimeTriggerDeserializer, ); + #[cfg(feature = "onstartup_trigger")] + d.insert( + "onstartup", + append::rolling_file::policy::compound::trigger::onstartup::OnStartUpTriggerDeserializer, + ); + #[cfg(feature = "json_encoder")] d.insert("json", encode::json::JsonEncoderDeserializer); @@ -265,6 +271,8 @@ impl Deserializers { /// * Requires the `size_trigger` feature. /// * "time" -> `TimeTriggerDeserializer` /// * Requires the `time_trigger` feature. + /// * "onstartup" -> `OnStartUpTriggerDeserializer` + /// * Requires the `onstartup_trigger` feature. pub fn new() -> Deserializers { Deserializers::default() } diff --git a/src/lib.rs b/src/lib.rs index 96b3b992..a80f390b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,6 +22,7 @@ //! - Triggers //! - [size](append/rolling_file/policy/compound/trigger/size/struct.SizeTriggerDeserializer.html#configuration): requires the `size_trigger` feature //! - [time](append/rolling_file/policy/compound/trigger/tine/struct.TimeTriggerDeserializer.html#configuration): requires the `time_trigger` feature +//! - [onstartup](append/rolling_file/policy/compound/trigger/tine/struct.OnStartUpTriggerDeserializer.html#configuration): requires the `onstartup_trigger` feature //! //! ## Encoders //!