From 383d1ed423463d81a717119d724dac4323c97ddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boni=20Garc=C3=ADa?= Date: Fri, 17 May 2024 20:26:10 +0200 Subject: [PATCH] [rust] Include mixed output (INFO, WARN, DEBUG, etc. to stderr and minimal JSON to stdout) (#13414) * [rust] Include mixed output (INFO, WARN, DEBUG, etc. to stderr and minimal JSON to stdout) * [rust] Include field for error message in the minimal JSON * [rust] Change field message to error * [rust] Fix logger module --------- Co-authored-by: Diego Molina --- rust/src/logger.rs | 76 ++++++++++++++++++++++++++++++-------- rust/src/main.rs | 3 +- rust/tests/grid_tests.rs | 4 +- rust/tests/output_tests.rs | 26 ++++++++++++- 4 files changed, 88 insertions(+), 21 deletions(-) diff --git a/rust/src/logger.rs b/rust/src/logger.rs index ed80e8fd01130..36c154001bc37 100644 --- a/rust/src/logger.rs +++ b/rust/src/logger.rs @@ -17,7 +17,8 @@ use crate::config::{BooleanKey, StringKey}; use crate::metadata::now_unix_timestamp; -use env_logger::Target::Stdout; + +use env_logger::Target::{Stderr, Stdout}; use env_logger::DEFAULT_FILTER_ENV; use log::LevelFilter::{Debug, Info, Trace}; use log::{Level, LevelFilter}; @@ -25,7 +26,6 @@ use serde::{Deserialize, Serialize}; use std::cell::RefCell; use std::env; use std::fmt::Display; -use std::ops::Deref; use std::str::FromStr; pub const DRIVER_PATH: &str = "Driver path: "; @@ -37,6 +37,7 @@ enum OutputType { Logger, Json, Shell, + Mixed, } #[derive(Default)] @@ -45,6 +46,7 @@ pub struct Logger { trace: bool, output: OutputType, json: RefCell, + minimal_json: RefCell, } #[derive(Default, Serialize, Deserialize)] @@ -68,6 +70,13 @@ pub struct JsonOutput { pub result: Result, } +#[derive(Default, Serialize, Deserialize)] +pub struct MinimalJson { + pub driver_path: String, + pub browser_path: String, + pub error: String, +} + impl Logger { pub fn new() -> Self { let debug = BooleanKey("debug", false).get_value(); @@ -82,13 +91,15 @@ impl Logger { output_type = OutputType::Json; } else if output.eq_ignore_ascii_case("shell") { output_type = OutputType::Shell; + } else if output.eq_ignore_ascii_case("mixed") { + output_type = OutputType::Mixed; } else { output_type = OutputType::Logger; } match output_type { - OutputType::Logger => { + OutputType::Logger | OutputType::Mixed => { if env::var(DEFAULT_FILTER_ENV).unwrap_or_default().is_empty() { - let filter = if !log_level.is_empty() { + let mut filter = if !log_level.is_empty() { LevelFilter::from_str(log_level).unwrap_or(Info) } else { let mut filter = match debug { @@ -100,9 +111,17 @@ impl Logger { } filter }; + if trace { + filter = Trace + } + let target = if output_type == OutputType::Logger { + Stdout + } else { + Stderr + }; env_logger::Builder::new() .filter_module(env!("CARGO_CRATE_NAME"), filter) - .target(Stdout) + .target(target) .format_target(false) .format_timestamp_millis() .try_init() @@ -131,6 +150,7 @@ impl Logger { browser_path: "".to_string(), }, }), + minimal_json: RefCell::new(Default::default()), } } @@ -173,12 +193,11 @@ impl Logger { } if level == Level::Info || level <= Level::Error { if message.starts_with(DRIVER_PATH) { - let driver_path = message.replace(DRIVER_PATH, ""); - self.json.borrow_mut().result.driver_path = driver_path.to_owned(); - self.json.borrow_mut().result.message = driver_path; + self.json.borrow_mut().result.driver_path = + self.clean_driver_path(&message); } else if message.starts_with(BROWSER_PATH) { - let browser_path = message.replace(BROWSER_PATH, ""); - self.json.borrow_mut().result.browser_path = browser_path; + self.json.borrow_mut().result.browser_path = + self.clean_browser_path(&message); } else { self.json.borrow_mut().result.message = message; } @@ -192,11 +211,31 @@ impl Logger { } } _ => { + if self.output == OutputType::Mixed && level == Level::Info || level <= Level::Error + { + if message.starts_with(DRIVER_PATH) { + self.minimal_json.borrow_mut().driver_path = + self.clean_driver_path(&message); + } else if message.starts_with(BROWSER_PATH) { + self.minimal_json.borrow_mut().browser_path = + self.clean_browser_path(&message); + } else { + self.minimal_json.borrow_mut().error = message.clone(); + } + } log::log!(level, "{}", message); } } } + fn clean_driver_path(&self, message: &str) -> String { + message.replace(DRIVER_PATH, "") + } + + fn clean_browser_path(&self, message: &str) -> String { + message.replace(BROWSER_PATH, "") + } + fn create_json_log(&self, message: String, level: Level) -> Logs { Logs { level: level.to_string().to_uppercase(), @@ -205,17 +244,22 @@ impl Logger { } } + fn get_json_blog(&self, json_output: &T) -> String + where + T: Serialize, + { + serde_json::to_string_pretty(json_output).unwrap() + } + pub fn set_code(&self, code: i32) { self.json.borrow_mut().result.code = code; } pub fn flush(&self) { - let json_output = &self.json.borrow(); - let json = json_output.deref(); - if !json.logs.is_empty() { - print!("{}", serde_json::to_string_pretty(json).unwrap()); - } else if self.output == OutputType::Json { - panic!("JSON output has been specified, but no entries have been collected") + if self.output == OutputType::Json { + print!("{}", self.get_json_blog(&self.json)); + } else if self.output == OutputType::Mixed { + print!("{}", self.get_json_blog(&self.minimal_json)); } } } diff --git a/rust/src/main.rs b/rust/src/main.rs index 9de4c6a90f443..f47b8c83854c6 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -77,7 +77,8 @@ struct Cli { #[clap(long, value_parser)] browser_mirror_url: Option, - /// Output type: LOGGER (using INFO, WARN, etc.), JSON (custom JSON notation), or SHELL (Unix-like) + /// Output type: LOGGER (using INFO, WARN, etc.), JSON (custom JSON notation), SHELL (Unix-like), + /// or MIXED (INFO, WARN, DEBUG, etc. to stderr and minimal JSON to stdout) #[clap(long, value_parser, default_value = "LOGGER")] output: String, diff --git a/rust/tests/grid_tests.rs b/rust/tests/grid_tests.rs index 632ef4e52a2f4..99bbc590d53e3 100644 --- a/rust/tests/grid_tests.rs +++ b/rust/tests/grid_tests.rs @@ -43,7 +43,7 @@ fn grid_latest_test() { let output_code = json.result.code; assert_eq!(output_code, 0); - let jar = Path::new(&json.result.message); + let jar = Path::new(&json.result.driver_path); assert!(jar.exists()); let jar_name = jar.file_name().unwrap().to_str().unwrap(); @@ -66,7 +66,7 @@ fn grid_version_test(#[case] grid_version: &str) { println!("{}", output); let json: JsonOutput = serde_json::from_str(output).unwrap(); - let jar = Path::new(&json.result.message); + let jar = Path::new(&json.result.driver_path); let jar_name = jar.file_name().unwrap().to_str().unwrap(); assert!(jar_name.contains(grid_version)); } diff --git a/rust/tests/output_tests.rs b/rust/tests/output_tests.rs index b8e7b90a428a9..d01e74d1e71fe 100644 --- a/rust/tests/output_tests.rs +++ b/rust/tests/output_tests.rs @@ -16,7 +16,7 @@ // under the License. use assert_cmd::Command; -use selenium_manager::logger::{JsonOutput, DRIVER_PATH}; +use selenium_manager::logger::{JsonOutput, MinimalJson, DRIVER_PATH}; use std::path::Path; use std::str; @@ -38,7 +38,7 @@ fn json_output_test() { let output_code = json.result.code; assert_eq!(output_code, 0); - let driver = Path::new(&json.result.message); + let driver = Path::new(&json.result.driver_path); assert!(driver.exists()); } @@ -55,3 +55,25 @@ fn shell_output_test() { println!("{}", output); assert!(output.contains(DRIVER_PATH)); } + +#[test] +fn mixed_output_test() { + let mut cmd = Command::new(env!("CARGO_BIN_EXE_selenium-manager")); + cmd.args(["--browser", "chrome", "--output", "mixed"]) + .assert() + .success() + .code(0); + + let stdout = &cmd.unwrap().stdout; + let output = str::from_utf8(stdout).unwrap(); + println!("stdout: {}", output); + + let json: MinimalJson = serde_json::from_str(output).unwrap(); + let driver = Path::new(&json.driver_path); + assert!(driver.exists()); + + let stderr = &cmd.unwrap().stderr; + let err_output = str::from_utf8(stderr).unwrap(); + println!("stderr: {}", err_output); + assert!(!err_output.is_empty()); +}