diff --git a/rust/src/chrome.rs b/rust/src/chrome.rs index 5e11b3bc10b85..0775f52a308a6 100644 --- a/rust/src/chrome.rs +++ b/rust/src/chrome.rs @@ -25,6 +25,7 @@ use crate::config::ARCH::ARM64; use crate::config::OS::{LINUX, MACOS, WINDOWS}; use crate::downloads::read_content_from_link; use crate::files::{compose_driver_path_in_cache, BrowserPath}; +use crate::logger::Logger; use crate::metadata::{ create_driver_metadata, get_driver_version_from_metadata, get_metadata, write_metadata, }; @@ -44,6 +45,7 @@ pub struct ChromeManager { pub driver_name: &'static str, pub config: ManagerConfig, pub http_client: Client, + pub log: Logger, } impl ChromeManager { @@ -53,6 +55,7 @@ impl ChromeManager { driver_name: DRIVER_NAME, config: ManagerConfig::default(), http_client: create_default_http_client(), + log: Logger::default(), }) } } @@ -150,15 +153,15 @@ impl SeleniumManager for ChromeManager { fn request_driver_version(&self) -> Result> { let browser_version = self.get_browser_version(); - let mut metadata = get_metadata(); + let mut metadata = get_metadata(self.get_logger()); match get_driver_version_from_metadata(&metadata.drivers, self.driver_name, browser_version) { Some(driver_version) => { - log::trace!( + self.log.trace(format!( "Driver TTL is valid. Getting {} version from metadata", &self.driver_name - ); + )); Ok(driver_version) } _ => { @@ -173,7 +176,10 @@ impl SeleniumManager for ChromeManager { if !browser_version.is_empty() && browser_version_int <= 0 { break; } - log::debug!("Reading {} version from {}", &self.driver_name, driver_url); + self.log.debug(format!( + "Reading {} version from {}", + &self.driver_name, driver_url + )); let content = read_content_from_link(self.get_http_client(), driver_url); match content { Ok(version) => { @@ -181,14 +187,14 @@ impl SeleniumManager for ChromeManager { break; } _ => { - log::warn!( + self.log.warn(format!( "Error getting version of {} {}. Retrying with {} {} (attempt {}/{})", &self.driver_name, browser_version_int, &self.driver_name, browser_version_int - 1, i + 1, FALLBACK_RETRIES - ); + )); browser_version_int -= 1; } } @@ -199,7 +205,7 @@ impl SeleniumManager for ChromeManager { self.driver_name, &driver_version, )); - write_metadata(&metadata); + write_metadata(&metadata, self.get_logger()); } Ok(driver_version) } @@ -262,4 +268,12 @@ impl SeleniumManager for ChromeManager { fn set_config(&mut self, config: ManagerConfig) { self.config = config; } + + fn get_logger(&self) -> &Logger { + &self.log + } + + fn set_logger(&mut self, log: Logger) { + self.log = log; + } } diff --git a/rust/src/downloads.rs b/rust/src/downloads.rs index bf206927be459..bfface458c1ca 100644 --- a/rust/src/downloads.rs +++ b/rust/src/downloads.rs @@ -24,18 +24,20 @@ use std::io::Cursor; use tempfile::{Builder, TempDir}; use crate::files::parse_version; +use crate::Logger; #[tokio::main] pub async fn download_driver_to_tmp_folder( http_client: &Client, url: String, + log: &Logger, ) -> Result<(TempDir, String), Box> { let tmp_dir = Builder::new().prefix("selenium-manager").tempdir()?; - log::trace!( + log.trace(format!( "Downloading {} to temporal folder {:?}", url, tmp_dir.path() - ); + )); let response = http_client.get(url).send().await?; let target_path; @@ -47,11 +49,14 @@ pub async fn download_driver_to_tmp_folder( .and_then(|name| if name.is_empty() { None } else { Some(name) }) .unwrap_or("tmp.bin"); - log::trace!("File to be downloaded: {}", target_name); + log.trace(format!("File to be downloaded: {}", target_name)); let target_name = tmp_dir.path().join(target_name); target_path = String::from(target_name.to_str().unwrap()); - log::trace!("Temporal folder for driver package: {}", target_path); + log.trace(format!( + "Temporal folder for driver package: {}", + target_path + )); File::create(target_name)? }; let mut content = Cursor::new(response.bytes().await?); diff --git a/rust/src/edge.rs b/rust/src/edge.rs index 7affeb66fad44..65c5e93c849fb 100644 --- a/rust/src/edge.rs +++ b/rust/src/edge.rs @@ -29,9 +29,9 @@ use crate::metadata::{ create_driver_metadata, get_driver_version_from_metadata, get_metadata, write_metadata, }; use crate::{ - create_default_http_client, SeleniumManager, BETA, DASH_DASH_VERSION, DEV, ENV_LOCALAPPDATA, - ENV_PROGRAM_FILES, ENV_PROGRAM_FILES_X86, NIGHTLY, REG_QUERY, STABLE, WMIC_COMMAND, - WMIC_COMMAND_ENV, + create_default_http_client, Logger, SeleniumManager, BETA, DASH_DASH_VERSION, DEV, + ENV_LOCALAPPDATA, ENV_PROGRAM_FILES, ENV_PROGRAM_FILES_X86, NIGHTLY, REG_QUERY, STABLE, + WMIC_COMMAND, WMIC_COMMAND_ENV, }; const BROWSER_NAME: &str = "edge"; @@ -45,6 +45,7 @@ pub struct EdgeManager { pub driver_name: &'static str, pub config: ManagerConfig, pub http_client: Client, + pub log: Logger, } impl EdgeManager { @@ -54,6 +55,7 @@ impl EdgeManager { driver_name: DRIVER_NAME, config: ManagerConfig::default(), http_client: create_default_http_client(), + log: Logger::default(), }) } } @@ -151,15 +153,15 @@ impl SeleniumManager for EdgeManager { fn request_driver_version(&self) -> Result> { let browser_version = self.get_browser_version(); - let mut metadata = get_metadata(); + let mut metadata = get_metadata(self.get_logger()); match get_driver_version_from_metadata(&metadata.drivers, self.driver_name, browser_version) { Some(driver_version) => { - log::trace!( + self.log.trace(format!( "Driver TTL is valid. Getting {} version from metadata", &self.driver_name - ); + )); Ok(driver_version) } _ => { @@ -174,7 +176,10 @@ impl SeleniumManager for EdgeManager { self.get_os().to_uppercase() ) }; - log::debug!("Reading {} version from {}", &self.driver_name, driver_url); + self.log.debug(format!( + "Reading {} version from {}", + &self.driver_name, driver_url + )); let driver_version = read_content_from_link(self.get_http_client(), driver_url)?; if !browser_version.is_empty() { @@ -183,7 +188,7 @@ impl SeleniumManager for EdgeManager { self.driver_name, &driver_version, )); - write_metadata(&metadata); + write_metadata(&metadata, self.get_logger()); } Ok(driver_version) @@ -249,4 +254,12 @@ impl SeleniumManager for EdgeManager { fn set_config(&mut self, config: ManagerConfig) { self.config = config; } + + fn get_logger(&self) -> &Logger { + &self.log + } + + fn set_logger(&mut self, log: Logger) { + self.log = log; + } } diff --git a/rust/src/files.rs b/rust/src/files.rs index ae270b11e10d9..b8dfe9cd07b6e 100644 --- a/rust/src/files.rs +++ b/rust/src/files.rs @@ -30,6 +30,7 @@ use tar::Archive; use zip::ZipArchive; use crate::config::OS::WINDOWS; +use crate::Logger; const CACHE_FOLDER: &str = ".cache/selenium"; const ZIP: &str = "zip"; @@ -57,20 +58,24 @@ pub fn create_path_if_not_exists(path: &Path) { } } -pub fn uncompress(compressed_file: &String, target: PathBuf) -> Result<(), Box> { +pub fn uncompress( + compressed_file: &String, + target: PathBuf, + log: &Logger, +) -> Result<(), Box> { let file = File::open(compressed_file)?; let kind = infer::get_from_path(compressed_file)? .ok_or(format!("Format for file {:?} cannot be inferred", file))?; let extension = kind.extension(); - log::trace!( + log.trace(format!( "The detected extension of the compressed file is {}", extension - ); + )); if extension.eq_ignore_ascii_case(ZIP) { - unzip(file, target)? + unzip(file, target, log)? } else if extension.eq_ignore_ascii_case(GZ) { - untargz(file, target)? + untargz(file, target, log)? } else if extension.eq_ignore_ascii_case(XML) { return Err("Wrong browser/driver version".into()); } else { @@ -83,8 +88,8 @@ pub fn uncompress(compressed_file: &String, target: PathBuf) -> Result<(), Box Result<(), Box> { - log::trace!("Untargz file to {}", target.display()); +pub fn untargz(file: File, target: PathBuf, log: &Logger) -> Result<(), Box> { + log.trace(format!("Untargz file to {}", target.display())); let tar = GzDecoder::new(&file); let mut archive = Archive::new(tar); let parent_path = target @@ -96,8 +101,8 @@ pub fn untargz(file: File, target: PathBuf) -> Result<(), Box> { Ok(()) } -pub fn unzip(file: File, target: PathBuf) -> Result<(), Box> { - log::trace!("Unzipping file to {}", target.display()); +pub fn unzip(file: File, target: PathBuf, log: &Logger) -> Result<(), Box> { + log.trace(format!("Unzipping file to {}", target.display())); let mut archive = ZipArchive::new(file)?; for i in 0..archive.len() { @@ -107,11 +112,11 @@ pub fn unzip(file: File, target: PathBuf) -> Result<(), Box> { } let target_file_name = target.file_name().unwrap().to_str().unwrap(); if target_file_name.eq_ignore_ascii_case(file.name()) { - log::debug!( + log.debug(format!( "File extracted to {} ({} bytes)", target.display(), file.size() - ); + )); if let Some(p) = target.parent() { create_path_if_not_exists(p); } diff --git a/rust/src/firefox.rs b/rust/src/firefox.rs index ec908af80fedf..b6dbdb9a9c6a7 100644 --- a/rust/src/firefox.rs +++ b/rust/src/firefox.rs @@ -29,8 +29,8 @@ use crate::metadata::{ create_driver_metadata, get_driver_version_from_metadata, get_metadata, write_metadata, }; use crate::{ - create_default_http_client, SeleniumManager, BETA, DASH_VERSION, DEV, ENV_PROGRAM_FILES, - ENV_PROGRAM_FILES_X86, NIGHTLY, STABLE, WMIC_COMMAND, WMIC_COMMAND_ENV, + create_default_http_client, Logger, SeleniumManager, BETA, DASH_VERSION, DEV, + ENV_PROGRAM_FILES, ENV_PROGRAM_FILES_X86, NIGHTLY, STABLE, WMIC_COMMAND, WMIC_COMMAND_ENV, }; const BROWSER_NAME: &str = "firefox"; @@ -43,6 +43,7 @@ pub struct FirefoxManager { pub driver_name: &'static str, pub config: ManagerConfig, pub http_client: Client, + pub log: Logger, } impl FirefoxManager { @@ -52,6 +53,7 @@ impl FirefoxManager { driver_name: DRIVER_NAME, config: ManagerConfig::default(), http_client: create_default_http_client(), + log: Logger::default(), }) } } @@ -141,15 +143,15 @@ impl SeleniumManager for FirefoxManager { fn request_driver_version(&self) -> Result> { let browser_version = self.get_browser_version(); - let mut metadata = get_metadata(); + let mut metadata = get_metadata(self.get_logger()); match get_driver_version_from_metadata(&metadata.drivers, self.driver_name, browser_version) { Some(driver_version) => { - log::trace!( + self.log.trace(format!( "Driver TTL is valid. Getting {} version from metadata", &self.driver_name - ); + )); Ok(driver_version) } _ => { @@ -162,7 +164,7 @@ impl SeleniumManager for FirefoxManager { self.driver_name, &driver_version, )); - write_metadata(&metadata); + write_metadata(&metadata, self.get_logger()); } Ok(driver_version) @@ -248,6 +250,14 @@ impl SeleniumManager for FirefoxManager { fn set_config(&mut self, config: ManagerConfig) { self.config = config; } + + fn get_logger(&self) -> &Logger { + &self.log + } + + fn set_logger(&mut self, log: Logger) { + self.log = log; + } } #[cfg(test)] diff --git a/rust/src/iexplorer.rs b/rust/src/iexplorer.rs index afe247cd01813..d9320962948ac 100644 --- a/rust/src/iexplorer.rs +++ b/rust/src/iexplorer.rs @@ -24,7 +24,7 @@ use std::path::PathBuf; use crate::downloads::read_redirect_from_link; use crate::files::{compose_driver_path_in_cache, BrowserPath}; -use crate::{create_default_http_client, SeleniumManager}; +use crate::{create_default_http_client, Logger, SeleniumManager}; use crate::metadata::{ create_driver_metadata, get_driver_version_from_metadata, get_metadata, write_metadata, @@ -40,6 +40,7 @@ pub struct IExplorerManager { pub driver_name: &'static str, pub config: ManagerConfig, pub http_client: Client, + pub log: Logger, } impl IExplorerManager { @@ -49,6 +50,7 @@ impl IExplorerManager { driver_name: DRIVER_NAME, config: ManagerConfig::default(), http_client: create_default_http_client(), + log: Logger::default(), }) } } @@ -76,15 +78,15 @@ impl SeleniumManager for IExplorerManager { fn request_driver_version(&self) -> Result> { let browser_version = self.get_browser_version(); - let mut metadata = get_metadata(); + let mut metadata = get_metadata(self.get_logger()); match get_driver_version_from_metadata(&metadata.drivers, self.driver_name, browser_version) { Some(driver_version) => { - log::trace!( + self.log.trace(format!( "Driver TTL is valid. Getting {} version from metadata", &self.driver_name - ); + )); Ok(driver_version) } _ => { @@ -97,7 +99,7 @@ impl SeleniumManager for IExplorerManager { self.driver_name, &driver_version, )); - write_metadata(&metadata); + write_metadata(&metadata, self.get_logger()); } Ok(driver_version) @@ -130,4 +132,12 @@ impl SeleniumManager for IExplorerManager { fn set_config(&mut self, config: ManagerConfig) { self.config = config; } + + fn get_logger(&self) -> &Logger { + &self.log + } + + fn set_logger(&mut self, log: Logger) { + self.log = log; + } } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index f99cec9b94944..a04f75f46305f 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -31,6 +31,7 @@ use std::process::Command; use crate::downloads::download_driver_to_tmp_folder; use crate::files::{parse_version, uncompress, BrowserPath}; +use crate::logger::Logger; use crate::metadata::{ create_browser_metadata, get_browser_version_from_metadata, get_metadata, write_metadata, }; @@ -42,6 +43,7 @@ pub mod edge; pub mod files; pub mod firefox; pub mod iexplorer; +pub mod logger; pub mod metadata; pub const STABLE: &str = "stable"; @@ -84,17 +86,22 @@ pub trait SeleniumManager { fn set_config(&mut self, config: ManagerConfig); + fn get_logger(&self) -> &Logger; + + fn set_logger(&mut self, log: Logger); + // ---------------------------------------------------------- // Shared functions // ---------------------------------------------------------- fn download_driver(&self) -> Result<(), Box> { let driver_url = Self::get_driver_url(self)?; - log::debug!("Driver URL: {}", driver_url); + self.get_logger() + .debug(format!("Driver URL: {}", driver_url)); let (_tmp_folder, driver_zip_file) = - download_driver_to_tmp_folder(self.get_http_client(), driver_url)?; + download_driver_to_tmp_folder(self.get_http_client(), driver_url, self.get_logger())?; let driver_path_in_cache = Self::get_driver_path_in_cache(self); - uncompress(&driver_zip_file, driver_path_in_cache) + uncompress(&driver_zip_file, driver_path_in_cache, self.get_logger()) } fn detect_browser_path(&self) -> Option<&str> { @@ -110,19 +117,22 @@ pub trait SeleniumManager { } fn detect_browser_version(&self, shell: &str, flag: &str, args: Vec) -> Option { - let mut metadata = get_metadata(); + let mut metadata = get_metadata(self.get_logger()); let browser_name = &self.get_browser_name(); match get_browser_version_from_metadata(&metadata.browsers, browser_name) { Some(version) => { - log::trace!( + self.get_logger().trace(format!( "Browser with valid TTL. Getting {} version from metadata", browser_name - ); + )); Some(version) } _ => { - log::debug!("Using shell command to find out {} version", browser_name); + self.get_logger().debug(format!( + "Using shell command to find out {} version", + browser_name + )); let mut browser_version = "".to_string(); for arg in args.iter() { let output = match self.run_shell_command(shell, flag, arg.to_string()) { @@ -133,11 +143,10 @@ pub trait SeleniumManager { if full_browser_version.is_empty() { continue; } - log::debug!( + self.get_logger().debug(format!( "The version of {} is {}", - browser_name, - full_browser_version - ); + browser_name, full_browser_version + )); match self.get_major_version(&full_browser_version) { Ok(v) => browser_version = v, Err(_) => return None, @@ -148,7 +157,7 @@ pub trait SeleniumManager { metadata .browsers .push(create_browser_metadata(browser_name, &browser_version)); - write_metadata(&metadata); + write_metadata(&metadata, self.get_logger()); if !browser_version.is_empty() { Some(browser_version) } else { @@ -163,17 +172,21 @@ pub trait SeleniumManager { if browser_version.is_empty() || self.is_browser_version_unstable() { match self.discover_browser_version() { Some(version) => { - log::debug!("Detected browser: {} {}", self.get_browser_name(), version); + self.get_logger().debug(format!( + "Detected browser: {} {}", + self.get_browser_name(), + version + )); self.set_browser_version(version); } None => { if self.is_browser_version_unstable() { return Err(format!("Browser version '{browser_version}' not found")); } else { - log::debug!( + self.get_logger().debug(format!( "The version of {} cannot be detected. Trying with latest driver version", self.get_browser_name() - ); + )); } } } @@ -181,11 +194,11 @@ pub trait SeleniumManager { let driver_version = self .request_driver_version() .unwrap_or_else(|err| err.to_string()); - log::debug!( + self.get_logger().debug(format!( "Required driver: {} {}", self.get_driver_name(), driver_version - ); + )); Ok(driver_version) } @@ -205,11 +218,11 @@ pub trait SeleniumManager { let driver_path = self.get_driver_path_in_cache(); if driver_path.exists() { - log::debug!( + self.get_logger().debug(format!( "{} {} already in the cache", self.get_driver_name(), self.get_driver_version() - ); + )); } else { self.download_driver()?; } @@ -222,9 +235,10 @@ pub trait SeleniumManager { flag: &str, args: String, ) -> Result> { - log::debug!("Running {} command: {:?}", command, args); + self.get_logger() + .debug(format!("Running {} command: {:?}", command, args)); let output = Command::new(command).args([flag, args.as_str()]).output()?; - log::debug!("{:?}", output); + self.get_logger().debug(format!("{:?}", output)); Ok(String::from_utf8_lossy(&output.stdout).to_string()) } @@ -341,16 +355,16 @@ pub fn get_manager_by_driver(driver_name: String) -> Result, +} + +#[derive(Serialize, Deserialize)] +pub struct Logs { + pub level: String, + pub timestamp: u64, + pub message: String, +} + +#[derive(Serialize, Deserialize)] +pub struct Result { + pub code: i32, + pub message: String, +} + +#[derive(Serialize, Deserialize)] +pub struct JsonOutput { + pub logs: Vec, + pub result: Result, +} + +impl Logger { + pub fn default() -> Self { + Logger::create("".to_string(), false, false) + } + + pub fn create(output: String, debug: bool, trace: bool) -> Self { + let output_type; + if output.eq_ignore_ascii_case("json") { + output_type = OutputType::Json; + } else if output.eq_ignore_ascii_case("shell") { + output_type = OutputType::Shell; + } else { + output_type = OutputType::Logger; + } + match output_type { + OutputType::Logger => { + let mut filter = match debug { + true => Debug, + false => Info, + }; + if trace { + filter = Trace + } + env_logger::Builder::new() + .filter_module(env!("CARGO_CRATE_NAME"), filter) + .target(Stdout) + .format(|buf, record| { + let mut level_style = buf.style(); + match record.level() { + Level::Trace => level_style.set_color(Cyan), + Level::Debug => level_style.set_color(Blue), + Level::Info => level_style.set_color(Green), + Level::Warn => level_style.set_color(Yellow), + Level::Error => level_style.set_color(Red).set_bold(true), + }; + writeln!( + buf, + "{}\t{}", + level_style.value(record.level()), + record.args() + ) + }) + .try_init() + .unwrap_or_default(); + } + _ => { + env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")) + .try_init() + .unwrap_or_default(); + } + } + + Logger { + debug, + trace, + output: output_type, + json: RefCell::new(JsonOutput { + logs: Vec::new(), + result: Result { + code: 0, + message: "".to_string(), + }, + }), + } + } + + pub fn error(&self, message: String) { + self.logger(message, Level::Error); + } + + pub fn warn(&self, message: String) { + self.logger(message, Level::Warn); + } + + pub fn info(&self, message: String) { + self.logger(message, Level::Info); + } + + pub fn debug(&self, message: String) { + self.logger(message, Level::Debug); + } + + pub fn trace(&self, message: String) { + self.logger(message, Level::Trace); + } + + fn logger(&self, message: String, level: Level) { + match self.output { + OutputType::Json => { + let trace = level <= Level::Trace && self.trace; + let debug = level <= Level::Debug && self.debug; + let other = level <= Level::Info; + if trace || debug || other { + self.json + .borrow_mut() + .logs + .push(self.create_json_log(message.to_string(), level)); + } + if level == Level::Info || level <= Level::Error { + self.json.borrow_mut().result.message = message; + } + } + OutputType::Shell => { + if level == Level::Info { + print!("{}", message); + } else if level == Level::Error { + eprint!("{}", message); + } + } + _ => { + log::log!(level, "{}", message); + } + } + } + + fn create_json_log(&self, message: String, level: Level) -> Logs { + Logs { + level: level.to_string().to_uppercase(), + timestamp: now_unix_timestamp(), + message, + } + } + + 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.deref()).unwrap()); + } + } +} diff --git a/rust/src/main.rs b/rust/src/main.rs index 812782cf4aa34..a39a7b55ac378 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -16,18 +16,14 @@ // under the License. use std::error::Error; -use std::io::Write; + use std::process::exit; use clap::Parser; -use env_logger::fmt::Color; -use env_logger::Target::Stdout; -use exitcode::DATAERR; -use Color::{Blue, Cyan, Green, Red, Yellow}; -use log::Level; -use log::LevelFilter::{Debug, Info, Trace}; +use exitcode::DATAERR; +use selenium_manager::logger::Logger; use selenium_manager::{ clear_cache, get_manager_by_browser, get_manager_by_driver, SeleniumManager, }; @@ -62,6 +58,10 @@ struct Cli { #[clap(short = 'P', long, value_parser)] browser_path: Option, + /// Output type: LOGGER (using INFO, WARN, etc.), JSON (custom JSON notation), or SHELL (Unix-like) + #[clap(short = 'O', long, value_parser, default_value = "LOGGER")] + output: String, + /// Display DEBUG messages #[clap(short = 'D', long)] debug: bool, @@ -77,10 +77,10 @@ struct Cli { fn main() -> Result<(), Box> { let cli = Cli::parse(); - setup_logging(&cli); + let log = Logger::create(cli.output, cli.debug, cli.trace); if cli.clear_cache { - clear_cache(); + clear_cache(&log); } let browser_name: String = cli.browser.unwrap_or_default(); @@ -88,61 +88,45 @@ fn main() -> Result<(), Box> { let mut selenium_manager: Box = if !browser_name.is_empty() { get_manager_by_browser(browser_name).unwrap_or_else(|err| { - log::error!("{}", err); + log.error(err); + flush_and_exit(DATAERR, &log); exit(DATAERR); }) } else if !driver_name.is_empty() { get_manager_by_driver(driver_name).unwrap_or_else(|err| { - log::error!("{}", err); + log.error(err); + flush_and_exit(DATAERR, &log); exit(DATAERR); }) } else { - log::error!("You need to specify a browser or driver"); + log.error("You need to specify a browser or driver".to_string()); + flush_and_exit(DATAERR, &log); exit(DATAERR); }; + selenium_manager.set_logger(log); selenium_manager.set_browser_version(cli.browser_version.unwrap_or_default()); selenium_manager.set_driver_version(cli.driver_version.unwrap_or_default()); selenium_manager.set_browser_path(cli.browser_path.unwrap_or_default()); match selenium_manager.resolve_driver() { - Ok(driver_path) => log::info!("{}", driver_path.display()), + Ok(driver_path) => { + selenium_manager + .get_logger() + .info(driver_path.display().to_string()); + flush_and_exit(0, selenium_manager.get_logger()); + } Err(err) => { - log::error!("{}", err); - exit(DATAERR); + selenium_manager.get_logger().error(err.to_string()); + flush_and_exit(DATAERR, selenium_manager.get_logger()); } }; Ok(()) } -fn setup_logging(cli: &Cli) { - let mut filter = match cli.debug { - true => Debug, - false => Info, - }; - if cli.trace { - filter = Trace - } - - env_logger::Builder::new() - .filter_level(filter) - .target(Stdout) - .format(|buf, record| { - let mut level_style = buf.style(); - match record.level() { - Level::Trace => level_style.set_color(Cyan), - Level::Debug => level_style.set_color(Blue), - Level::Info => level_style.set_color(Green), - Level::Warn => level_style.set_color(Yellow), - Level::Error => level_style.set_color(Red).set_bold(true), - }; - writeln!( - buf, - "{}\t{}", - level_style.value(record.level()), - record.args() - ) - }) - .init(); +fn flush_and_exit(code: i32, log: &Logger) { + log.set_code(code); + log.flush(); + exit(code); } diff --git a/rust/src/metadata.rs b/rust/src/metadata.rs index 645ca11b159df..7e3dfe64af696 100644 --- a/rust/src/metadata.rs +++ b/rust/src/metadata.rs @@ -23,6 +23,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use serde::{Deserialize, Serialize}; use crate::files::get_cache_folder; +use crate::Logger; const METADATA_FILE: &str = "selenium-manager.json"; const TTL_BROWSERS_SEC: u64 = 0; @@ -53,24 +54,24 @@ fn get_metadata_path() -> PathBuf { get_cache_folder().join(METADATA_FILE) } -fn now_unix_timestamp() -> u64 { +pub fn now_unix_timestamp() -> u64 { SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs() } -fn new_metadata() -> Metadata { - log::trace!("Metadata file does not exist. Creating a new one"); +fn new_metadata(log: &Logger) -> Metadata { + log.trace("Metadata file does not exist. Creating a new one".to_string()); Metadata { browsers: Vec::new(), drivers: Vec::new(), } } -pub fn get_metadata() -> Metadata { +pub fn get_metadata(log: &Logger) -> Metadata { let metadata_path = get_cache_folder().join(METADATA_FILE); - log::trace!("Reading metadata from {}", metadata_path.display()); + log.trace(format!("Reading metadata from {}", metadata_path.display())); if metadata_path.exists() { let metadata_file = File::open(&metadata_path).unwrap(); @@ -81,11 +82,11 @@ pub fn get_metadata() -> Metadata { meta.drivers.retain(|d| d.driver_ttl > now); meta } - Err(_e) => new_metadata(), + Err(_e) => new_metadata(log), }; metadata } else { - new_metadata() + new_metadata(log) } } @@ -141,9 +142,9 @@ pub fn create_driver_metadata( } } -pub fn write_metadata(metadata: &Metadata) { +pub fn write_metadata(metadata: &Metadata, log: &Logger) { let metadata_path = get_metadata_path(); - log::trace!("Writing metadata to {}", metadata_path.display()); + log.trace(format!("Writing metadata to {}", metadata_path.display())); fs::write( metadata_path, serde_json::to_string_pretty(metadata).unwrap(), diff --git a/rust/tests/output_tests.rs b/rust/tests/output_tests.rs new file mode 100644 index 0000000000000..517a846ab41e9 --- /dev/null +++ b/rust/tests/output_tests.rs @@ -0,0 +1,60 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use assert_cmd::Command; +use std::path::Path; + +use selenium_manager::logger::JsonOutput; +use std::str; + +#[test] +fn json_output_test() { + let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap(); + cmd.args(["--browser", "chrome", "--output", "json"]) + .assert() + .success() + .code(0); + + let stdout = &cmd.unwrap().stdout; + let output = str::from_utf8(stdout).unwrap(); + println!("{}", output); + + let json: JsonOutput = serde_json::from_str(output).unwrap(); + assert!(!json.logs.is_empty()); + + let output_code = json.result.code; + assert_eq!(output_code, 0); + + let driver = Path::new(&json.result.message); + assert!(driver.exists()); +} + +#[test] +fn shell_output_test() { + let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap(); + cmd.args(["--browser", "chrome", "--output", "shell"]) + .assert() + .success() + .code(0); + + let stdout = &cmd.unwrap().stdout; + let output = str::from_utf8(stdout).unwrap(); + println!("{}", output); + + let driver = Path::new(output); + assert!(driver.exists()); +} \ No newline at end of file