From bc439d186b0821f460934a1121d77e1126cdbafb Mon Sep 17 00:00:00 2001 From: konard Date: Thu, 11 Sep 2025 13:19:30 +0300 Subject: [PATCH 1/3] Initial commit with task details for issue #12 Adding CLAUDE.md with task information for AI processing. This file will be removed when the task is complete. Issue: https://github.com/linksplatform/doublets-rs/issues/12 --- CLAUDE.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..22c58381 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +Issue to solve: https://github.com/linksplatform/doublets-rs/issues/12 +Your prepared branch: issue-12-abf50ed2 +Your prepared working directory: /tmp/gh-issue-solver-1757585962743 + +Proceed. \ No newline at end of file From b95639e516c792228aa9f34c84439ac8578fbb1f Mon Sep 17 00:00:00 2001 From: konard Date: Thu, 11 Sep 2025 13:19:47 +0300 Subject: [PATCH 2/3] Remove CLAUDE.md - PR created successfully --- CLAUDE.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 22c58381..00000000 --- a/CLAUDE.md +++ /dev/null @@ -1,5 +0,0 @@ -Issue to solve: https://github.com/linksplatform/doublets-rs/issues/12 -Your prepared branch: issue-12-abf50ed2 -Your prepared working directory: /tmp/gh-issue-solver-1757585962743 - -Proceed. \ No newline at end of file From 293f3033aad088e83fc237b1455a740fb3eaab92 Mon Sep 17 00:00:00 2001 From: konard Date: Thu, 11 Sep 2025 13:28:02 +0300 Subject: [PATCH 3/3] Add log level resetting functionality for FFI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add global reload handle using tracing_subscriber::reload::Handle - Implement set_log_level() FFI function to change log level dynamically - Implement reset_log_level() FFI function to reset log level to trace - Use tracing_subscriber 0.3.18 with reload functionality - Add once_cell dependency for global handle management - Add comprehensive tests for log level functionality - Update setup_shared_logger to use reloadable filter This addresses issue #12 by providing a way to reset log levels through FFI without requiring newer tracing versions or complex holder objects. The implementation uses a global lazy static handle as suggested in the issue description. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- Cargo.lock | 123 +++++++++++++++++++++++++++++++++------- doublets-ffi/Cargo.toml | 3 +- doublets-ffi/src/lib.rs | 118 +++++++++++++++++++++++++++++++++++++- 3 files changed, 221 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fbf94c63..cc0dbcc1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" @@ -11,15 +11,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "atty" version = "0.2.14" @@ -276,8 +267,9 @@ dependencies = [ "ffi-attributes", "libc", "log", + "once_cell", "tracing", - "tracing-log", + "tracing-log 0.1.3", "tracing-subscriber", ] @@ -486,6 +478,15 @@ dependencies = [ "libmimalloc-sys", ] +[[package]] +name = "nu-ansi-term" +version = "0.50.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +dependencies = [ + "windows-sys", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -507,9 +508,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.13.0" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "oorandom" @@ -985,9 +986,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.28" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -1004,18 +1005,29 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + [[package]] name = "tracing-subscriber" -version = "0.3.15" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ - "ansi_term", + "nu-ansi-term", "sharded-slab", "smallvec", "thread_local", "tracing-core", - "tracing-log", + "tracing-log 0.2.0", ] [[package]] @@ -1147,3 +1159,76 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/doublets-ffi/Cargo.toml b/doublets-ffi/Cargo.toml index f1672de5..7ed86682 100644 --- a/doublets-ffi/Cargo.toml +++ b/doublets-ffi/Cargo.toml @@ -9,9 +9,10 @@ crate-type = ["cdylib", "staticlib"] [dependencies] log = "0.4.14" libc = "0.2.100" -tracing-subscriber = "0.3.3" +tracing-subscriber = "0.3.18" tracing-log = "0.1.2" tracing = "0.1.29" +once_cell = "1.19" doublets = { path = "../doublets" } ffi-attributes = { path = "ffi-attributes" } env-decorators = { path = "env-decorators" } diff --git a/doublets-ffi/src/lib.rs b/doublets-ffi/src/lib.rs index c9c571ae..cfadecbb 100644 --- a/doublets-ffi/src/lib.rs +++ b/doublets-ffi/src/lib.rs @@ -10,6 +10,7 @@ use std::{ mem, ops::{RangeInclusive, Try}, ptr::{drop_in_place, null_mut}, + sync::Mutex, }; use doublets::{ @@ -23,6 +24,9 @@ use doublets::{ }; use libc::c_char; use log::{error, warn}; +use once_cell::sync::Lazy; +use tracing_subscriber::{reload, EnvFilter, Registry, fmt}; +use tracing_subscriber::layer::SubscriberExt; #[allow(non_camel_case_types)] type c_void = core::ffi::c_void; @@ -30,6 +34,10 @@ type c_void = core::ffi::c_void; use doublets::{parts, unit, Doublets}; use ffi_attributes as ffi; +// Global reload handle for log level management +static RELOAD_HANDLE: Lazy>>> = + Lazy::new(|| Mutex::new(None)); + fn result_into_log(result: Result, default: R) -> R { result.unwrap_or_else(|e| { error!("{e}"); @@ -433,9 +441,18 @@ pub fn build_shared_logger() -> SharedLogger { pub extern "C" fn setup_shared_logger(logger: SharedLogger) { log::set_max_level(log::STATIC_MAX_LEVEL); - let subscriber = tracing_subscriber::fmt::fmt() - .with_max_level(tracing::Level::TRACE) - .finish(); + // Create a reloadable filter + let (filter, reload_handle) = reload::Layer::new(EnvFilter::new("trace")); + + // Store the reload handle globally + if let Ok(mut handle_guard) = RELOAD_HANDLE.lock() { + *handle_guard = Some(reload_handle); + } + + let subscriber = Registry::default() + .with(fmt::layer().with_target(false)) + .with(filter); + if let Err(err) = tracing::subscriber::set_global_default(subscriber) { warn!("subscriber error: {}", err) } @@ -451,6 +468,48 @@ pub extern "C" fn init_fmt_logger() { setup_shared_logger(logger); } +#[no_mangle] +pub extern "C" fn set_log_level(level: *const c_char) -> bool { + if level.is_null() { + return false; + } + + let level_str = unsafe { + match CStr::from_ptr(level).to_str() { + Ok(s) => s, + Err(_) => return false, + } + }; + + let filter = match EnvFilter::try_new(level_str) { + Ok(f) => f, + Err(_) => return false, + }; + + if let Ok(handle_guard) = RELOAD_HANDLE.lock() { + if let Some(ref handle) = *handle_guard { + match handle.reload(filter) { + Ok(_) => true, + Err(err) => { + warn!("Failed to reload log level: {}", err); + false + } + } + } else { + warn!("Log level reload handle not initialized"); + false + } + } else { + warn!("Failed to lock reload handle"); + false + } +} + +#[no_mangle] +pub extern "C" fn reset_log_level() -> bool { + set_log_level(b"trace\0".as_ptr() as *const c_char) +} + mod tests { #[test] fn error_log() { @@ -465,4 +524,57 @@ mod tests { warn!("warn"); error!("error"); } + + #[test] + fn test_log_level_reset() { + use crate::{build_shared_logger, reset_log_level, set_log_level, setup_shared_logger}; + use std::ffi::CString; + use log::{debug, error, info, trace, warn}; + + let logger = build_shared_logger(); + setup_shared_logger(logger); + + // Test setting log level to info + let info_level = CString::new("info").unwrap(); + assert!(set_log_level(info_level.as_ptr())); + + trace!("This trace should not be visible"); + debug!("This debug should not be visible"); + info!("This info should be visible"); + warn!("This warn should be visible"); + error!("This error should be visible"); + + // Test resetting log level to trace + assert!(reset_log_level()); + + trace!("This trace should now be visible"); + debug!("This debug should now be visible"); + info!("This info should still be visible"); + + // Test setting log level to error + let error_level = CString::new("error").unwrap(); + assert!(set_log_level(error_level.as_ptr())); + + trace!("This trace should not be visible"); + debug!("This debug should not be visible"); + info!("This info should not be visible"); + warn!("This warn should not be visible"); + error!("This error should be visible"); + } + + #[test] + fn test_invalid_log_level() { + use crate::{build_shared_logger, set_log_level, setup_shared_logger}; + use std::ffi::CString; + + let logger = build_shared_logger(); + setup_shared_logger(logger); + + // Test with invalid log level + let invalid_level = CString::new("invalid_level").unwrap(); + assert!(!set_log_level(invalid_level.as_ptr())); + + // Test with null pointer + assert!(!set_log_level(std::ptr::null())); + } }