diff --git a/.bazelrc b/.bazelrc index 05e4a21c..5dce79d5 100644 --- a/.bazelrc +++ b/.bazelrc @@ -25,12 +25,11 @@ build --tool_java_language_version=17 build --java_runtime_version=remotejdk_17 build --tool_java_runtime_version=remotejdk_17 +build --@score_baselibs_rust//src/log:safety_level=qm build --@score_baselibs//score/json:base_library=nlohmann build --@score_baselibs//score/mw/log/flags:KRemote_Logging=False -# ToDo: needed for "wrong" implicit dependencies -build --@score-baselibs//score/json:base_library=nlohmann -build --@score-baselibs//score/mw/log/flags:KRemote_Logging=False +common --extra_toolchains=@gcc_toolchain//:host_gcc_12 # Clippy linting (enabled by default) build --aspects=@rules_rust//rust:defs.bzl%rust_clippy_aspect diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 812f04b3..4b0dd10f 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -38,7 +38,7 @@ jobs: - name: Cargo Clippy run: | - cargo clippy --all-targets --all-features + cargo clippy --all-targets --features stdout_logger -- -D warnings - name: Cargo Fmt run: | diff --git a/BUILD b/BUILD index f967eb61..b312fcbf 100644 --- a/BUILD +++ b/BUILD @@ -20,8 +20,8 @@ load("//:project_config.bzl", "PROJECT_CONFIG") # - `:docs` for building documentation at build-time docs( data = [ - "@score_platform//:needs_json", - "@score_process//:needs_json", + # "@score_platform//:needs_json", + # "@score_process//:needs_json", ], source_dir = "docs", ) diff --git a/Cargo.lock b/Cargo.lock index 1489a6d2..d1bc52a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,9 +74,9 @@ checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "log" -version = "0.4.27" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" @@ -84,6 +84,31 @@ version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +[[package]] +name = "mw_log" +version = "0.0.1" +source = "git+https://github.com/eclipse-score/baselibs_rust.git?rev=54e2af036603107039a2eca4912413763ab112e7#54e2af036603107039a2eca4912413763ab112e7" +dependencies = [ + "mw_log_fmt", + "mw_log_fmt_macro", +] + +[[package]] +name = "mw_log_fmt" +version = "0.0.1" +source = "git+https://github.com/eclipse-score/baselibs_rust.git?rev=54e2af036603107039a2eca4912413763ab112e7#54e2af036603107039a2eca4912413763ab112e7" + +[[package]] +name = "mw_log_fmt_macro" +version = "0.0.1" +source = "git+https://github.com/eclipse-score/baselibs_rust.git?rev=54e2af036603107039a2eca4912413763ab112e7#54e2af036603107039a2eca4912413763ab112e7" +dependencies = [ + "mw_log_fmt", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "nu-ansi-term" version = "0.50.1" @@ -140,6 +165,7 @@ name = "rust_kvs" version = "0.1.0" dependencies = [ "adler32", + "mw_log", "tempfile", "tinyjson", ] @@ -148,8 +174,10 @@ dependencies = [ name = "rust_kvs_tool" version = "0.1.0" dependencies = [ + "mw_log", "pico-args", "rust_kvs", + "stdout_logger", "tinyjson", ] @@ -157,9 +185,11 @@ dependencies = [ name = "rust_test_scenarios" version = "0.1.0" dependencies = [ + "mw_log", "rust_kvs", "serde", "serde_json", + "stdout_logger", "test_scenarios_rust", "tinyjson", "tracing", @@ -232,6 +262,14 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "stdout_logger" +version = "0.0.1" +source = "git+https://github.com/eclipse-score/baselibs_rust.git?rev=54e2af036603107039a2eca4912413763ab112e7#54e2af036603107039a2eca4912413763ab112e7" +dependencies = [ + "mw_log", +] + [[package]] name = "syn" version = "2.0.104" diff --git a/Cargo.toml b/Cargo.toml index bcbd8227..cbd4f20e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,9 @@ edition = "2021" [workspace.dependencies] rust_kvs = { path = "src/rust/rust_kvs" } rust_kvs_tool = { path = "src/rust/rust_kvs_tool" } +mw_log = { git = "https://github.com/eclipse-score/baselibs_rust.git", rev = "54e2af036603107039a2eca4912413763ab112e7", features = [ + "qm", +] } adler32 = "1.2.0" tinyjson = "2.5.1" diff --git a/MODULE.bazel b/MODULE.bazel index cde0e717..36c3bb0b 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -46,7 +46,7 @@ bazel_dep(name = "score_rust_policies", version = "0.0.2", dev_dependency = True rust = use_extension("@rules_rust//rust:extensions.bzl", "rust", dev_dependency = True) rust.toolchain( edition = "2021", - versions = ["1.85.0"], + versions = ["1.90.0"], ) # bazel cc rules @@ -85,7 +85,16 @@ bazel_dep(name = "google_benchmark", version = "1.9.4", dev_dependency = True) bazel_dep(name = "platforms", version = "1.0.0") ## S-CORE bazel registry -bazel_dep(name = "score_baselibs", version = "0.1.2") +bazel_dep(name = "score_baselibs", version = "0.2.2") +bazel_dep(name = "score_baselibs_rust", version = "0.0.1") + +# TODO: replace with stable version. +git_override( + module_name = "score_baselibs_rust", + commit = "54e2af036603107039a2eca4912413763ab112e7", + remote = "https://github.com/eclipse-score/baselibs_rust.git", +) + bazel_dep(name = "score_bazel_platforms", version = "0.0.2") bazel_dep(name = "score_docs_as_code", version = "2.2.0") @@ -103,6 +112,13 @@ git_override( remote = "https://github.com/eclipse-score/tooling.git", ) +bazel_dep(name = "score_logging", version = "0.0.3") +git_override( + module_name = "score_logging", + commit = "cd7528ce86c6eb5215a53dbbd3cdc761451e5a4b", + remote = "https://github.com/eclipse-score/logging.git", +) + # ToDo: implicit dependencies for score_tooling, but needed directly here?? bazel_dep(name = "aspect_rules_lint", version = "1.10.2") bazel_dep(name = "buildifier_prebuilt", version = "8.2.0.2") @@ -152,6 +168,22 @@ archive_override( ], ) +# TODO: remove once inherited properly from `score_logging`. +bazel_dep(name = "trlc") +git_override( + module_name = "trlc", + commit = "650b51a47264a4f232b3341f473527710fc32669", # trlc-2.0.2 release + remote = "https://github.com/bmw-software-engineering/trlc.git", +) + +# TODO: remove once inherited properly from `score_logging`. +bazel_dep(name = "score_communication", version = "0.1.2") +git_override( + module_name = "score_communication", + commit = "5a70133dd8bd632f5c07f200a5ee4bc9f507c23b", + remote = "https://github.com/eclipse-score/communication.git", +) + # Registers the custom Rust toolchain wired to @qnx_rust register_toolchains( "@score_toolchains_rust//toolchains/aarch64-unknown-qnx8_0:toolchain_aarch64_qnx8_0", diff --git a/src/rust/rust_kvs/BUILD b/src/rust/rust_kvs/BUILD index 9682b384..ec2d0b3f 100644 --- a/src/rust/rust_kvs/BUILD +++ b/src/rust/rust_kvs/BUILD @@ -17,6 +17,7 @@ rust_library( srcs = glob(["src/**/*.rs"]), visibility = ["//visibility:public"], deps = [ + "@score_baselibs_rust//src/log/mw_log", "@score_crates//:adler32", "@score_crates//:tinyjson", ], diff --git a/src/rust/rust_kvs/Cargo.toml b/src/rust/rust_kvs/Cargo.toml index bf0be609..016ecc4f 100644 --- a/src/rust/rust_kvs/Cargo.toml +++ b/src/rust/rust_kvs/Cargo.toml @@ -7,6 +7,7 @@ edition.workspace = true [dependencies] adler32.workspace = true tinyjson.workspace = true +mw_log.workspace = true [dev-dependencies] diff --git a/src/rust/rust_kvs/src/error_code.rs b/src/rust/rust_kvs/src/error_code.rs index ff842527..9f6a0be7 100644 --- a/src/rust/rust_kvs/src/error_code.rs +++ b/src/rust/rust_kvs/src/error_code.rs @@ -11,11 +11,12 @@ extern crate alloc; +use crate::log::{error, ScoreDebug}; use alloc::string::FromUtf8Error; use core::array::TryFromSliceError; /// Runtime Error Codes -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, ScoreDebug)] pub enum ErrorCode { /// Error that was not yet mapped UnmappedError, @@ -93,7 +94,7 @@ impl From for ErrorCode { match kind { std::io::ErrorKind::NotFound => ErrorCode::FileNotFound, _ => { - eprintln!("error: unmapped error: {kind}"); + error!("Unmapped IO error: {:?}", kind.to_string()); ErrorCode::UnmappedError } } @@ -102,21 +103,21 @@ impl From for ErrorCode { impl From for ErrorCode { fn from(cause: FromUtf8Error) -> Self { - eprintln!("error: UTF-8 conversion failed: {cause:#?}"); + error!("Conversion from UTF-8 failed: {:#?}", cause); ErrorCode::ConversionFailed } } impl From for ErrorCode { fn from(cause: TryFromSliceError) -> Self { - eprintln!("error: try_into from slice failed: {cause:#?}"); + error!("Conversion from slice failed: {:#?}", cause); ErrorCode::ConversionFailed } } impl From> for ErrorCode { fn from(cause: Vec) -> Self { - eprintln!("error: try_into from u8 vector failed: {cause:#?}"); + error!("Conversion from vector of u8 failed: {:#?}", cause); ErrorCode::ConversionFailed } } diff --git a/src/rust/rust_kvs/src/json_backend.rs b/src/rust/rust_kvs/src/json_backend.rs index 736d7432..ef1678ca 100644 --- a/src/rust/rust_kvs/src/json_backend.rs +++ b/src/rust/rust_kvs/src/json_backend.rs @@ -13,6 +13,7 @@ use crate::error_code::ErrorCode; use crate::kvs_api::{InstanceId, SnapshotId}; use crate::kvs_backend::KvsBackend; use crate::kvs_value::{KvsMap, KvsValue}; +use crate::log::{debug, error, trace, ScoreDebug}; use std::collections::HashMap; use std::fs; use std::path::{Path, PathBuf}; @@ -133,8 +134,8 @@ impl From for JsonValue { /// tinyjson::JsonParseError -> ErrorCode::JsonParseError impl From for ErrorCode { fn from(cause: JsonParseError) -> Self { - eprintln!( - "error: JSON parser error: line = {}, column = {}", + error!( + "JSON parser error: line = {}, column = {}", cause.line(), cause.column() ); @@ -145,7 +146,7 @@ impl From for ErrorCode { /// tinyjson::JsonGenerateError -> ErrorCode::JsonGenerateError impl From for ErrorCode { fn from(cause: JsonGenerateError) -> Self { - eprintln!("error: JSON generator error: msg = {}", cause.message()); + error!("JSON generator error: msg = {}", cause.message()); ErrorCode::JsonGeneratorError } } @@ -171,12 +172,14 @@ impl JsonBackendBuilder { /// Set the working directory used by the JSON backend. pub fn working_dir(mut self, working_dir: PathBuf) -> Self { + trace!("\"working_dir\" set to {:?}", working_dir); self.working_dir = working_dir; self } /// Set max number of snapshots. pub fn snapshot_max_count(mut self, snapshot_max_count: usize) -> Self { + trace!("\"snapshot_max_count\" set to {:?}", snapshot_max_count); self.snapshot_max_count = snapshot_max_count; self } @@ -197,7 +200,7 @@ impl Default for JsonBackendBuilder { } /// KVS backend implementation based on TinyJSON. -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq, ScoreDebug)] pub struct JsonBackend { working_dir: PathBuf, snapshot_max_count: usize, @@ -242,6 +245,7 @@ impl JsonBackend { // In other case - this is erroneous scenario. // Either snapshot or hash file got removed. else if !snap_old_exists || !hash_old_exists { + error!("KVS or hash file not found"); return Err(ErrorCode::IntegrityCorrupted); } @@ -250,7 +254,7 @@ impl JsonBackend { let snap_name_new = Self::kvs_file_name(instance_id, new_snapshot_id); let snap_path_new = self.kvs_file_path(instance_id, new_snapshot_id); - println!("rotating: {snap_name_old} -> {snap_name_new}"); + debug!("Rotating snapshots: {} -> {}", snap_name_old, snap_name_new); fs::rename(hash_path_old, hash_path_new)?; fs::rename(snap_path_old, snap_path_new)?; @@ -266,10 +270,15 @@ impl JsonBackend { ext.is_some_and(|ep| ep.to_str().is_some_and(|es| es == extension)) } + debug!("Checking KVS file path: {:?}", kvs_path); if !check_extension(kvs_path, "json") { + error!("Invalid KVS file path extension: {:?}", kvs_path); return Err(ErrorCode::KvsFileReadError); } + + debug!("Checking hash file path: {:?}", hash_path); if !check_extension(hash_path, "hash") { + error!("Invalid hash file path extension: {:?}", hash_path); return Err(ErrorCode::KvsHashFileReadError); } @@ -280,13 +289,24 @@ impl JsonBackend { Self::check_path_extensions(kvs_path, hash_path)?; // Load KVS file. - let json_str = fs::read_to_string(kvs_path)?; + debug!("Loading KVS file: {:?}", kvs_path); + let json_str = fs::read_to_string(kvs_path).inspect_err(|_| { + error!("Failed to load KVS file: {:?}", kvs_path); + })?; // Load hash file. - let hash_bytes = fs::read(hash_path)?; + debug!("Loading hash file: {:?}", hash_path); + let hash_bytes = fs::read(hash_path).inspect_err(|_| { + error!("Failed to load hash file: {:?}", hash_path); + })?; // Perform hash check. + debug!( + "Performing hash check, KVS file: {:?}, hash file: {:?}", + kvs_path, hash_path + ); if hash_bytes.len() != 4 { + error!("Invalid hash length: {:?}", hash_path); return Err(ErrorCode::ValidationFailed); } @@ -295,17 +315,26 @@ impl JsonBackend { let hash_kvs = adler32::RollingAdler32::from_buffer(json_str.as_bytes()).hash(); if hash_kvs != file_hash { + error!( + "Hash mismatch, KVS file: {:?}, hash file: {:?}", + kvs_path, hash_path + ); return Err(ErrorCode::ValidationFailed); } // Parse KVS from string to `JsonValue`. - let json_value = Self::parse(&json_str)?; + debug!("Parsing KVS file: {:?}", kvs_path); + let json_value = Self::parse(&json_str).inspect_err(|_| { + error!("Failed to parse KVS file: {:?}", kvs_path); + })?; // Cast from `JsonValue` to `KvsValue`. + debug!("Converting JSON values to KVS values"); let kvs_value = KvsValue::from(json_value); if let KvsValue::Object(kvs_map) = kvs_value { Ok(kvs_map) } else { + error!("Conversion from JSON to KVS failed"); Err(ErrorCode::JsonParserError) } } @@ -318,16 +347,30 @@ impl JsonBackend { Self::check_path_extensions(kvs_path, hash_path)?; // Cast from `KvsValue` to `JsonValue`. + debug!("Converting KVS values to JSON values"); let kvs_value = KvsValue::Object(kvs_map.clone()); let json_value = JsonValue::from(kvs_value); // Stringify `JsonValue` and save to KVS file. - let json_str = Self::stringify(&json_value)?; - fs::write(kvs_path, &json_str)?; + debug!("Stringifying KVS file: {:?}", kvs_path); + let json_str = Self::stringify(&json_value).inspect_err(|_| { + error!("Failed to stringify KVS file content: {:?}", kvs_path); + })?; + + debug!("Saving KVS file: {:?}", kvs_path); + fs::write(kvs_path, &json_str).inspect_err(|_| { + error!("Failed to save KVS file: {:?}", kvs_path); + })?; // Generate hash and save to hash file. + debug!( + "Generating KVS hash, KVS file: {:?}, hash file: {:?}", + kvs_path, hash_path + ); let hash = adler32::RollingAdler32::from_buffer(json_str.as_bytes()).hash(); - fs::write(hash_path, hash.to_be_bytes())?; + fs::write(hash_path, hash.to_be_bytes()).inspect_err(|_| { + error!("Failed to save hash file: {:?}", hash_path); + })?; Ok(()) } @@ -394,16 +437,14 @@ impl KvsBackend for JsonBackend { } fn flush(&self, instance_id: InstanceId, kvs_map: &KvsMap) -> Result<(), ErrorCode> { - self.snapshot_rotate(instance_id).map_err(|e| { - eprintln!("error: snapshot_rotate failed: {e:?}"); - e + self.snapshot_rotate(instance_id).inspect_err(|e| { + error!("Failed to rotate snapshots: {:?}", e); })?; let snapshot_id = SnapshotId(0); let kvs_path = self.kvs_file_path(instance_id, snapshot_id); let hash_path = self.hash_file_path(instance_id, snapshot_id); - Self::save(kvs_map, &kvs_path, &hash_path).map_err(|e| { - eprintln!("error: save failed: {e:?}"); - e + Self::save(kvs_map, &kvs_path, &hash_path).inspect_err(|e| { + error!("Failed to save snapshot: {:?}", e); })?; Ok(()) } @@ -433,18 +474,20 @@ impl KvsBackend for JsonBackend { instance_id: InstanceId, snapshot_id: SnapshotId, ) -> Result { - // fail if the snapshot ID is the current KVS + // Fail if the snapshot ID is the current KVS. if snapshot_id == SnapshotId(0) { - eprintln!("error: tried to restore current KVS as snapshot"); + error!("Restoring current KVS snapshot is not allowed"); return Err(ErrorCode::InvalidSnapshotId); } if self.snapshot_count(instance_id) < snapshot_id.0 { - eprintln!("error: tried to restore a non-existing snapshot"); + error!("Unable to restore non-existing snapshot"); return Err(ErrorCode::InvalidSnapshotId); } - self.load_kvs(instance_id, snapshot_id) + self.load_kvs(instance_id, snapshot_id).inspect_err(|e| { + error!("Failed to load snapshot: {:?}", e); + }) } } diff --git a/src/rust/rust_kvs/src/kvs.rs b/src/rust/rust_kvs/src/kvs.rs index e7bfae8d..d82968f7 100644 --- a/src/rust/rust_kvs/src/kvs.rs +++ b/src/rust/rust_kvs/src/kvs.rs @@ -14,9 +14,11 @@ use crate::kvs_api::{InstanceId, KvsApi, KvsDefaults, KvsLoad, SnapshotId}; use crate::kvs_backend::KvsBackend; use crate::kvs_builder::KvsData; use crate::kvs_value::{KvsMap, KvsValue}; +use crate::log::{error, warn, ScoreDebug}; use std::sync::{Arc, Mutex}; /// KVS instance parameters. +#[derive(ScoreDebug)] pub struct KvsParameters { /// Instance ID. pub instance_id: InstanceId, @@ -75,7 +77,7 @@ impl KvsApi for Kvs { fn reset_key(&self, key: &str) -> Result<(), ErrorCode> { let mut data = self.data.lock()?; if !data.defaults_map.contains_key(key) { - eprintln!("error: resetting key without a default value"); + error!("Resetting key without a default value: {}", key); return Err(ErrorCode::KeyDefaultNotFound); } @@ -126,7 +128,7 @@ impl KvsApi for Kvs { } else if let Some(value) = data.defaults_map.get(key) { Ok(value.clone()) } else { - eprintln!("error: get_value could not find key: {key}"); + error!("Key not found: {}", key); Err(ErrorCode::KeyNotFound) } } @@ -149,17 +151,15 @@ impl KvsApi for Kvs { /// * `ErrorCode::KeyNotFound`: Key wasn't found in KVS nor in defaults fn get_value_as(&self, key: &str) -> Result where - for<'a> T: TryFrom<&'a KvsValue> + core::clone::Clone, - for<'a> >::Error: core::fmt::Debug, + for<'a> T: TryFrom<&'a KvsValue>, + for<'a> >::Error: ScoreDebug, { let data = self.data.lock()?; if let Some(value) = data.kvs_map.get(key) { match T::try_from(value) { Ok(value) => Ok(value), Err(err) => { - eprintln!( - "error: get_value could not convert KvsValue from KVS store: {err:#?}" - ); + error!("Failed to convert KVS value: {:#?}", err); Err(ErrorCode::ConversionFailed) } } @@ -168,15 +168,12 @@ impl KvsApi for Kvs { match T::try_from(value) { Ok(value) => Ok(value), Err(err) => { - eprintln!( - "error: get_value could not convert KvsValue from default store: {err:#?}" - ); + error!("Failed to convert default value: {:#?}", err); Err(ErrorCode::ConversionFailed) } } } else { - eprintln!("error: get_value could not find key: {key}"); - + error!("Key not found: {}", key); Err(ErrorCode::KeyNotFound) } } @@ -198,6 +195,7 @@ impl KvsApi for Kvs { if let Some(value) = data.defaults_map.get(key) { Ok(value.clone()) } else { + error!("Key not found: {}", key); Err(ErrorCode::KeyNotFound) } } @@ -222,6 +220,7 @@ impl KvsApi for Kvs { } else if data.defaults_map.contains_key(key) { Ok(true) } else { + error!("Key not found: {}", key); Err(ErrorCode::KeyNotFound) } } @@ -259,6 +258,7 @@ impl KvsApi for Kvs { if data.kvs_map.remove(key).is_some() { Ok(()) } else { + error!("Key not found: {}", key); Err(ErrorCode::KeyNotFound) } } @@ -278,7 +278,7 @@ impl KvsApi for Kvs { /// * `ErrorCode::UnmappedError`: Unmapped error fn flush(&self) -> Result<(), ErrorCode> { if self.snapshot_max_count() == 0 { - eprintln!("warn: snapshot_max_count == 0, flush ignored"); + warn!("snapshot_max_count == 0, flush ignored"); return Ok(()); } @@ -343,12 +343,13 @@ mod kvs_tests { use crate::kvs_backend::KvsBackend; use crate::kvs_builder::KvsData; use crate::kvs_value::{KvsMap, KvsValue}; + use crate::log::ScoreDebug; use std::sync::{Arc, Mutex}; use tempfile::tempdir; /// Most tests can be performed with mocked backend. /// Only those with file handling must use concrete implementation. - #[derive(PartialEq)] + #[derive(PartialEq, Debug, ScoreDebug)] struct MockBackend; impl KvsBackend for MockBackend { diff --git a/src/rust/rust_kvs/src/kvs_api.rs b/src/rust/rust_kvs/src/kvs_api.rs index 84b507c6..0ba1ab78 100644 --- a/src/rust/rust_kvs/src/kvs_api.rs +++ b/src/rust/rust_kvs/src/kvs_api.rs @@ -11,14 +11,14 @@ use crate::error_code::ErrorCode; use crate::kvs_value::KvsValue; -use core::fmt; +use crate::log::ScoreDebug; /// Instance ID -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, ScoreDebug)] pub struct InstanceId(pub usize); -impl fmt::Display for InstanceId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl core::fmt::Display for InstanceId { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { write!(f, "{}", self.0) } } @@ -30,11 +30,11 @@ impl From for usize { } /// Snapshot ID -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, ScoreDebug)] pub struct SnapshotId(pub usize); -impl fmt::Display for SnapshotId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl core::fmt::Display for SnapshotId { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { write!(f, "{}", self.0) } } @@ -46,7 +46,7 @@ impl From for usize { } /// Defaults handling mode. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, ScoreDebug)] pub enum KvsDefaults { /// Defaults are not loaded. Ignored, @@ -59,7 +59,7 @@ pub enum KvsDefaults { } /// KVS load mode. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, ScoreDebug)] pub enum KvsLoad { /// KVS is not loaded. Ignored, @@ -79,8 +79,8 @@ pub trait KvsApi { fn get_value(&self, key: &str) -> Result; fn get_value_as(&self, key: &str) -> Result where - for<'a> T: TryFrom<&'a KvsValue> + Clone, - for<'a> >::Error: core::fmt::Debug; + for<'a> T: TryFrom<&'a KvsValue>, + for<'a> >::Error: ScoreDebug; fn get_default_value(&self, key: &str) -> Result; fn is_value_default(&self, key: &str) -> Result; fn set_value, J: Into>( diff --git a/src/rust/rust_kvs/src/kvs_backend.rs b/src/rust/rust_kvs/src/kvs_backend.rs index f55c1adf..89860d13 100644 --- a/src/rust/rust_kvs/src/kvs_backend.rs +++ b/src/rust/rust_kvs/src/kvs_backend.rs @@ -12,6 +12,7 @@ use crate::error_code::ErrorCode; use crate::kvs_api::{InstanceId, SnapshotId}; use crate::kvs_value::KvsMap; +use crate::log::ScoreDebug; use core::any::Any; /// Trait for comparisons between types. @@ -40,7 +41,7 @@ where } /// KVS backend interface. -pub trait KvsBackend: DynEq + Sync + Send { +pub trait KvsBackend: DynEq + Sync + Send + ScoreDebug { /// Load KVS content. fn load_kvs( &self, diff --git a/src/rust/rust_kvs/src/kvs_builder.rs b/src/rust/rust_kvs/src/kvs_builder.rs index a38e8a3d..9f9cff0a 100644 --- a/src/rust/rust_kvs/src/kvs_builder.rs +++ b/src/rust/rust_kvs/src/kvs_builder.rs @@ -15,6 +15,7 @@ use crate::kvs::{Kvs, KvsParameters}; use crate::kvs_api::{InstanceId, KvsDefaults, KvsLoad, SnapshotId}; use crate::kvs_backend::KvsBackend; use crate::kvs_value::KvsMap; +use crate::log::{debug, error, info, trace, ScoreDebug}; use std::sync::{Arc, LazyLock, Mutex, MutexGuard, PoisonError}; /// Maximum number of instances. @@ -31,7 +32,8 @@ pub(crate) struct KvsData { } impl From>> for ErrorCode { - fn from(_cause: PoisonError>) -> Self { + fn from(cause: PoisonError>) -> Self { + error!("KVS data lock failed: {:?}", cause); ErrorCode::MutexLockFailed } } @@ -49,12 +51,14 @@ static KVS_POOL: LazyLock; KVS_MAX_INSTANCES]>> = LazyLock::new(|| Mutex::new([const { None }; KVS_MAX_INSTANCES])); impl From; KVS_MAX_INSTANCES]>>> for ErrorCode { - fn from(_cause: PoisonError; KVS_MAX_INSTANCES]>>) -> Self { + fn from(cause: PoisonError; KVS_MAX_INSTANCES]>>) -> Self { + error!("KVS instance pool lock failed: {:?}", cause); ErrorCode::MutexLockFailed } } /// Key-value-storage builder. +#[derive(ScoreDebug)] pub struct KvsBuilder { /// Instance ID. instance_id: InstanceId, @@ -105,6 +109,7 @@ impl KvsBuilder { /// # Return Values /// * KvsBuilder instance pub fn defaults(mut self, mode: KvsDefaults) -> Self { + trace!("\"defaults\" set to {:?}", mode); self.defaults = Some(mode); self } @@ -117,6 +122,7 @@ impl KvsBuilder { /// # Return Values /// * KvsBuilder instance pub fn kvs_load(mut self, mode: KvsLoad) -> Self { + trace!("\"kvs_load\" set to {:?}", mode); self.kvs_load = Some(mode); self } @@ -138,17 +144,17 @@ impl KvsBuilder { fn compare_parameters(&self, other: &KvsParameters) -> bool { // Compare instance ID. if self.instance_id != other.instance_id { - eprintln!("error: instance ID mismatched"); + error!("Instance ID mismatched"); false } // Compare defaults handling mode. else if self.defaults.is_some_and(|v| v != other.defaults) { - eprintln!("error: defaults handling mode mismatched"); + error!("Defaults handling mode mismatched"); false } // Compare KVS load mode. else if self.kvs_load.is_some_and(|v| v != other.kvs_load) { - eprintln!("error: KVS load mode mismatched"); + error!("KVS load mode mismatched"); false } // Compare backend. @@ -157,7 +163,7 @@ impl KvsBuilder { .as_ref() .is_some_and(|v| !v.dyn_eq(other.backend.as_any())) { - eprintln!("error: backend parameters mismatched"); + error!("Backend parameters mismatched"); false } // Success. @@ -186,24 +192,35 @@ impl KvsBuilder { let instance_id = self.instance_id; let instance_id_index: usize = instance_id.into(); + debug!("Requested KVS instance with ID: {}", instance_id); + // Check if instance already exists. { + debug!("Checking for existing KVS instance in instance pool"); let kvs_pool = KVS_POOL.lock()?; let kvs_inner_option = match kvs_pool.get(instance_id_index) { Some(kvs_pool_entry) => match kvs_pool_entry { // If instance exists then parameters must match. Some(kvs_inner) => { if self.compare_parameters(&kvs_inner.parameters) { + debug!("Using KVS instance from instance pool"); Ok(Some(kvs_inner)) } else { + error!("Requested KVS instance parameters mismatch, provided: {:?}, available: {:?}", self, kvs_inner.parameters); Err(ErrorCode::InstanceParametersMismatch) } } // Instance not found - not an error, will initialize later. - None => Ok(None), + None => { + debug!("KVS instance not found in instance pool"); + Ok(None) + } }, // Instance ID out of range. - None => Err(ErrorCode::InvalidInstanceId), + None => { + error!("Provided instance ID is out of range: {}", instance_id); + Err(ErrorCode::InvalidInstanceId) + } }?; // Return existing instance if initialized. @@ -226,6 +243,7 @@ impl KvsBuilder { }; // Load defaults. + debug!("Loading defaults"); let defaults_map = match parameters.defaults { KvsDefaults::Ignored => KvsMap::new(), KvsDefaults::Optional => match parameters.backend.load_defaults(instance_id) { @@ -239,6 +257,7 @@ impl KvsBuilder { }; // Load KVS and hash files. + debug!("Loading KVS data"); let snapshot_id = SnapshotId(0); let kvs_map = match parameters.kvs_load { KvsLoad::Ignored => KvsMap::new(), @@ -263,10 +282,15 @@ impl KvsBuilder { // Initialize entry in pool and return new KVS instance. { + debug!("Initializing instance pool entry"); let mut kvs_pool = KVS_POOL.lock()?; let kvs_pool_entry = match kvs_pool.get_mut(instance_id_index) { Some(entry) => entry, - None => return Err(ErrorCode::InvalidInstanceId), + None => { + // Unlikely - this was checked previously. + error!("Provided instance ID is out of range: {}", instance_id); + return Err(ErrorCode::InvalidInstanceId); + } }; let _ = kvs_pool_entry.insert(KvsInner { @@ -275,6 +299,7 @@ impl KvsBuilder { }); } + info!("KVS instance initialized: {:?}", parameters.clone()); Ok(Kvs::new(data, parameters)) } } diff --git a/src/rust/rust_kvs/src/kvs_mock.rs b/src/rust/rust_kvs/src/kvs_mock.rs index 23a5aa20..656cb7bc 100644 --- a/src/rust/rust_kvs/src/kvs_mock.rs +++ b/src/rust/rust_kvs/src/kvs_mock.rs @@ -12,6 +12,7 @@ use crate::error_code::ErrorCode; use crate::kvs_api::{KvsApi, SnapshotId}; use crate::kvs_value::{KvsMap, KvsValue}; +use crate::log::ScoreDebug; use std::sync::{Arc, Mutex}; #[derive(Clone)] @@ -79,8 +80,8 @@ impl KvsApi for MockKvs { } fn get_value_as(&self, key: &str) -> Result where - for<'a> T: TryFrom<&'a KvsValue> + Clone, - for<'a> >::Error: core::fmt::Debug, + for<'a> T: TryFrom<&'a KvsValue>, + for<'a> >::Error: ScoreDebug, { if self.fail { return Err(ErrorCode::UnmappedError); diff --git a/src/rust/rust_kvs/src/kvs_value.rs b/src/rust/rust_kvs/src/kvs_value.rs index 294797c1..a7af3c00 100644 --- a/src/rust/rust_kvs/src/kvs_value.rs +++ b/src/rust/rust_kvs/src/kvs_value.rs @@ -9,6 +9,7 @@ // // SPDX-License-Identifier: Apache-2.0 +use crate::log::ScoreDebug; use core::convert::TryFrom; use std::collections::HashMap; @@ -16,7 +17,7 @@ use std::collections::HashMap; pub type KvsMap = HashMap; /// Key-value-storage value -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, ScoreDebug)] pub enum KvsValue { /// 32-bit signed integer I32(i32), diff --git a/src/rust/rust_kvs/src/lib.rs b/src/rust/rust_kvs/src/lib.rs index bdba28a6..8e997141 100644 --- a/src/rust/rust_kvs/src/lib.rs +++ b/src/rust/rust_kvs/src/lib.rs @@ -140,6 +140,7 @@ pub mod kvs_builder; pub mod kvs_mock; pub mod kvs_serialize; pub mod kvs_value; +mod log; /// Prelude module for convenient imports pub mod prelude { diff --git a/src/rust/rust_kvs/src/log.rs b/src/rust/rust_kvs/src/log.rs new file mode 100644 index 00000000..142b9bf3 --- /dev/null +++ b/src/rust/rust_kvs/src/log.rs @@ -0,0 +1,64 @@ +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// +// +// SPDX-License-Identifier: Apache-2.0 + +//! Logging module. +//! Utilizes `"PERS"` context by default. + +#![allow(unused_macros)] + +pub(crate) const CONTEXT: &str = "PERS"; + +/// Proxy for `mw_log::fatal!`. +#[clippy::format_args] +macro_rules! fatal { + ($($arg:tt)+) => (mw_log::fatal!(context: $crate::log::CONTEXT, $($arg)+)); +} + +/// Proxy for `mw_log::error!`. +#[clippy::format_args] +macro_rules! error { + ($($arg:tt)+) => (mw_log::error!(context: $crate::log::CONTEXT, $($arg)+)); +} + +/// Proxy for `mw_log::warn!`. +#[clippy::format_args] +macro_rules! warning { + ($($arg:tt)+) => (mw_log::warn!(context: $crate::log::CONTEXT, $($arg)+)); +} + +/// Proxy for `mw_log::info!`. +#[clippy::format_args] +macro_rules! info { + ($($arg:tt)+) => (mw_log::info!(context: $crate::log::CONTEXT, $($arg)+)); +} + +/// Proxy for `mw_log::debug!`. +#[clippy::format_args] +macro_rules! debug { + ($($arg:tt)+) => (mw_log::debug!(context: $crate::log::CONTEXT, $($arg)+)); +} + +/// Proxy for `mw_log::trace!`. +#[clippy::format_args] +macro_rules! trace { + ($($arg:tt)+) => (mw_log::trace!(context: $crate::log::CONTEXT, $($arg)+)); +} + +// Export macros from this module (e.g., `crate::log::error`). +// `#[macro_export]` would export them from crate (e.g., `crate::error`). +// +// `warning as warn` is due to `warn` macro name conflicting with `warn` attribute. +#[allow(unused_imports)] +pub(crate) use {debug, error, fatal, info, trace, warning as warn}; + +// Re-export symbols from `mw_log`. +pub(crate) use mw_log::fmt::ScoreDebug; +pub(crate) use mw_log::ScoreDebug; diff --git a/src/rust/rust_kvs_tool/BUILD b/src/rust/rust_kvs_tool/BUILD index 5e84e158..b1c2a018 100644 --- a/src/rust/rust_kvs_tool/BUILD +++ b/src/rust/rust_kvs_tool/BUILD @@ -17,11 +17,19 @@ rust_binary( srcs = [ "src/kvs_tool.rs", ], + crate_features = ["mw_logger"], crate_name = "rust_kvs_tool", + rustc_flags = [ + "-Clink-arg=-lstdc++", + "-Clink-arg=-lm", + "-Clink-arg=-lc", + ], visibility = ["//visibility:public"], deps = [ "//src/rust/rust_kvs", + "@score_baselibs_rust//src/log/mw_log", "@score_crates//:pico_args", "@score_crates//:tinyjson", + "@score_logging//src/rust/mw_logger", ], ) diff --git a/src/rust/rust_kvs_tool/Cargo.toml b/src/rust/rust_kvs_tool/Cargo.toml index 28900e3a..54e6bb85 100644 --- a/src/rust/rust_kvs_tool/Cargo.toml +++ b/src/rust/rust_kvs_tool/Cargo.toml @@ -13,6 +13,13 @@ path = "src/kvs_tool.rs" rust_kvs.workspace = true tinyjson.workspace = true pico-args.workspace = true +mw_log.workspace = true +stdout_logger = { git = "https://github.com/eclipse-score/baselibs_rust.git", rev = "54e2af036603107039a2eca4912413763ab112e7", optional = true } + + +[features] +default = ["stdout_logger"] +mw_logger = [] [lints] diff --git a/src/rust/rust_kvs_tool/src/kvs_tool.rs b/src/rust/rust_kvs_tool/src/kvs_tool.rs index 8c8cb011..0f4b11cf 100644 --- a/src/rust/rust_kvs_tool/src/kvs_tool.rs +++ b/src/rust/rust_kvs_tool/src/kvs_tool.rs @@ -73,6 +73,7 @@ //! ``` //! +use mw_log::error; use pico_args::Arguments; use rust_kvs::prelude::*; use std::collections::HashMap; @@ -126,21 +127,19 @@ fn _getkey(kvs: Kvs, mut args: Arguments) -> Result<(), ErrorCode> { Ok(None) | Err(_) => match args.opt_value_from_str("-k") { Ok(Some(val)) => val, _ => { - eprintln!("Error: Key (-k or --key) needs to be specified!"); + error!("Key (-k or --key) needs to be specified!"); return Err(ErrorCode::UnmappedError); } }, }; println!("Read Key {}", &key); - let key_exist = kvs.key_exists(&key).map_err(|e| { - eprintln!("KVS get:key_exists failed: {e:?}"); - e + let key_exist = kvs.key_exists(&key).inspect_err(|e| { + error!("KVS get:key_exists failed: {:?}", e); })?; - let is_default = kvs.is_value_default(&key).map_err(|e| { - eprintln!("KVS get:is_value_default failed: {e:?}"); - e + let is_default = kvs.is_value_default(&key).inspect_err(|e| { + error!("KVS get:is_value_default failed: {:?}", e); })?; if key_exist { @@ -150,7 +149,7 @@ fn _getkey(kvs: Kvs, mut args: Arguments) -> Result<(), ErrorCode> { println!("Key Value: {value:?}"); } Err(e) => { - eprintln!("Get Key Error: {e:?}"); + error!("Get Key Error: {:?}", e); } }; } else { @@ -168,7 +167,7 @@ fn _getkey(kvs: Kvs, mut args: Arguments) -> Result<(), ErrorCode> { println!("Default Value: {value:?}"); } Err(e) => { - eprintln!("Default Value Error: {e:?}"); + error!("Default Value Error: {:?}", e); } }; @@ -189,7 +188,7 @@ fn _setkey(kvs: Kvs, mut args: Arguments) -> Result<(), ErrorCode> { Ok(None) | Err(_) => match args.opt_value_from_str("-k") { Ok(Some(val)) => val, _ => { - eprintln!("Error: Key (-k or --key) needs to be specified!"); + error!("Key (-k or --key) needs to be specified!"); return Err(ErrorCode::UnmappedError); } }, @@ -208,22 +207,20 @@ fn _setkey(kvs: Kvs, mut args: Arguments) -> Result<(), ErrorCode> { if let Ok(json_val) = value.parse::() { let kvs_val = from_tinyjson(&json_val); println!("Key:'{}' \nParsed as JSON Value: {:?}", &key, kvs_val); - kvs.set_value(key, kvs_val).map_err(|e| { - eprintln!("KVS set failed: {e:?}"); - e + kvs.set_value(key, kvs_val).inspect_err(|e| { + error!("KVS set failed: {:?}", e); })?; } else { println!("Key:'{}' \nParsed as String Value: {}", &key, value); - kvs.set_value(key, KvsValue::String(value)).map_err(|e| { - eprintln!("KVS set failed: {e:?}"); - e - })?; + kvs.set_value(key, KvsValue::String(value)) + .inspect_err(|e| { + error!("KVS set failed: {:?}", e); + })?; } } None => { - kvs.set_value(key, KvsValue::Null).map_err(|e| { - eprintln!("KVS set failed: {e:?}"); - e + kvs.set_value(key, KvsValue::Null).inspect_err(|e| { + error!("KVS set failed: {:?}", e); })?; } } @@ -240,15 +237,14 @@ fn _removekey(kvs: Kvs, mut args: Arguments) -> Result<(), ErrorCode> { Ok(None) | Err(_) => match args.opt_value_from_str("-k") { Ok(Some(val)) => val, _ => { - eprintln!("Error: Key (-k or --key) needs to be specified!"); + error!("Key (-k or --key) needs to be specified!"); return Err(ErrorCode::UnmappedError); } }, }; println!("Remove Key {}", &key); - kvs.remove_key(&key).map_err(|e| { - eprintln!("KVS remove failed: {e:?}"); - e + kvs.remove_key(&key).inspect_err(|e| { + error!("KVS remove failed: {:?}", e); })?; kvs.flush()?; println!("----------------------"); @@ -260,9 +256,8 @@ fn _removekey(kvs: Kvs, mut args: Arguments) -> Result<(), ErrorCode> { fn _listkeys(kvs: Kvs) -> Result<(), ErrorCode> { println!("----------------------"); println!("List Keys"); - let keys = kvs.get_all_keys().map_err(|e| { - eprintln!("KVS list failed: {e:?}"); - e + let keys = kvs.get_all_keys().inspect_err(|e| { + error!("KVS list failed: {:?}", e); })?; for key in keys { @@ -277,9 +272,8 @@ fn _listkeys(kvs: Kvs) -> Result<(), ErrorCode> { fn _reset(kvs: Kvs) -> Result<(), ErrorCode> { println!("----------------------"); println!("Reset KVS"); - kvs.reset().map_err(|e| { - eprintln!("KVS set failed: {e:?}"); - e + kvs.reset().inspect_err(|e| { + error!("KVS set failed: {:?}", e); })?; kvs.flush()?; println!("----------------------"); @@ -317,16 +311,15 @@ fn _snapshotrestore(kvs: Kvs, mut args: Arguments) -> Result<(), ErrorCode> { Ok(None) | Err(_) => match args.opt_value_from_str("-s") { Ok(Some(val)) => val, _ => { - eprintln!("Error: Snapshot ID (-s or --snapshotid) needs to be specified!"); + error!("Snapshot ID (-s or --snapshotid) needs to be specified!"); return Err(ErrorCode::UnmappedError); } }, }; println!("Restore Snapshot {}", &snapshot_id); let snapshot_id = SnapshotId(snapshot_id as usize); - kvs.snapshot_restore(snapshot_id).map_err(|e| { - eprintln!("KVS restore failed: {e:?}"); - e + kvs.snapshot_restore(snapshot_id).inspect_err(|e| { + error!("KVS restore failed: {:?}", e); })?; kvs.flush()?; println!("----------------------"); @@ -338,7 +331,7 @@ fn _downcast_backend(kvs: &Kvs) -> Result<&JsonBackend, ErrorCode> { match kvs.parameters().backend.as_any().downcast_ref() { Some(backend) => Ok(backend), None => { - eprintln!("Invalid backend type"); + error!("Invalid backend type"); Err(ErrorCode::UnmappedError) } } @@ -353,7 +346,7 @@ fn _getkvsfilename(kvs: Kvs, mut args: Arguments) -> Result<(), ErrorCode> { Ok(None) | Err(_) => match args.opt_value_from_str("-s") { Ok(Some(val)) => val, _ => { - eprintln!("Error: Snapshot ID (-s or --snapshotid) needs to be specified!"); + error!("Snapshot ID (-s or --snapshotid) needs to be specified!"); return Err(ErrorCode::UnmappedError); } }, @@ -377,7 +370,7 @@ fn _gethashfilename(kvs: Kvs, mut args: Arguments) -> Result<(), ErrorCode> { Ok(None) | Err(_) => match args.opt_value_from_str("-s") { Ok(Some(val)) => val, _ => { - eprintln!("Error: Snapshot ID (-s or --snapshotid) needs to be specified!"); + error!("Snapshot ID (-s or --snapshotid) needs to be specified!"); return Err(ErrorCode::UnmappedError); } }, @@ -396,21 +389,18 @@ fn _createtestdata(kvs: Kvs) -> Result<(), ErrorCode> { println!("----------------------"); println!("Create Test Data"); - kvs.set_value("number", 123.0).map_err(|e| { - eprintln!("KVS Create Test Data Error (number): {e:?}"); - e - })?; - kvs.set_value("bool", true).map_err(|e| { - eprintln!("KVS Create Test Data Error (bool): {e:?}"); - e + kvs.set_value("number", 123.0).inspect_err(|e| { + error!("KVS Create Test Data Error (number): {:?}", e); })?; - kvs.set_value("string", "First".to_string()).map_err(|e| { - eprintln!("KVS Create Test Data Error (string): {e:?}"); - e + kvs.set_value("bool", true).inspect_err(|e| { + error!("KVS Create Test Data Error (bool): {:?}", e); })?; - kvs.set_value("null", ()).map_err(|e| { - eprintln!("KVS Create Test Data Error (null): {e:?}"); - e + kvs.set_value("string", "First".to_string()) + .inspect_err(|e| { + error!("KVS Create Test Data Error (string): {:?}", e); + })?; + kvs.set_value("null", ()).inspect_err(|e| { + error!("KVS Create Test Data Error (null): {:?}", e); })?; kvs.set_value( "array", @@ -420,9 +410,8 @@ fn _createtestdata(kvs: Kvs) -> Result<(), ErrorCode> { "Second".to_string().into(), ], ) - .map_err(|e| { - eprintln!("KVS Create Test Data Error (array): {e:?}"); - e + .inspect_err(|e| { + error!("KVS Create Test Data Error (array): {:?}", e); })?; kvs.set_value( "object", @@ -441,9 +430,8 @@ fn _createtestdata(kvs: Kvs) -> Result<(), ErrorCode> { ), ]), ) - .map_err(|e| { - eprintln!("KVS Create Test Data Error (object): {e:?}"); - e + .inspect_err(|e| { + error!("KVS Create Test Data Error (object): {:?}", e); })?; kvs.flush()?; println!("Done!"); @@ -451,10 +439,20 @@ fn _createtestdata(kvs: Kvs) -> Result<(), ErrorCode> { Ok(()) } +fn init_logging() { + #[cfg(feature = "stdout_logger")] + stdout_logger::StdoutLoggerBuilder::new().set_as_default_logger(); + + #[cfg(feature = "mw_logger")] + mw_logger::MwLoggerBuilder::new().set_as_default_logger(); +} + /// Main function to run the KVS tool command line interface. fn main() -> Result<(), ErrorCode> { let mut args = Arguments::from_env(); + init_logging(); + if args.contains(["-h", "--help"]) { const HELP: &str = r#" @@ -545,7 +543,7 @@ fn main() -> Result<(), ErrorCode> { let kvs = match builder.build() { Ok(kvs) => kvs, Err(e) => { - eprintln!("Error opening KVS: {e:?}"); + error!("Error opening KVS: {:?}", e); return Err(e); } }; @@ -555,9 +553,7 @@ fn main() -> Result<(), ErrorCode> { Ok(None) | Err(_) => match args.opt_value_from_str("-o") { Ok(Some(val)) => Some(val), _ => { - eprintln!( - "Error: No operation specified. Use -o or --operation followed by a value." - ); + error!("No operation specified. Use -o or --operation followed by a value."); return Err(ErrorCode::UnmappedError); } }, @@ -626,9 +622,7 @@ fn main() -> Result<(), ErrorCode> { Ok(()) } OperationMode::Invalid => { - println!("----------------------"); - eprintln!("Invalid operation specified. Use -o or --operation to specify a valid operation. (See -h or --help for more information)"); - println!("----------------------"); + error!("Invalid operation specified. Use -o or --operation to specify a valid operation. (See -h or --help for more information)"); Err(ErrorCode::UnmappedError) } } diff --git a/tests/python_test_cases/config/logging.json b/tests/python_test_cases/config/logging.json new file mode 100644 index 00000000..bd60e5cf --- /dev/null +++ b/tests/python_test_cases/config/logging.json @@ -0,0 +1,7 @@ +{ + "appId": "RUTS", + "appDesc": "Rust test scenarios", + "logMode": "kConsole", + "logLevel": "kVerbose", + "logLevelThresholdConsole": "kInfo" +} \ No newline at end of file diff --git a/tests/python_test_cases/pytest.ini b/tests/python_test_cases/pytest.ini index 46f32015..879529cc 100644 --- a/tests/python_test_cases/pytest.ini +++ b/tests/python_test_cases/pytest.ini @@ -23,4 +23,6 @@ markers = DerivationTechnique ; Additional environment variables -env = D:RUST_BACKTRACE = 1 +env = + D:RUST_BACKTRACE = 1 + D:MW_LOG_CONFIG_FILE = config/logging.json diff --git a/tests/python_test_cases/tests/test_cit_default_values.py b/tests/python_test_cases/tests/test_cit_default_values.py index 879d0f6d..6e33256b 100644 --- a/tests/python_test_cases/tests/test_cit_default_values.py +++ b/tests/python_test_cases/tests/test_cit_default_values.py @@ -321,7 +321,7 @@ def test_invalid( assert defaults_file is not None assert results.return_code == ResultCode.PANIC assert results.stderr is not None - pattern = r'error: file ".*" could not be read: JsonParserError' + pattern = r'file ".*" could not be read: JsonParserError' assert re.findall(pattern, results.stderr) is not None @@ -360,7 +360,7 @@ def test_config(self, temp_dir: Path, defaults: str) -> dict[str, Any]: def test_invalid(self, results: ScenarioResult) -> None: assert results.return_code == ResultCode.PANIC assert results.stderr is not None - pattern = r'error: file ".*" could not be read: KvsFileReadError' + pattern = r'file ".*" could not be read: KvsFileReadError' assert re.findall(pattern, results.stderr) is not None diff --git a/tests/python_test_cases/tests/test_cit_snapshots.py b/tests/python_test_cases/tests/test_cit_snapshots.py index 30a10faa..5b715d0f 100644 --- a/tests/python_test_cases/tests/test_cit_snapshots.py +++ b/tests/python_test_cases/tests/test_cit_snapshots.py @@ -203,9 +203,6 @@ def test_config(self, temp_dir: Path) -> dict[str, Any]: "count": 3, } - def capture_stderr(self) -> bool: - return True - def test_error( self, results: ScenarioResult, @@ -213,8 +210,10 @@ def test_error( ): assert results.return_code == ResultCode.SUCCESS - assert results.stderr is not None - assert "error: tried to restore current KVS as snapshot" in results.stderr + # TODO: Restore 'stderr' capture for error logs. + # 'mw_log' prints to 'stdout'. + # 'stderr' would be preferred for pytest-based tests. + assert "Restoring current KVS snapshot is not allowed" in results.stdout result_log = logs_info_level.find_log("result") assert result_log is not None @@ -246,9 +245,6 @@ def test_config(self, temp_dir: Path) -> dict[str, Any]: "count": 1, } - def capture_stderr(self) -> bool: - return True - def test_error( self, results: ScenarioResult, @@ -256,8 +252,10 @@ def test_error( ): assert results.return_code == ResultCode.SUCCESS - assert results.stderr is not None - assert "error: tried to restore a non-existing snapshot" in results.stderr + # TODO: Restore 'stderr' capture for error logs. + # 'mw_log' prints to 'stdout'. + # 'stderr' would be preferred for pytest-based tests. + assert "Unable to restore non-existing snapshot" in results.stdout result_log = logs_info_level.find_log("result") assert result_log is not None diff --git a/tests/rust_test_scenarios/BUILD b/tests/rust_test_scenarios/BUILD index 6f98b1b5..93875e2d 100644 --- a/tests/rust_test_scenarios/BUILD +++ b/tests/rust_test_scenarios/BUILD @@ -15,6 +15,12 @@ load("@rules_rust//rust:defs.bzl", "rust_binary") rust_binary( name = "rust_test_scenarios", srcs = glob(["src/**/*.rs"]), + crate_features = ["mw_logger"], + rustc_flags = [ + "-Clink-arg=-lstdc++", + "-Clink-arg=-lm", + "-Clink-arg=-lc", + ], visibility = ["//tests/python_test_cases:__pkg__"], deps = [ @@ -24,6 +30,7 @@ rust_binary( "@score_crates//:tinyjson", "@score_crates//:tracing", "@score_crates//:tracing_subscriber", + "@score_logging//src/rust/mw_logger", "@score_test_scenarios//test_scenarios_rust", ], ) diff --git a/tests/rust_test_scenarios/Cargo.toml b/tests/rust_test_scenarios/Cargo.toml index ad4b2fc0..b1b9d24b 100644 --- a/tests/rust_test_scenarios/Cargo.toml +++ b/tests/rust_test_scenarios/Cargo.toml @@ -7,11 +7,18 @@ edition.workspace = true [dependencies] rust_kvs.workspace = true tinyjson.workspace = true +mw_log.workspace = true tracing = "0.1.41" tracing-subscriber = { version = "0.3.20", features = ["json"] } serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.140" test_scenarios_rust = { git = "https://github.com/eclipse-score/testing_tools.git", tag = "v0.3.0" } +stdout_logger = { git = "https://github.com/eclipse-score/baselibs_rust.git", rev = "54e2af036603107039a2eca4912413763ab112e7", optional = true } + + +[features] +default = ["stdout_logger"] +mw_logger = [] [lints] diff --git a/tests/rust_test_scenarios/src/main.rs b/tests/rust_test_scenarios/src/main.rs index f9cd9f26..027190aa 100644 --- a/tests/rust_test_scenarios/src/main.rs +++ b/tests/rust_test_scenarios/src/main.rs @@ -25,6 +25,7 @@ impl FormatTime for NumericUnixTime { } } +/// `tracing` is used for test outputs. fn init_tracing_subscriber() { let subscriber = FmtSubscriber::builder() .with_max_level(Level::TRACE) @@ -37,8 +38,18 @@ fn init_tracing_subscriber() { .expect("Setting default subscriber failed!"); } +/// Logging is used for regular logs. +fn init_logging() { + #[cfg(feature = "stdout_logger")] + stdout_logger::StdoutLoggerBuilder::new().set_as_default_logger(); + + #[cfg(feature = "mw_logger")] + mw_logger::MwLoggerBuilder::new().set_as_default_logger(); +} + fn main() -> Result<(), String> { let raw_arguments: Vec = std::env::args().collect(); + init_logging(); // Basic group. let basic_scenario = Box::new(BasicScenario);