diff --git a/crates/main/Cargo.toml b/crates/main/Cargo.toml index e33be48..892f12e 100644 --- a/crates/main/Cargo.toml +++ b/crates/main/Cargo.toml @@ -26,6 +26,7 @@ serde = "=1.0.118" [dev-dependencies] simple_logger = "1.6.0" # used in doc test lazy_static = "1.4.0" # used in doc test +maplit = "1.0.2" [features] # Print some internal logs by log_utf8_string diff --git a/crates/main/src/logger.rs b/crates/main/src/logger.rs index 58bf059..e462711 100644 --- a/crates/main/src/logger.rs +++ b/crates/main/src/logger.rs @@ -43,9 +43,10 @@ //! [`log`]: https://docs.rs/log use log::Level as LogLevel; +use std::collections::HashMap; /// By default, logger will be initialized with log level from this environment variable. -pub const WASM_LOG_ENV_NAME: &'static str = "WASM_LOG"; +pub const WASM_LOG_ENV_NAME: &str = "WASM_LOG"; /// If WASM_LOG_ENV isn't set, then this level will be used as the default. const WASM_DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Info; @@ -54,7 +55,10 @@ const WASM_DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Info; /// TODO: use i64 for bitmask when wasmpack/bindgen issue with i64 is fixed. /// Currently, i64 doesn't work on some versions of V8 because log_utf8_string function /// isn't marked as #[wasm_bindgen]. In result, TS/JS code throws 'TypeError' on every log. -pub type TargetMap = std::collections::HashMap<&'static str, i32>; +pub type TargetMap = HashMap<&'static str, i32>; + +/// Mapping from module name to their log levels. +type ModuleMap = HashMap; /// The Wasm Logger. /// @@ -68,14 +72,15 @@ pub type TargetMap = std::collections::HashMap<&'static str, i32>; /// [`Log`]: https://docs.rs/log/0.4.11/log/trait.Log.html struct WasmLogger { target_map: TargetMap, + modules_level: ModuleMap, + default_log_level: log::Level, } /// The Wasm logger builder. /// /// Build logger for the Fluence network, allows specifying target map and log level while building. pub struct WasmLoggerBuilder { - target_map: TargetMap, - log_level: log::Level, + wasm_logger: WasmLogger, } impl WasmLoggerBuilder { @@ -85,27 +90,37 @@ impl WasmLoggerBuilder { pub fn new() -> Self { use std::str::FromStr; - let log_level = std::env::var(WASM_LOG_ENV_NAME) + let default_log_level = std::env::var(WASM_LOG_ENV_NAME) .map_or(WASM_DEFAULT_LOG_LEVEL, |log_level_str| { LogLevel::from_str(&log_level_str).unwrap_or(WASM_DEFAULT_LOG_LEVEL) }); - Self { - log_level, - target_map: <_>::default(), - } + let wasm_logger = WasmLogger { + target_map: HashMap::new(), + modules_level: HashMap::new(), + default_log_level, + }; + + Self { wasm_logger } } /// Set the log level. pub fn with_log_level(mut self, level: log::Level) -> Self { - self.log_level = level; + self.wasm_logger.default_log_level = level; self } /// Set mapping between logging targets and numbers. /// Used to efficiently enable & disable logs per target on the host. pub fn with_target_map(mut self, map: TargetMap) -> Self { - self.target_map = map; + self.wasm_logger.target_map = map; + self + } + + pub fn filter(mut self, module_name: impl Into, level: log::Level) -> Self { + self.wasm_logger + .modules_level + .insert(module_name.into(), level); self } @@ -127,15 +142,9 @@ impl WasmLoggerBuilder { /// # } /// ``` pub fn build(self) -> Result<(), log::SetLoggerError> { - let Self { - log_level, - target_map, - } = self; - - let wasm_logger = WasmLogger { target_map }; + let Self { wasm_logger } = self; log::set_boxed_logger(Box::new(wasm_logger))?; - log::set_max_level(log_level.to_level_filter()); Ok(()) } } @@ -143,7 +152,12 @@ impl WasmLoggerBuilder { impl log::Log for WasmLogger { #[inline] fn enabled(&self, metadata: &log::Metadata<'_>) -> bool { - metadata.level() <= log::max_level() + let allowed_level = match self.modules_level.get(metadata.target()) { + Some(allowed_level) => allowed_level, + None => &self.default_log_level, + }; + + metadata.level() <= *allowed_level } #[inline] @@ -204,3 +218,70 @@ fn level_from_i32(level: i32) -> log::Level { _ => log::Level::max(), } } + +#[cfg(test)] +mod tests { + use super::WasmLogger; + use log::Log; + + use std::collections::HashMap; + + fn create_metadata(module_name: &str, level: log::Level) -> log::Metadata<'_> { + log::MetadataBuilder::new() + .level(level) + .target(module_name) + .build() + } + + #[test] + fn enabled_by_module_name() { + let module_1_name = "module_1"; + let module_2_name = "module_2"; + + let modules_level = maplit::hashmap!( + module_1_name.to_string() => log::Level::Info, + module_2_name.to_string() => log::Level::Warn, + ); + + let logger = WasmLogger { + target_map: HashMap::new(), + modules_level, + default_log_level: log::Level::Error, + }; + + let allowed_metadata = create_metadata(module_1_name, log::Level::Info); + assert!(logger.enabled(&allowed_metadata)); + + let allowed_metadata = create_metadata(module_1_name, log::Level::Warn); + assert!(logger.enabled(&allowed_metadata)); + + let allowed_metadata = create_metadata(module_2_name, log::Level::Warn); + assert!(logger.enabled(&allowed_metadata)); + + let not_allowed_metadata = create_metadata(module_1_name, log::Level::Debug); + assert!(!logger.enabled(¬_allowed_metadata)); + + let not_allowed_metadata = create_metadata(module_2_name, log::Level::Info); + assert!(!logger.enabled(¬_allowed_metadata)); + } + + #[test] + fn default_log_level() { + let modules_level = maplit::hashmap!( + "module_1".to_string() => log::Level::Info, + ); + + let logger = WasmLogger { + target_map: HashMap::new(), + modules_level, + default_log_level: log::Level::Warn, + }; + + let module_name = "some_module"; + let allowed_metadata = create_metadata(module_name, log::Level::Warn); + assert!(logger.enabled(&allowed_metadata)); + + let not_allowed_metadata = create_metadata(module_name, log::Level::Info); + assert!(!logger.enabled(¬_allowed_metadata)); + } +} diff --git a/crates/main/src/mounted_binary.rs b/crates/main/src/mounted_binary.rs index 47ea4be..e016189 100644 --- a/crates/main/src/mounted_binary.rs +++ b/crates/main/src/mounted_binary.rs @@ -51,7 +51,7 @@ impl Result { /// Return true, if this Result represents a success result, otherwise false. pub fn is_success(&self) -> bool { - return self.ret_code == SUCCESS_CODE; + self.ret_code == SUCCESS_CODE } /// This function tries to transform a result to the string representation.