diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 0952638..facdd72 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 logging -- -D warnings - name: Cargo Fmt run: | diff --git a/Cargo.lock b/Cargo.lock index 2b83c72..5c358e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,25 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +[[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.59.0", +] + +[[package]] +name = "deranged" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" +dependencies = [ + "powerfmt", +] + [[package]] name = "errno" version = "0.3.13" @@ -86,25 +105,33 @@ checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" dependencies = [ - "overload", - "winapi", + "windows-sys 0.52.0", ] [[package]] -name = "once_cell" -version = "1.21.3" +name = "num-conv" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] -name = "overload" -version = "0.1.1" +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "pico-args" @@ -118,6 +145,12 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "proc-macro2" version = "1.0.95" @@ -147,6 +180,7 @@ name = "rust_kvs" version = "0.1.0" dependencies = [ "adler32", + "log", "tempfile", "tinyjson", ] @@ -155,8 +189,10 @@ dependencies = [ name = "rust_kvs_tool" version = "0.1.0" dependencies = [ + "log", "pico-args", "rust_kvs", + "simple_logger", "tinyjson", ] @@ -164,9 +200,11 @@ dependencies = [ name = "rust_test_scenarios" version = "0.1.0" dependencies = [ + "log", "rust_kvs", "serde", "serde_json", + "simple_logger", "test_scenarios_rust", "tinyjson", "tracing", @@ -194,18 +232,28 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.226" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.226" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" dependencies = [ "proc-macro2", "quote", @@ -214,14 +262,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.141" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] @@ -233,6 +282,18 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "simple_logger" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c5dfa5e08767553704aa0ffd9d9794d527103c736aba9854773851fd7497eb" +dependencies = [ + "colored", + "log", + "time", + "windows-sys 0.48.0", +] + [[package]] name = "smallvec" version = "1.15.1" @@ -281,6 +342,39 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tinyjson" version = "2.5.1" @@ -342,9 +436,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "nu-ansi-term", "serde", @@ -379,26 +473,22 @@ dependencies = [ ] [[package]] -name = "winapi" -version = "0.3.9" +name = "windows-sys" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "windows-targets 0.48.5", ] [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "windows-sys" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] [[package]] name = "windows-sys" @@ -418,6 +508,21 @@ dependencies = [ "windows-targets 0.53.2", ] +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -450,6 +555,12 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -462,6 +573,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -474,6 +591,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -498,6 +621,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -510,6 +639,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -522,6 +657,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -534,6 +675,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/MODULE.bazel b/MODULE.bazel index 8bff2de..c3caf93 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -43,7 +43,7 @@ bazel_dep(name = "rules_rust", version = "0.56.0") 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 @@ -94,8 +94,38 @@ bazel_dep(name = "score_python_basics", version = "0.3.4") bazel_dep(name = "score_platform", version = "0.4.1") bazel_dep(name = "score_process", version = "1.3.1") -# Module deps -bazel_dep(name = "score_baselibs", version = "0.1.2") +# S-CORE crates +bazel_dep(name = "score_crates", version = "0.0.3") +git_override( + module_name = "score_crates", + # TODO: 'commit' and 'remote' must be updated once required changes are merged. + # https://github.com/eclipse-score/score-crates/pull/16 + commit = "f0bf4bdf98d8a8ad548256ab24dc08f9ba8f04aa", + remote = "https://github.com/qorix-group/score-crates.git", +) + +# S-CORE base libraries and logging library. +bazel_dep(name = "score_baselibs", version = "0.2.0") +git_override( + module_name = "score_baselibs", + commit = "59dba065def85e3c7fb38e08f0d4cb04cc693b2f", + remote = "https://github.com/eclipse-score/baselibs.git", +) + +bazel_dep(name = "score_baselibs_rust", version = "0.1.0") +git_override( + module_name = "score_baselibs_rust", + branch = "arkjedrz_mw-log-main", + remote = "https://github.com/qorix-group/baselibs_rust.git", +) + + +bazel_dep(name = "score_logging", version = "0.0.3") +git_override( + module_name = "score_logging", + commit = "95f8e005713590820e8ed62144bfcf5b4c3becf9", + remote = "https://github.com/qorix-group/logging.git", +) ## additional settings / config crate = use_extension("@rules_rust//crate_universe:extensions.bzl", "crate") diff --git a/src/rust/rust_kvs/BUILD b/src/rust/rust_kvs/BUILD index 2b1f322..2942e3f 100644 --- a/src/rust/rust_kvs/BUILD +++ b/src/rust/rust_kvs/BUILD @@ -16,15 +16,17 @@ load("@score_persistency_crates//:defs.bzl", "all_crate_deps") rust_library( name = "rust_kvs", srcs = glob(["src/**/*.rs"]), + crate_features = ["score-log"], visibility = ["//visibility:public"], - deps = all_crate_deps( - normal = True, - ), + deps = [ + "@score_baselibs_rust//src/log/mw_log", + ] + all_crate_deps(normal = True), ) rust_test( name = "tests", crate = "rust_kvs", + crate_features = ["score-log"], tags = [ "unit_tests", "ut", diff --git a/src/rust/rust_kvs/Cargo.toml b/src/rust/rust_kvs/Cargo.toml index 56f680e..36b5691 100644 --- a/src/rust/rust_kvs/Cargo.toml +++ b/src/rust/rust_kvs/Cargo.toml @@ -6,6 +6,12 @@ edition.workspace = true [dependencies] adler32.workspace = true tinyjson.workspace = true +log = { version = "0.4.27", optional = true } + +[features] +default = ["logging"] +score-log = [] +logging = ["log"] [dev-dependencies] tempfile = "3.20" diff --git a/src/rust/rust_kvs/src/error_code.rs b/src/rust/rust_kvs/src/error_code.rs index ff84252..eb89332 100644 --- a/src/rust/rust_kvs/src/error_code.rs +++ b/src/rust/rust_kvs/src/error_code.rs @@ -11,6 +11,7 @@ extern crate alloc; +use crate::log::error; use alloc::string::FromUtf8Error; use core::array::TryFromSliceError; @@ -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}"); 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 56aa542..efa94c3 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, KvsPathResolver}; use crate::kvs_value::{KvsMap, KvsValue}; +use crate::log::{debug, error}; 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,18 +172,32 @@ impl JsonBackend { impl KvsBackend for JsonBackend { fn load_kvs(kvs_path: &Path, hash_path: &Path) -> Result { + // Check file path extensions. + debug!("Checking KVS file path: {kvs_path:?}"); if !Self::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 !Self::check_extension(hash_path, "hash") { + error!("Invalid hash file path extension: {hash_path:?}"); return Err(ErrorCode::KvsHashFileReadError); } // Load KVS file and parse from string to `JsonValue`. - let json_str = fs::read_to_string(kvs_path)?; - let json_value = Self::parse(&json_str)?; + debug!("Loading KVS file: {kvs_path:?}"); + let json_str = fs::read_to_string(kvs_path).inspect_err(|_e| { + error!("Failed to load KVS file: {kvs_path:?}"); + })?; + + debug!("Parsing KVS file: {kvs_path:?}"); + let json_value = Self::parse(&json_str).inspect_err(|_e| { + error!("Failed to parse KVS file: {kvs_path:?}"); + })?; // Perform hash check. + debug!("Performing hash check, KVS file: {kvs_path:?}, hash file: {hash_path:?}"); match fs::read(hash_path) { Ok(hash_bytes) => { let hash_kvs = adler32::RollingAdler32::from_buffer(json_str.as_bytes()).hash(); @@ -194,44 +209,67 @@ impl KvsBackend for JsonBackend { hash_bytes[3], ]); if hash_kvs != file_hash { + error!("Hash mismatch, KVS file: {kvs_path:?}, hash file: {hash_path:?}"); return Err(ErrorCode::ValidationFailed); } } else { + error!("Invalid hash length: {hash_path:?}"); return Err(ErrorCode::ValidationFailed); } } - Err(e) => return Err(e.into()), + Err(e) => { + error!("Failed to load hash file: {hash_path:?}"); + return Err(e.into()); + } }; // 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) } } fn save_kvs(kvs_map: &KvsMap, kvs_path: &Path, hash_path: &Path) -> Result<(), ErrorCode> { - // Validate extensions. + // Check file path extensions. + debug!("Checking KVS file path: {kvs_path:?}"); if !Self::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 !Self::check_extension(hash_path, "hash") { + error!("Invalid hash file path extension: {hash_path:?}"); return Err(ErrorCode::KvsHashFileReadError); } // 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(|_e| { + error!("Failed to stringify KVS file content: {kvs_path:?}"); + })?; + + debug!("Saving KVS file: {kvs_path:?}"); + fs::write(kvs_path, &json_str).inspect_err(|_e| { + error!("Failed to save KVS file: {kvs_path:?}"); + })?; // Generate hash and save to hash file. + debug!("Generating KVS hash, KVS file: {kvs_path:?}, hash file: {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(|_e| { + error!("Failed to save hash file: {hash_path:?}"); + })?; Ok(()) } diff --git a/src/rust/rust_kvs/src/kvs.rs b/src/rust/rust_kvs/src/kvs.rs index d36676a..ea77791 100644 --- a/src/rust/rust_kvs/src/kvs.rs +++ b/src/rust/rust_kvs/src/kvs.rs @@ -14,13 +14,14 @@ use crate::kvs_api::{InstanceId, KvsApi, KvsDefaults, KvsLoad, SnapshotId}; use crate::kvs_backend::{KvsBackend, KvsPathResolver}; use crate::kvs_builder::KvsData; use crate::kvs_value::{KvsMap, KvsValue}; +use crate::log::{debug, error, warn}; use std::fs; use std::marker::PhantomData; use std::path::PathBuf; use std::sync::{Arc, Mutex}; /// KVS instance parameters. -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Debug)] pub struct KvsParameters { /// Instance ID. pub instance_id: InstanceId, @@ -117,7 +118,7 @@ impl GenericKvs {snap_name_new}"); + debug!("Rotating snapshots: {snap_name_old} -> {snap_name_new}"); // Check snapshot and hash files exist. let snap_old_exists = snap_path_old.exists(); @@ -135,6 +136,7 @@ impl GenericKvs KvsApi 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); } @@ -220,7 +222,7 @@ impl KvsApi } 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) } } @@ -251,9 +253,7 @@ impl KvsApi 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) } } @@ -262,15 +262,12 @@ impl KvsApi 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) } } @@ -292,6 +289,7 @@ impl KvsApi if let Some(value) = data.defaults_map.get(key) { Ok(value.clone()) } else { + error!("Key not found: {key}"); Err(ErrorCode::KeyNotFound) } } @@ -316,6 +314,7 @@ impl KvsApi } else if data.defaults_map.contains_key(key) { Ok(true) } else { + error!("Key not found: {key}"); Err(ErrorCode::KeyNotFound) } } @@ -353,6 +352,7 @@ impl KvsApi if data.kvs_map.remove(key).is_some() { Ok(()) } else { + error!("Key not found: {key}"); Err(ErrorCode::KeyNotFound) } } @@ -372,12 +372,12 @@ impl KvsApi /// * `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(()); } self.snapshot_rotate().map_err(|e| { - eprintln!("error: snapshot_rotate failed: {e:?}"); + error!("Failed to rotate snapshots: {e:?}"); e })?; let snapshot_id = SnapshotId(0); @@ -394,7 +394,7 @@ impl KvsApi let data = self.data.lock()?; Backend::save_kvs(&data.kvs_map, &kvs_path, &hash_path).map_err(|e| { - eprintln!("error: save_kvs failed: {e:?}"); + error!("Failed to save snapshot: {e:?}"); e })?; Ok(()) @@ -454,12 +454,12 @@ impl KvsApi let mut data = self.data.lock()?; // 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() < snapshot_id.0 { - eprintln!("error: tried to restore a non-existing snapshot"); + error!("Unable to restore non-existing snapshot"); return Err(ErrorCode::InvalidSnapshotId); } @@ -473,7 +473,10 @@ impl KvsApi self.parameters.instance_id, snapshot_id, ); - data.kvs_map = Backend::load_kvs(&kvs_path, &hash_path)?; + data.kvs_map = Backend::load_kvs(&kvs_path, &hash_path).map_err(|e| { + error!("Failed to load snapshot: {e:?}"); + e + })?; Ok(()) } @@ -493,6 +496,7 @@ impl KvsApi snapshot_id, ); if !path.exists() { + error!("File not found: {}", path.display()); Err(ErrorCode::FileNotFound) } else { Ok(path) @@ -514,6 +518,7 @@ impl KvsApi snapshot_id, ); if !path.exists() { + error!("File not found: {}", path.display()); Err(ErrorCode::FileNotFound) } else { Ok(path) diff --git a/src/rust/rust_kvs/src/kvs_builder.rs b/src/rust/rust_kvs/src/kvs_builder.rs index 31076a3..4ee0398 100644 --- a/src/rust/rust_kvs/src/kvs_builder.rs +++ b/src/rust/rust_kvs/src/kvs_builder.rs @@ -14,6 +14,7 @@ use crate::kvs::{GenericKvs, KvsParameters}; use crate::kvs_api::{InstanceId, KvsDefaults, KvsLoad, SnapshotId}; use crate::kvs_backend::{KvsBackend, KvsPathResolver}; use crate::kvs_value::KvsMap; +use crate::log::{debug, error, info, trace}; use std::marker::PhantomData; use std::path::PathBuf; use std::sync::{Arc, LazyLock, Mutex, MutexGuard, PoisonError}; @@ -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,7 +52,8 @@ 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 } } @@ -158,6 +161,7 @@ impl GenericKvsBuilder Self { + trace!("\"defaults\" set to {mode:?}"); self.parameters.defaults = Some(mode); self } @@ -170,6 +174,7 @@ impl GenericKvsBuilder Self { + trace!("\"kvs_load\" set to {mode:?}"); self.parameters.kvs_load = Some(mode); self } @@ -182,7 +187,9 @@ impl GenericKvsBuilder>(mut self, dir: P) -> Self { - self.parameters.working_dir = Some(PathBuf::from(dir.into())); + let working_dir = PathBuf::from(dir.into()); + trace!("\"dir\" set to {working_dir:?}"); + self.parameters.working_dir = Some(working_dir); self } @@ -194,6 +201,7 @@ impl GenericKvsBuilder Self { + trace!("\"snapshot_max_count\" set to {snapshot_max_count:?}"); self.parameters.snapshot_max_count = Some(snapshot_max_count); self } @@ -218,8 +226,11 @@ impl GenericKvsBuilder match kvs_pool_entry { @@ -230,20 +241,29 @@ impl GenericKvsBuilder 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. if let Some(kvs_inner) = kvs_inner_option { + info!("Existing KVS instance: {}", instance_id); return Ok(GenericKvs::::new( kvs_inner.data.clone(), kvs_inner.parameters.clone(), @@ -257,6 +277,7 @@ impl GenericKvsBuilder GenericKvsBuilder GenericKvsBuilder 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 { @@ -311,6 +338,7 @@ impl GenericKvsBuilder; diff --git a/src/rust/rust_kvs/src/log.rs b/src/rust/rust_kvs/src/log.rs new file mode 100644 index 0000000..b5d2323 --- /dev/null +++ b/src/rust/rust_kvs/src/log.rs @@ -0,0 +1,24 @@ +// 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 + +// This file exposes common logging interface for different front-ends. + +// Compile error if both `logging` and `score-log` features are enabled. +#[cfg(all(feature = "logging", feature = "score-log"))] +compile_error!("`logging` and `score-log` cannot be enabled at the same time."); + +#[allow(unused_imports)] +#[cfg(feature = "logging")] +pub use log::{debug, error, info, trace, warn}; + +#[allow(unused_imports)] +#[cfg(feature = "score-log")] +pub use mw_log::{debug, error, info, trace, warn}; diff --git a/src/rust/rust_kvs_tool/BUILD b/src/rust/rust_kvs_tool/BUILD index a28b0b8..de67977 100644 --- a/src/rust/rust_kvs_tool/BUILD +++ b/src/rust/rust_kvs_tool/BUILD @@ -18,11 +18,11 @@ rust_binary( srcs = [ "src/kvs_tool.rs", ], + crate_features = ["score-log"], crate_name = "rust_kvs_tool", visibility = ["//visibility:public"], - deps = all_crate_deps( - normal = True, - ) + [ + deps = [ "//src/rust/rust_kvs", - ], + "@score_logging//src/rust/mw_log_subscriber", + ] + all_crate_deps(normal = True), ) diff --git a/src/rust/rust_kvs_tool/Cargo.toml b/src/rust/rust_kvs_tool/Cargo.toml index e6c987d..e24fc25 100644 --- a/src/rust/rust_kvs_tool/Cargo.toml +++ b/src/rust/rust_kvs_tool/Cargo.toml @@ -3,7 +3,6 @@ name = "rust_kvs_tool" version.workspace = true edition.workspace = true - [[bin]] name = "kvs_tool" path = "src/kvs_tool.rs" @@ -12,3 +11,10 @@ path = "src/kvs_tool.rs" rust_kvs.workspace = true tinyjson.workspace = true pico-args.workspace = true +log = { version = "0.4.27", optional = true } +simple_logger = { version = "5.0.0", optional = true } + +[features] +default = ["logging"] +score-log = [] +logging = ["log", "simple_logger"] diff --git a/src/rust/rust_kvs_tool/src/kvs_tool.rs b/src/rust/rust_kvs_tool/src/kvs_tool.rs index 6540e2a..4b5ced8 100644 --- a/src/rust/rust_kvs_tool/src/kvs_tool.rs +++ b/src/rust/rust_kvs_tool/src/kvs_tool.rs @@ -436,10 +436,24 @@ fn _createtestdata(kvs: Kvs) -> Result<(), ErrorCode> { Ok(()) } +fn init_logging() { + #[cfg(feature = "logging")] + simple_logger::SimpleLogger::new() + .with_level(log::LevelFilter::Warn) + .env() + .init() + .unwrap(); + + #[cfg(feature = "score-log")] + mw_log_subscriber::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#" diff --git a/tests/python_test_cases/config/logging.json b/tests/python_test_cases/config/logging.json new file mode 100644 index 0000000..bd60e5c --- /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 46f3201..879529c 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 ec681da..734fe70 100644 --- a/tests/python_test_cases/tests/test_cit_default_values.py +++ b/tests/python_test_cases/tests/test_cit_default_values.py @@ -309,7 +309,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 @@ -348,7 +348,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 b8440a3..b704edc 100644 --- a/tests/python_test_cases/tests/test_cit_snapshots.py +++ b/tests/python_test_cases/tests/test_cit_snapshots.py @@ -213,8 +213,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 @@ -256,8 +258,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 1e50658..efb81b4 100644 --- a/tests/rust_test_scenarios/BUILD +++ b/tests/rust_test_scenarios/BUILD @@ -16,10 +16,10 @@ load("@score_persistency_crates//:defs.bzl", "all_crate_deps") rust_binary( name = "rust_test_scenarios", srcs = glob(["src/**/*.rs"]), + crate_features = ["score-log"], visibility = ["//tests/python_test_cases:__pkg__"], - deps = - [ - "//src/rust/rust_kvs", - "@score_persistency_crates//:tinyjson", - ] + all_crate_deps(normal = True), + deps = [ + "//src/rust/rust_kvs", + "@score_logging//src/rust/mw_log_subscriber", + ] + all_crate_deps(normal = True), ) diff --git a/tests/rust_test_scenarios/Cargo.toml b/tests/rust_test_scenarios/Cargo.toml index 072c077..c953284 100644 --- a/tests/rust_test_scenarios/Cargo.toml +++ b/tests/rust_test_scenarios/Cargo.toml @@ -4,10 +4,17 @@ version.workspace = true edition.workspace = true [dependencies] -rust_kvs.workspace = true +rust_kvs = { workspace = true, features = ["logging"] } tinyjson.workspace = true tracing = "0.1.41" tracing-subscriber = { version = "0.3.19", features = ["json"] } serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.140" test_scenarios_rust = { git = "https://github.com/qorix-group/testing_tools.git", tag = "v0.2.0" } +log = { version = "0.4.27", optional = true } +simple_logger = { version = "5.0.0", optional = true } + +[features] +default = ["logging"] +score-log = [] +logging = ["log", "simple_logger"] diff --git a/tests/rust_test_scenarios/src/main.rs b/tests/rust_test_scenarios/src/main.rs index 28e80f6..7a6948e 100644 --- a/tests/rust_test_scenarios/src/main.rs +++ b/tests/rust_test_scenarios/src/main.rs @@ -7,8 +7,21 @@ mod test_basic; use crate::cit::cit_scenario_group; use crate::test_basic::BasicScenario; +fn init_logging() { + #[cfg(feature = "logging")] + simple_logger::SimpleLogger::new() + .with_level(log::LevelFilter::Warn) + .env() + .init() + .unwrap(); + + #[cfg(feature = "score-log")] + mw_log_subscriber::MwLoggerBuilder::new().set_as_default_logger::(); +} + fn main() { let raw_arguments: Vec = std::env::args().collect(); + init_logging(); // Basic group. let basic_scenario = Box::new(BasicScenario);