diff --git a/Cargo.lock b/Cargo.lock index a955a7e7..cad31a4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -769,7 +769,7 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hl" -version = "0.29.6" +version = "0.29.7-alpha.1" dependencies = [ "bincode", "bitmask", @@ -822,6 +822,7 @@ dependencies = [ "stats_alloc", "strum", "thiserror", + "titlecase", "wildflower", "wildmatch", "winapi-util", @@ -1704,6 +1705,16 @@ dependencies = [ "serde_json", ] +[[package]] +name = "titlecase" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acc5e312c2e5385c8d3d31487041ca8ceaec09a8a1174bdfa9c6418c5f6b36fa" +dependencies = [ + "regex", + "wasm-bindgen", +] + [[package]] name = "toml" version = "0.8.13" diff --git a/Cargo.toml b/Cargo.toml index 2c530bd0..4183a9b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ members = [".", "crate/encstr"] [workspace.package] repository = "https://github.com/pamburus/hl" authors = ["Pavel Ivanov "] -version = "0.29.6" +version = "0.29.7-alpha.1" edition = "2021" license = "MIT" @@ -44,7 +44,7 @@ clap_complete = "4" clap_mangen = "0" closure = "0" collection_macros = "0" -config = "0" +config = { version = "0", features = ["yaml", "json", "toml"] } crossbeam-channel = "0" crossbeam-queue = "0" crossbeam-utils = "0" @@ -77,6 +77,7 @@ signal-hook = "0" snap = "1" strum = { version = "0", features = ["derive"] } thiserror = "1" +titlecase = "3" wildflower = { git = "https://github.com/cassaundra/wildflower.git" } winapi-util = { version = "0" } wyhash = "0" diff --git a/etc/defaults/config.yaml b/etc/defaults/config.yaml index 1d25d7a9..b04886f2 100644 --- a/etc/defaults/config.yaml +++ b/etc/defaults/config.yaml @@ -32,10 +32,10 @@ fields: variants: - names: [level, LEVEL, Level] values: - debug: [debug, Debug] - info: [info, information, Information] - warning: [warning, Warning, warn] - error: [error, Error, err, fatal, critical, panic] + debug: [debug] + info: [info, information] + warning: [warning, warn] + error: [error, err, fatal, critical, panic] - names: [PRIORITY] values: debug: [7] diff --git a/src/app.rs b/src/app.rs index 96fbd280..307a9f6f 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1066,13 +1066,20 @@ where #[cfg(test)] mod tests { + // super imports use super::*; + // std imports use std::io::Cursor; + // third-party imports use chrono_tz::UTC; + use maplit::hashmap; - use crate::{filtering::MatchOptions, model::FieldFilterSet, themecfg::testing, LinuxDateFormat}; + // local imports + use crate::{ + filtering::MatchOptions, level::Level, model::FieldFilterSet, settings, themecfg::testing, LinuxDateFormat, + }; #[test] fn test_common_prefix_len() { @@ -1333,6 +1340,43 @@ mod tests { ); } + #[test] + fn test_issue_288_t1() { + let input = input(concat!( + r#"time="2024-06-04 17:14:35.190733+0200" level=INF msg="An INFO log message" logger=aLogger caller=aCaller"#, + "\n", + )); + + let mut output = Vec::new(); + let app = App::new(options().with_fields(FieldOptions { + settings: Fields { + predefined: settings::PredefinedFields { + level: settings::LevelField { + variants: vec![settings::LevelFieldVariant { + names: vec!["level".to_string()], + values: hashmap! { + Level::Debug => vec!["dbg".to_string()], + Level::Info => vec!["INF".to_string()], + Level::Warning => vec!["wrn".to_string()], + Level::Error => vec!["ERR".to_string()], + }, + level: None, + }], + ..Default::default() + }, + ..Default::default() + }, + ..Default::default() + }, + ..Default::default() + })); + app.run(vec![input], &mut output).unwrap(); + assert_eq!( + std::str::from_utf8(&output).unwrap(), + "2024-06-04 15:14:35.190 |INF| aLogger: An INFO log message @ aCaller\n", + ); + } + fn input>(s: S) -> InputHolder { InputHolder::new(InputReference::Stdin, Some(Box::new(Cursor::new(s.into())))) } diff --git a/src/config.rs b/src/config.rs index ce5d72bc..e2a491d7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -69,7 +69,10 @@ pub mod global { #[cfg(test)] mod tests { use super::*; - use crate::settings::Settings; + + use maplit::hashmap; + + use crate::{level::Level, settings::Settings}; #[test] fn test_default() { @@ -90,6 +93,24 @@ mod tests { assert_eq!(settings.fields.predefined.level.variants.len(), 2); } + #[test] + fn test_issue_288() { + let settings = super::load(Some("src/testing/assets/configs/issue-288.yaml")).unwrap(); + assert_eq!(settings.fields.predefined.level.variants.len(), 1); + let variant = &settings.fields.predefined.level.variants[0]; + assert_eq!(variant.names, vec!["level".to_owned()]); + assert_eq!( + variant.values, + hashmap! { + Level::Debug => vec!["dbg".to_owned()], + // TODO: replace `"inf"` with `"INF"` when https://github.com/mehcode/config-rs/issues/568 is fixed + Level::Info => vec!["inf".to_owned()], + Level::Warning => vec!["wrn".to_owned()], + Level::Error => vec!["ERR".to_owned()], + } + ); + } + #[test] fn test_load_auto() { super::load(None).unwrap(); diff --git a/src/model.rs b/src/model.rs index ce6fb1d7..379a183f 100644 --- a/src/model.rs +++ b/src/model.rs @@ -14,6 +14,7 @@ use chrono::{DateTime, Utc}; use regex::Regex; use serde::de::{Deserialize, Deserializer, MapAccess, SeqAccess, Visitor}; use serde_json::{self as json}; +use titlecase::titlecase; use wildflower::Pattern; // other local crates @@ -525,6 +526,9 @@ impl ParserSettings { for (level, values) in &variant.values { for value in values { mapping.insert(value.clone(), level.clone()); + mapping.insert(value.to_lowercase(), level.clone()); + mapping.insert(value.to_uppercase(), level.clone()); + mapping.insert(titlecase(value), level.clone()); } } let k = self.level.len(); diff --git a/src/testing/assets/configs/issue-288.yaml b/src/testing/assets/configs/issue-288.yaml new file mode 100644 index 00000000..0ae07b30 --- /dev/null +++ b/src/testing/assets/configs/issue-288.yaml @@ -0,0 +1,11 @@ +fields: + predefined: + level: + show: auto + variants: + - names: [level] + values: + debug: [dbg] + info: [INF] + warning: [wrn] + error: [ERR]