Skip to content

Commit

Permalink
validate time format (#69)
Browse files Browse the repository at this point in the history
Co-authored-by: Regis Galvao de Oliveira <[email protected]>
  • Loading branch information
rejao and Regis Galvao de Oliveira authored Apr 27, 2022
1 parent 0b0c37c commit 5d7ee30
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 7 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to the time project will be documented in this file.
---


<a name="0.3.1"></a>
### 0.3.1 (2022-04-26)

#### Changes

* Added warning if running with old time format from chrono and falling back to default one

<a name="0.3.0"></a>
### 0.3.0 (2022-03-14)
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
edition = "2018"
name = "flowgger"
version = "0.3.0"
version = "0.3.1"
authors = ["Frank Denis <[email protected]>", "Matteo Bigoi <[email protected]>", "Vivien Chene <[email protected]>", "Francesco Berni <[email protected]>"]
build = "build.rs"
repository = "https://github.com/awslabs/flowgger"
Expand Down
21 changes: 18 additions & 3 deletions src/flowgger/encoder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<dyn Encoder + Send + 'a>
where
Expand All @@ -54,15 +56,28 @@ pub trait Encoder: CloneBoxedEncoder {
}

pub fn config_get_prepend_ts(config: &Config) -> Option<String> {
config
let prepend_ts = config
.lookup("output.syslog_prepend_timestamp")
.map_or(None, |bs| {
Some(
bs.as_str()
.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<String, &'static str> {
Expand Down
86 changes: 86 additions & 0 deletions src/flowgger/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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));
}
}
9 changes: 8 additions & 1 deletion src/flowgger/output/file_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ 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};
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;
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions src/flowgger/utils/rotating_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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'
Expand Down

0 comments on commit 5d7ee30

Please sign in to comment.