diff --git a/CHANGELOG.md b/CHANGELOG.md
index 025ac3a..063d22b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,12 @@ All notable changes to the time project will be documented in this file.
---
+
+### 0.3.1 (2022-04-26)
+
+#### Changes
+
+* Added warning if running with old time format from chrono and falling back to default one
### 0.3.0 (2022-03-14)
diff --git a/Cargo.toml b/Cargo.toml
index d52c9f6..354d183 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,7 @@
[package]
edition = "2018"
name = "flowgger"
-version = "0.3.0"
+version = "0.3.1"
authors = ["Frank Denis ", "Matteo Bigoi ", "Vivien Chene ", "Francesco Berni "]
build = "build.rs"
repository = "https://github.com/awslabs/flowgger"
diff --git a/src/flowgger/encoder/mod.rs b/src/flowgger/encoder/mod.rs
index a0e0cba..06c712f 100644
--- a/src/flowgger/encoder/mod.rs
+++ b/src/flowgger/encoder/mod.rs
@@ -24,10 +24,12 @@ pub use self::rfc3164_encoder::RFC3164Encoder;
#[cfg(feature = "rfc5424")]
pub use self::rfc5424_encoder::RFC5424Encoder;
-use crate::flowgger::config::Config;
use crate::flowgger::record::Record;
+use crate::flowgger::{config::Config, validate_time_format_input};
use time::{format_description, OffsetDateTime};
+const SYSLOG_PREPEND_DEFAULT_TIME_FORMAT: &str = "[year][month][day]T[hour][minute][second]Z";
+
pub trait CloneBoxedEncoder {
fn clone_boxed<'a>(&self) -> Box
where
@@ -54,7 +56,7 @@ pub trait Encoder: CloneBoxedEncoder {
}
pub fn config_get_prepend_ts(config: &Config) -> Option {
- config
+ let prepend_ts = config
.lookup("output.syslog_prepend_timestamp")
.map_or(None, |bs| {
Some(
@@ -62,7 +64,20 @@ pub fn config_get_prepend_ts(config: &Config) -> Option {
.expect("output.syslog_prepend_timestamp should be a string")
.to_string(),
)
- })
+ });
+
+ match prepend_ts {
+ Some(time_format) => {
+ let actual_time_format = validate_time_format_input(
+ "syslog_prepend_timestamp",
+ &time_format,
+ SYSLOG_PREPEND_DEFAULT_TIME_FORMAT.to_string(),
+ );
+ Some(actual_time_format)
+ }
+ // Not using syslog_prepend_timestamp - no need to validate
+ None => None,
+ }
}
pub fn build_prepend_ts(format_str: &str) -> Result {
diff --git a/src/flowgger/mod.rs b/src/flowgger/mod.rs
index 5c3b87a..6aa408b 100644
--- a/src/flowgger/mod.rs
+++ b/src/flowgger/mod.rs
@@ -8,6 +8,8 @@ mod record;
mod splitter;
mod utils;
+use std::io::{stderr, Write};
+
#[cfg(feature = "capnp-recompile")]
extern crate capnp;
extern crate clap;
@@ -307,6 +309,48 @@ fn get_encoder_passthrough(_config: &Config) -> ! {
panic!("Support for passthrough hasn't been compiled in")
}
+/// Validate that the time format used are in
+/// conform to https://docs.rs/time/0.3.7/time/format_description/index.html
+///
+/// This is to raise a warning to users that are still using the old format from
+/// `chrono` library.
+///
+/// If '%' is still desirable to be part of the time format string, it needs to be escaped, like:
+/// file_rotation_timeformat = "[year][month][day]\\%T[hour][minute]Z"
+/// This will result in a file with name: "20220425%T1043Z"
+///
+/// # Paramters
+/// - `name`: the name of the param. Like `file_rotation_timeformat`
+/// - `time_format`: the format to be validated
+/// - `time_format_default`: the default value to use if `time_format` is invalid
+///
+/// # Returns
+/// Return an `String` which is the same value as `time_format` if valid
+/// or `time_format_default`
+///
+pub fn validate_time_format_input(
+ name: &str,
+ time_format: &str,
+ time_format_default: String,
+) -> String {
+ if time_format.matches("%").count() != time_format.matches("\\%").count() {
+ let _ = writeln!(
+ stderr(),
+ "WARNING: Wrong {} value received: {}.\n\
+ From version \"0.3.0\" forward the time format needs to be compliant with:\n\
+ https://docs.rs/time/0.3.7/time/format_description/index.html \n\
+ Will use the default one: {}. If you want to use %, you need to escape it (\\\\%)\n",
+ name,
+ time_format,
+ time_format_default
+ );
+ time_format_default
+ } else {
+ //replacing the escaped chars so the file has the correct name
+ time_format.replace("\\%", "%").to_string()
+ }
+}
+
pub fn start(config_file: &str) {
let config = match Config::from_path(config_file) {
Ok(config) => config,
@@ -385,3 +429,45 @@ pub fn start(config_file: &str) {
output.start(arx, merger);
input.accept(tx, decoder, encoder);
}
+
+#[cfg(test)]
+mod tests {
+ use super::validate_time_format_input;
+
+ #[test]
+ fn test_invalid_time_format() {
+ let default_value = "DEFAULT VALUE";
+ let time_format = validate_time_format_input(
+ "file_rotation_timeformat",
+ "%Y%M",
+ default_value.to_string(),
+ );
+
+ assert!(time_format.eq(default_value));
+ }
+
+ #[test]
+ fn test_valid_time_format() {
+ let input_time_format = "[year][month]]";
+ let time_format = validate_time_format_input(
+ "file_rotation_timeformat",
+ input_time_format,
+ "default_value".to_string(),
+ );
+
+ assert!(time_format.eq(input_time_format));
+ }
+
+ #[test]
+ fn test_valid_time_format_escaped() {
+ let input_time_format = "[year]\\%T[month]]";
+ let input_time_format_without_escaped_char = "[year]%T[month]]";
+ let time_format = validate_time_format_input(
+ "file_rotation_timeformat",
+ input_time_format,
+ "default_value".to_string(),
+ );
+
+ assert!(time_format.eq(input_time_format_without_escaped_char));
+ }
+}
diff --git a/src/flowgger/output/file_output.rs b/src/flowgger/output/file_output.rs
index f23b59a..873b660 100644
--- a/src/flowgger/output/file_output.rs
+++ b/src/flowgger/output/file_output.rs
@@ -2,6 +2,7 @@ use super::Output;
use crate::flowgger::config::Config;
use crate::flowgger::merger::Merger;
use crate::flowgger::utils::rotating_file::RotatingFile;
+use crate::flowgger::validate_time_format_input;
use std::io::{BufWriter, Write};
use std::sync::mpsc::Receiver;
use std::sync::{Arc, Mutex};
@@ -9,7 +10,7 @@ use std::thread;
use std::io::stderr;
const FILE_DEFAULT_BUFFER_SIZE: usize = 0;
-const FILE_DEFAULT_TIME_FORMAT: &str = "%Y%m%dT%H%M%SZ";
+const FILE_DEFAULT_TIME_FORMAT: &str = "[year][month][day]T[hour][minute][second]Z";
const FILE_DEFAULT_ROTATION_SIZE: usize = 0;
const FILE_DEFAULT_ROTATION_TIME: u32 = 0;
const FILE_DEFAULT_ROTATION_MAXFILES: i32 = 50;
@@ -99,6 +100,12 @@ impl FileOutput {
},
);
+ let time_format = validate_time_format_input(
+ "file_rotation_timeformat",
+ &time_format,
+ FILE_DEFAULT_TIME_FORMAT.to_string(),
+ );
+
FileOutput {
path,
buffer_size,
diff --git a/src/flowgger/utils/rotating_file.rs b/src/flowgger/utils/rotating_file.rs
index 4c3a3ef..f8adf1b 100644
--- a/src/flowgger/utils/rotating_file.rs
+++ b/src/flowgger/utils/rotating_file.rs
@@ -32,7 +32,7 @@ impl RotatingFile {
///
/// If the time trigger is specified (max_time >0):
/// All file names are appended with their creation timestamp. i.e. configured file "abcd.log" might become
- /// "abcd-20180108T0143Z.log" if the time format is configured to be "%Y%m%dT%H%MZ"
+ /// "abcd-20180108T0143Z.log" if the time format is configured to be "[year][month][day]T[hour][minute]Z"
/// A file "expires" when its creation time + configured max_time is reached (based on current UTC time).
/// Rotation occurs when a write is requested to an expired file. The file is then closed and a new one is created.
/// # Notes:
@@ -83,7 +83,7 @@ impl RotatingFile {
/// - basename = 'logs/syslog.log'
/// - max_time = 2
/// - app started on 2018-01-08 at 01:43 UTC
- /// - time format is "%Y%m%dT%H%M%SZ"
+ /// - time format is "[year][month][day]T[hour][minute][second]Z"
///
/// The following files will be generated:
/// - Current file = 'logs/syslog-20180108T014343Z.log'