diff --git a/.bazelrc b/.bazelrc index a3426dda..fe849f90 100644 --- a/.bazelrc +++ b/.bazelrc @@ -25,13 +25,15 @@ 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 +build --@score_logging//score/mw/log/flags:KRemote_Logging=False # Clippy linting (enabled by default) build --aspects=@score_rust_policies//clippy:linters.bzl%clippy_strict build --output_groups=+rules_lint_human build:lint --@aspect_rules_lint//lint:fail_on_violation=true +build:lint --extra_toolchains=@score_toolchains_rust//toolchains/ferrocene:ferrocene_x86_64_unknown_linux_gnu test --test_output=errors diff --git a/Cargo.lock b/Cargo.lock index bd213c36..25db7d1e 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" @@ -140,6 +140,7 @@ name = "rust_kvs" version = "0.1.0" dependencies = [ "adler32", + "score_log", "tempfile", "tinyjson", ] @@ -150,6 +151,8 @@ version = "0.1.0" dependencies = [ "pico-args", "rust_kvs", + "score_log", + "stdout_logger", "tinyjson", ] @@ -172,6 +175,31 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "score_log" +version = "0.0.1" +source = "git+https://github.com/eclipse-score/baselibs_rust.git?tag=v0.1.0#0eba2934fa8b0e1a343ec6bf6f7ff00cec27d81c" +dependencies = [ + "score_log_fmt", + "score_log_fmt_macro", +] + +[[package]] +name = "score_log_fmt" +version = "0.0.1" +source = "git+https://github.com/eclipse-score/baselibs_rust.git?tag=v0.1.0#0eba2934fa8b0e1a343ec6bf6f7ff00cec27d81c" + +[[package]] +name = "score_log_fmt_macro" +version = "0.0.1" +source = "git+https://github.com/eclipse-score/baselibs_rust.git?tag=v0.1.0#0eba2934fa8b0e1a343ec6bf6f7ff00cec27d81c" +dependencies = [ + "proc-macro2", + "quote", + "score_log_fmt", + "syn", +] + [[package]] name = "serde" version = "1.0.219" @@ -219,6 +247,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?tag=v0.1.0#0eba2934fa8b0e1a343ec6bf6f7ff00cec27d81c" +dependencies = [ + "score_log", +] + [[package]] name = "syn" version = "2.0.104" @@ -248,8 +284,10 @@ name = "test_scenarios" version = "0.1.0" dependencies = [ "rust_kvs", + "score_log", "serde", "serde_json", + "stdout_logger", "test_scenarios_rust", "tinyjson", "tracing", diff --git a/Cargo.toml b/Cargo.toml index 1fac0e07..8d36893f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,10 @@ edition = "2021" [workspace.dependencies] rust_kvs = { path = "src/rust/rust_kvs" } rust_kvs_tool = { path = "src/rust/rust_kvs_tool" } +score_log = { git = "https://github.com/eclipse-score/baselibs_rust.git", tag = "v0.1.0", features = [ + "qm", +] } +stdout_logger = { git = "https://github.com/eclipse-score/baselibs_rust.git", tag = "v0.1.0" } adler32 = "1.2.0" tinyjson = "2.5.1" diff --git a/MODULE.bazel b/MODULE.bazel index da5aa9e4..ddd15420 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -65,6 +65,7 @@ bazel_dep(name = "platforms", version = "1.0.0") ## S-CORE bazel registry bazel_dep(name = "score_baselibs", version = "0.2.4") +bazel_dep(name = "score_baselibs_rust", version = "0.1.0") bazel_dep(name = "score_bazel_platforms", version = "0.0.4") bazel_dep(name = "score_docs_as_code", version = "3.0.0") @@ -73,6 +74,15 @@ bazel_dep(name = "score_process", version = "1.4.3", dev_dependency = True) bazel_dep(name = "score_python_basics", version = "0.3.4") bazel_dep(name = "score_tooling", version = "1.1.0") +bazel_dep(name = "score_logging", version = "0.1.0") + +# TODO: remove once inherited properly from `score_logging`. +bazel_dep(name = "trlc", version = "0.0.0", dev_dependency = True) +git_override( + module_name = "trlc", + commit = "650b51a47264a4f232b3341f473527710fc32669", # trlc-2.0.2 release + remote = "https://github.com/bmw-software-engineering/trlc.git", +) # ToDo: implicit dependencies for score_tooling, but needed directly here?? bazel_dep(name = "aspect_rules_lint", version = "2.0.0") diff --git a/src/rust/rust_kvs/BUILD b/src/rust/rust_kvs/BUILD index 9682b384..1e7617aa 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/score_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..99674047 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 +score_log.workspace = true [dev-dependencies] diff --git a/src/rust/rust_kvs/examples/migration.rs b/src/rust/rust_kvs/examples/migration.rs index 320cf11d..33ffd628 100644 --- a/src/rust/rust_kvs/examples/migration.rs +++ b/src/rust/rust_kvs/examples/migration.rs @@ -14,10 +14,11 @@ //! Example for migrations between backends. use rust_kvs::prelude::*; +use score_log::ScoreDebug; /// Example custom backend. /// Returns some data on `load_kvs`. -#[derive(PartialEq)] +#[derive(PartialEq, ScoreDebug)] struct FromBackend; impl KvsBackend for FromBackend { @@ -51,7 +52,7 @@ impl KvsBackend for FromBackend { /// Example custom backend. /// Prints provided data to stdout. -#[derive(PartialEq)] +#[derive(PartialEq, ScoreDebug)] struct ToBackend; impl KvsBackend for ToBackend { diff --git a/src/rust/rust_kvs/src/error_code.rs b/src/rust/rust_kvs/src/error_code.rs index 1f20cdef..da0afd36 100644 --- a/src/rust/rust_kvs/src/error_code.rs +++ b/src/rust/rust_kvs/src/error_code.rs @@ -12,11 +12,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, @@ -94,7 +95,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 }, } @@ -103,21 +104,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 8b8eff21..f00b9e9c 100644 --- a/src/rust/rust_kvs/src/json_backend.rs +++ b/src/rust/rust_kvs/src/json_backend.rs @@ -14,6 +14,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}; @@ -123,8 +124,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() ); @@ -135,7 +136,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 } } @@ -161,12 +162,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 } @@ -187,7 +190,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, @@ -232,6 +235,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); } @@ -240,7 +244,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)?; @@ -256,10 +260,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); } @@ -270,13 +279,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); } @@ -284,17 +304,23 @@ 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) } } @@ -303,16 +329,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(()) } @@ -372,16 +412,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(()) } @@ -407,18 +445,20 @@ impl KvsBackend for JsonBackend { } fn snapshot_restore(&self, 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 0bb66992..ab367af2 100644 --- a/src/rust/rust_kvs/src/kvs.rs +++ b/src/rust/rust_kvs/src/kvs.rs @@ -15,9 +15,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, @@ -76,7 +78,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); } @@ -127,7 +129,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) } } @@ -150,15 +152,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) }, } @@ -167,13 +169,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) } } @@ -195,6 +196,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) } } @@ -219,6 +221,7 @@ impl KvsApi for Kvs { } else if data.defaults_map.contains_key(key) { Ok(true) } else { + error!("Key not found: {}", key); Err(ErrorCode::KeyNotFound) } } @@ -252,6 +255,7 @@ impl KvsApi for Kvs { if data.kvs_map.remove(key).is_some() { Ok(()) } else { + error!("Key not found: {}", key); Err(ErrorCode::KeyNotFound) } } @@ -271,7 +275,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(()); } @@ -334,12 +338,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 d432bbb1..aa3121f9 100644 --- a/src/rust/rust_kvs/src/kvs_api.rs +++ b/src/rust/rust_kvs/src/kvs_api.rs @@ -12,14 +12,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) } } @@ -31,11 +31,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) } } @@ -47,7 +47,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, @@ -60,7 +60,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, @@ -80,8 +80,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>(&self, key: S, value: J) -> Result<(), ErrorCode>; diff --git a/src/rust/rust_kvs/src/kvs_backend.rs b/src/rust/rust_kvs/src/kvs_backend.rs index 41331d36..4462d46f 100644 --- a/src/rust/rust_kvs/src/kvs_backend.rs +++ b/src/rust/rust_kvs/src/kvs_backend.rs @@ -13,6 +13,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. @@ -41,7 +42,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, instance_id: InstanceId, snapshot_id: SnapshotId) -> Result; diff --git a/src/rust/rust_kvs/src/kvs_builder.rs b/src/rust/rust_kvs/src/kvs_builder.rs index 2095b3eb..f74b21c6 100644 --- a/src/rust/rust_kvs/src/kvs_builder.rs +++ b/src/rust/rust_kvs/src/kvs_builder.rs @@ -16,6 +16,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. @@ -32,7 +33,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 } } @@ -50,12 +52,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, @@ -106,6 +110,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 } @@ -118,6 +123,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 } @@ -131,6 +137,7 @@ impl KvsBuilder { /// # Return Values /// * KvsBuilder instance pub fn backend(mut self, backend: Box) -> Self { + trace!("'backend' set to {:?}", backend); self.backend = Some(backend); self } @@ -139,22 +146,22 @@ 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. else if self.backend.as_ref().is_some_and(|v| !v.dyn_eq(other.backend.as_any())) { - eprintln!("error: backend parameters mismatched"); + error!("Backend parameters mismatched"); false } // Success. @@ -183,24 +190,38 @@ 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. @@ -218,6 +239,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) { @@ -231,6 +253,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(), @@ -252,10 +275,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 { @@ -264,6 +292,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 337aacb6..146d5a9e 100644 --- a/src/rust/rust_kvs/src/kvs_mock.rs +++ b/src/rust/rust_kvs/src/kvs_mock.rs @@ -13,6 +13,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)] @@ -75,8 +76,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 2a07b024..ccb73266 100644 --- a/src/rust/rust_kvs/src/kvs_value.rs +++ b/src/rust/rust_kvs/src/kvs_value.rs @@ -10,6 +10,8 @@ // // SPDX-License-Identifier: Apache-2.0 // ******************************************************************************* + +use crate::log::ScoreDebug; use core::convert::TryFrom; use std::collections::HashMap; @@ -17,7 +19,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 e634b839..b94acce3 100644 --- a/src/rust/rust_kvs/src/lib.rs +++ b/src/rust/rust_kvs/src/lib.rs @@ -141,6 +141,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..a6276ceb --- /dev/null +++ b/src/rust/rust_kvs/src/log.rs @@ -0,0 +1,66 @@ +// ******************************************************************************* +// Copyright (c) 2026 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 `score_log::fatal!`. +#[clippy::format_args] +macro_rules! fatal { + ($($arg:tt)+) => (score_log::fatal!(context: $crate::log::CONTEXT, $($arg)+)); +} + +/// Proxy for `score_log::error!`. +#[clippy::format_args] +macro_rules! error { + ($($arg:tt)+) => (score_log::error!(context: $crate::log::CONTEXT, $($arg)+)); +} + +/// Proxy for `score_log::warn!`. +#[clippy::format_args] +macro_rules! warning { + ($($arg:tt)+) => (score_log::warn!(context: $crate::log::CONTEXT, $($arg)+)); +} + +/// Proxy for `score_log::info!`. +#[clippy::format_args] +macro_rules! info { + ($($arg:tt)+) => (score_log::info!(context: $crate::log::CONTEXT, $($arg)+)); +} + +/// Proxy for `score_log::debug!`. +#[clippy::format_args] +macro_rules! debug { + ($($arg:tt)+) => (score_log::debug!(context: $crate::log::CONTEXT, $($arg)+)); +} + +/// Proxy for `score_log::trace!`. +#[clippy::format_args] +macro_rules! trace { + ($($arg:tt)+) => (score_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 `score_log`. +pub(crate) use score_log::fmt::ScoreDebug; +pub(crate) use score_log::ScoreDebug; diff --git a/src/rust/rust_kvs_tool/BUILD b/src/rust/rust_kvs_tool/BUILD index 5e84e158..db256db7 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 = ["score_log_bridge"], 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/score_log", "@score_crates//:pico_args", "@score_crates//:tinyjson", + "@score_logging//score/mw/log/rust/score_log_bridge", ], ) diff --git a/src/rust/rust_kvs_tool/Cargo.toml b/src/rust/rust_kvs_tool/Cargo.toml index 28900e3a..928589f9 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 +score_log.workspace = true +stdout_logger = { workspace = true, optional = true } + + +[features] +default = ["stdout_logger"] +score_log_bridge = [] [lints] diff --git a/src/rust/rust_kvs_tool/src/kvs_tool.rs b/src/rust/rust_kvs_tool/src/kvs_tool.rs index aa49ab15..7aef71e6 100644 --- a/src/rust/rust_kvs_tool/src/kvs_tool.rs +++ b/src/rust/rust_kvs_tool/src/kvs_tool.rs @@ -76,6 +76,7 @@ use pico_args::Arguments; use rust_kvs::prelude::*; +use score_log::error; use std::collections::HashMap; use tinyjson::JsonValue; @@ -124,21 +125,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 { @@ -148,7 +147,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 { @@ -166,7 +165,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); }, }; @@ -187,7 +186,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); }, }, @@ -206,22 +205,19 @@ 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); })?; }, } @@ -238,15 +234,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!("----------------------"); @@ -258,9 +253,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 { @@ -275,9 +269,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!("----------------------"); @@ -315,16 +308,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!("----------------------"); @@ -336,7 +328,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) }, } @@ -351,7 +343,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); }, }, @@ -375,7 +367,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); }, }, @@ -394,29 +386,24 @@ 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("number", 123.0).inspect_err(|e| { + error!("KVS Create Test Data Error (number): {:?}", e); })?; - kvs.set_value("bool", true).map_err(|e| { - eprintln!("KVS Create Test Data Error (bool): {e:?}"); - e + kvs.set_value("bool", true).inspect_err(|e| { + error!("KVS Create Test Data Error (bool): {:?}", e); })?; - kvs.set_value("string", "First".to_string()).map_err(|e| { - eprintln!("KVS Create Test Data Error (string): {e:?}"); - e + kvs.set_value("string", "First".to_string()).inspect_err(|e| { + error!("KVS Create Test Data Error (string): {:?}", e); })?; - kvs.set_value("null", ()).map_err(|e| { - eprintln!("KVS Create Test Data Error (null): {e:?}"); - e + kvs.set_value("null", ()).inspect_err(|e| { + error!("KVS Create Test Data Error (null): {:?}", e); })?; kvs.set_value( "array", vec![KvsValue::from(456.0), false.into(), "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", @@ -431,9 +418,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!"); @@ -441,10 +427,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 = "score_log_bridge")] + score_log_bridge::ScoreLogBridgeBuilder::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#" @@ -535,7 +531,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); }, }; @@ -545,7 +541,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); }, }, @@ -614,9 +610,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/test_cases/config/logging.json b/tests/test_cases/config/logging.json new file mode 100644 index 00000000..aa4a2228 --- /dev/null +++ b/tests/test_cases/config/logging.json @@ -0,0 +1,7 @@ +{ + "appId": "TEST", + "appDesc": "Rust test scenarios", + "logMode": "kConsole", + "logLevel": "kVerbose", + "logLevelThresholdConsole": "kInfo" +} \ No newline at end of file diff --git a/tests/test_cases/tests/test_cit_default_values.py b/tests/test_cases/tests/test_cit_default_values.py index 5c6d0dc5..2e7a9520 100644 --- a/tests/test_cases/tests/test_cit_default_values.py +++ b/tests/test_cases/tests/test_cit_default_values.py @@ -315,7 +315,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 @@ -352,7 +352,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/test_cases/tests/test_cit_snapshots.py b/tests/test_cases/tests/test_cit_snapshots.py index 9b82e4ee..0bd6e0e0 100644 --- a/tests/test_cases/tests/test_cit_snapshots.py +++ b/tests/test_cases/tests/test_cit_snapshots.py @@ -210,9 +210,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, @@ -222,8 +219,7 @@ def test_error( assert results.return_code == ResultCode.SUCCESS if version == "rust": - assert results.stderr is not None - assert "error: tried to restore current KVS as snapshot" in results.stderr + 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 @@ -253,15 +249,11 @@ 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, logs_info_level: LogContainer, version: str): assert results.return_code == ResultCode.SUCCESS if version == "rust": - assert results.stderr is not None - assert "error: tried to restore a non-existing snapshot" in results.stderr + 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/test_scenarios/rust/BUILD b/tests/test_scenarios/rust/BUILD index 88e5a3ac..adaaa37d 100644 --- a/tests/test_scenarios/rust/BUILD +++ b/tests/test_scenarios/rust/BUILD @@ -15,6 +15,12 @@ load("@rules_rust//rust:defs.bzl", "rust_binary") rust_binary( name = "test_scenarios", srcs = glob(["src/**/*.rs"]), + crate_features = ["score_log_bridge"], + rustc_flags = [ + "-Clink-arg=-lstdc++", + "-Clink-arg=-lm", + "-Clink-arg=-lc", + ], visibility = ["//tests/test_cases:__pkg__"], deps = [ @@ -24,6 +30,7 @@ rust_binary( "@score_crates//:tinyjson", "@score_crates//:tracing", "@score_crates//:tracing_subscriber", + "@score_logging//score/mw/log/rust/score_log_bridge", "@score_test_scenarios//test_scenarios_rust", ], ) diff --git a/tests/test_scenarios/rust/Cargo.toml b/tests/test_scenarios/rust/Cargo.toml index 0258b7bf..fc4f250a 100644 --- a/tests/test_scenarios/rust/Cargo.toml +++ b/tests/test_scenarios/rust/Cargo.toml @@ -7,11 +7,18 @@ edition.workspace = true [dependencies] rust_kvs.workspace = true tinyjson.workspace = true +score_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 = { workspace = true, optional = true } + + +[features] +default = ["stdout_logger"] +score_log_bridge = [] [lints] diff --git a/tests/test_scenarios/rust/src/main.rs b/tests/test_scenarios/rust/src/main.rs index c1b3e267..ac296e9e 100644 --- a/tests/test_scenarios/rust/src/main.rs +++ b/tests/test_scenarios/rust/src/main.rs @@ -32,6 +32,7 @@ impl FormatTime for NumericUnixTime { } } +/// `tracing` is used for test outputs. fn init_tracing_subscriber() { let subscriber = FmtSubscriber::builder() .with_max_level(Level::TRACE) @@ -43,8 +44,18 @@ fn init_tracing_subscriber() { tracing::subscriber::set_global_default(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 = "score_log_bridge")] + score_log_bridge::ScoreLogBridgeBuilder::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);