// file: crates/ksp-logging-lib/tests/runtime.rs // version: 10 //! Integration tests for global initialization, takeover filtering, non-blocking outputs and hot reload. const EXTERNAL_TARGET: &str = "sqlx"; const JSON_KSP_TARGET: &str = "ksp-logging-json-test"; const LOGGING_TARGET: &str = "ksp-logging-lib"; const OTHER_KSP_TARGET: &str = "ksp-store-lib"; fn logging_trace_enabled() -> bool { return tracing::enabled!(target: LOGGING_TARGET, tracing::Level::TRACE); } fn other_ksp_info_enabled() -> bool { return tracing::enabled!(target: OTHER_KSP_TARGET, tracing::Level::INFO); } fn other_ksp_debug_enabled() -> bool { return tracing::enabled!(target: OTHER_KSP_TARGET, tracing::Level::DEBUG); } fn external_error_enabled() -> bool { return tracing::enabled!(target: EXTERNAL_TARGET, tracing::Level::ERROR); } fn test_root_directory() -> std::path::PathBuf { return std::env::temp_dir().join(format!("ksp-logging-lib-runtime-{}", std::process::id())); } fn reset_directory(path: &std::path::Path) { if path.exists() { let remove_result = std::fs::remove_dir_all(path); assert!(remove_result.is_ok()); } } fn read_directory_text(path: &std::path::Path) -> std::string::String { let read_result = std::fs::read_dir(path); let entries = match read_result { std::result::Result::Ok(entries) => entries, std::result::Result::Err(_) => return std::string::String::new(), }; let mut output = std::string::String::new(); for entry_result in entries { let entry = match entry_result { std::result::Result::Ok(entry) => entry, std::result::Result::Err(_) => continue, }; let file_type = match entry.file_type() { std::result::Result::Ok(file_type) => file_type, std::result::Result::Err(_) => continue, }; if !file_type.is_file() { continue; } let content = match std::fs::read_to_string(entry.path()) { std::result::Result::Ok(content) => content, std::result::Result::Err(_) => continue, }; output.push_str(content.as_str()); } return output; } fn exercise_concurrent_reload(guard: &mut ksp_logging_lib::LoggingGuard, disabled: &ksp_logging_lib::LoggingSettings) { let quiet_console = ksp_logging_lib::LoggingSettings::new( ksp_logging_lib::LogFilterLevel::Off, ksp_logging_lib::SpanEvents::Off, std::option::Option::Some(ksp_logging_lib::ConsoleSettings::stderr()), std::vec::Vec::new(), ); let quiet_reload = ksp_logging_lib::reinitialize(guard, &quiet_console); assert!(quiet_reload.is_ok()); let stop = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)); let barrier = std::sync::Arc::new(std::sync::Barrier::new(5)); let mut threads = std::vec::Vec::new(); for worker_index in 0..4_u32 { let worker_stop = std::sync::Arc::clone(&stop); let worker_barrier = std::sync::Arc::clone(&barrier); threads.push(std::thread::spawn(move || { worker_barrier.wait(); let mut sequence = 0_u64; while !worker_stop.load(std::sync::atomic::Ordering::Relaxed) { ksp_logging_lib::trace!(target: LOGGING_TARGET, worker_index, sequence, "concurrent reload probe"); sequence = sequence.wrapping_add(1); } return; })); } barrier.wait(); let mut reloads_succeeded = true; for reload_index in 0..32_u32 { let settings = if reload_index % 2 == 0 { &quiet_console } else { disabled }; let reload_result = ksp_logging_lib::reinitialize(guard, settings); if reload_result.is_err() { reloads_succeeded = false; break; } } stop.store(true, std::sync::atomic::Ordering::Relaxed); let mut joins_succeeded = true; for thread in threads { if thread.join().is_err() { joins_succeeded = false; } } assert!(reloads_succeeded); assert!(joins_succeeded); } #[test] fn global_runtime_supports_takeover_non_blocking_outputs_hot_reload_and_single_initialization() { let root = test_root_directory(); reset_directory(root.as_path()); let disabled = ksp_logging_lib::LoggingSettings::new( ksp_logging_lib::LogFilterLevel::Info, ksp_logging_lib::SpanEvents::Off, std::option::Option::None, std::vec::Vec::new(), ); let identity = ksp_logging_lib::LoggingRuntimeIdentity::new("ksp-logging-runtime-test", "20260816-182519.123Z-p4242"); assert!(identity.is_ok()); let identity = match identity { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return, }; let initialize_result = ksp_logging_lib::initialize_with_identity(&disabled, &identity); assert!(initialize_result.is_ok()); let mut guard = match initialize_result { std::result::Result::Ok(guard) => guard, std::result::Result::Err(_) => return, }; assert_eq!(guard.dropped_lines(), ksp_logging_lib::DroppedLines::zero()); assert!(!logging_trace_enabled()); assert!(!other_ksp_info_enabled()); assert!(!external_error_enabled()); let console_enabled = ksp_logging_lib::LoggingSettings::new( ksp_logging_lib::LogFilterLevel::Info, ksp_logging_lib::SpanEvents::NewAndClose, std::option::Option::Some(ksp_logging_lib::ConsoleSettings::stderr()), std::vec::Vec::new(), ) .with_target_filter(ksp_logging_lib::TargetFilter::new(LOGGING_TARGET, ksp_logging_lib::LogFilterLevel::Trace)); let reload_result = ksp_logging_lib::reinitialize(&mut guard, &console_enabled); assert!(reload_result.is_ok()); assert_eq!(guard.settings(), &console_enabled); assert!(logging_trace_enabled()); assert!(other_ksp_info_enabled()); assert!(!other_ksp_debug_enabled()); assert!(!external_error_enabled()); let blocked_directory = root.join("not-a-directory"); let create_root = std::fs::create_dir_all(root.as_path()); assert!(create_root.is_ok()); let create_blocker = std::fs::write(blocked_directory.as_path(), b"file blocks directory creation"); assert!(create_blocker.is_ok()); let invalid_file = ksp_logging_lib::LoggingSettings::new( ksp_logging_lib::LogFilterLevel::Error, ksp_logging_lib::SpanEvents::Full, std::option::Option::None, std::vec![ksp_logging_lib::FileSettings::new( "file.invalid", true, blocked_directory.as_path(), "invalid", ksp_logging_lib::FileRotation::Daily, ksp_logging_lib::LogFormat::Human, ksp_logging_lib::OutputFilter::unrestricted(), )], ); let failed_reload = ksp_logging_lib::reinitialize(&mut guard, &invalid_file); assert!(failed_reload.is_err()); let file_error = match failed_reload { std::result::Result::Ok(()) => return, std::result::Result::Err(error) => error, }; assert_eq!(file_error.code(), ksp_logging_lib::ERROR_CODE_FILE_OUTPUT_INITIALIZATION_FAILED); assert_eq!(guard.settings(), &console_enabled); assert!(logging_trace_enabled()); assert!(!external_error_enabled()); exercise_concurrent_reload(&mut guard, &disabled); let human_directory = root.join("human"); let compact_directory = root.join("compact"); let pretty_directory = root.join("pretty"); let json_directory = root.join("json"); let files_enabled = ksp_logging_lib::LoggingSettings::new( ksp_logging_lib::LogFilterLevel::Trace, ksp_logging_lib::SpanEvents::Off, std::option::Option::None, std::vec![ ksp_logging_lib::FileSettings::new( "file.human.logging", true, human_directory.as_path(), "human.log", ksp_logging_lib::FileRotation::Never, ksp_logging_lib::LogFormat::Human, ksp_logging_lib::OutputFilter::new(ksp_logging_lib::LogFilterLevel::Info, std::vec![LOGGING_TARGET.to_string()], std::vec!["*".to_string()],), ), ksp_logging_lib::FileSettings::new( "file.compact.store", true, compact_directory.as_path(), "compact.log", ksp_logging_lib::FileRotation::Never, ksp_logging_lib::LogFormat::Compact, ksp_logging_lib::OutputFilter::new(ksp_logging_lib::LogFilterLevel::Warn, std::vec![OTHER_KSP_TARGET.to_string()], std::vec!["*".to_string()],), ), ksp_logging_lib::FileSettings::new( "file.pretty.error", true, pretty_directory.as_path(), "pretty.log", ksp_logging_lib::FileRotation::Never, ksp_logging_lib::LogFormat::Pretty, ksp_logging_lib::OutputFilter::new(ksp_logging_lib::LogFilterLevel::Error, std::vec![LOGGING_TARGET.to_string()], std::vec!["*".to_string()],), ), ksp_logging_lib::FileSettings::new( "file.json.logging", true, json_directory.as_path(), "runtime.jsonl", ksp_logging_lib::FileRotation::Never, ksp_logging_lib::LogFormat::Json, ksp_logging_lib::OutputFilter::new(ksp_logging_lib::LogFilterLevel::Trace, std::vec![JSON_KSP_TARGET.to_string()], std::vec!["*".to_string()],), ), ], ); let file_reload = ksp_logging_lib::reinitialize(&mut guard, &files_enabled); assert!(file_reload.is_ok()); assert_eq!(guard.runtime_identity(), std::option::Option::Some(&identity)); let active_files = guard.active_file_outputs(); assert_eq!(active_files.len(), 4); for file in &active_files { assert!(file.file_name_prefix().starts_with("ksp-logging-runtime-test.20260816-182519.123Z-p4242.")); } ksp_logging_lib::info!(target: LOGGING_TARGET, "logging info \x1b[31mmarker\x1b[0m"); ksp_logging_lib::error!(target: LOGGING_TARGET, "logging error marker"); ksp_logging_lib::info!(target: JSON_KSP_TARGET, "json info marker"); ksp_logging_lib::error!(target: JSON_KSP_TARGET, "json error marker"); ksp_logging_lib::warn!(target: OTHER_KSP_TARGET, "store warning marker"); ksp_logging_lib::info!(target: OTHER_KSP_TARGET, "store info must be filtered"); tracing::error!(target: EXTERNAL_TARGET, "external marker must remain silent"); let disable_after_file = ksp_logging_lib::reinitialize(&mut guard, &disabled); assert!(disable_after_file.is_ok()); let human_text = read_directory_text(human_directory.as_path()); let compact_text = read_directory_text(compact_directory.as_path()); let pretty_text = read_directory_text(pretty_directory.as_path()); let json_text = read_directory_text(json_directory.as_path()); assert!(human_text.contains("logging info marker")); assert!(human_text.contains("logging error marker")); assert!(!human_text.contains("store warning marker")); assert!(!human_text.contains("\x1b[")); assert!(compact_text.contains("store warning marker")); assert!(!compact_text.contains("store info must be filtered")); assert!(!compact_text.contains("logging info marker")); assert!(pretty_text.contains("logging error marker")); assert!(!pretty_text.contains("logging info marker")); assert!(json_text.contains("json info marker")); assert!(json_text.contains("json error marker")); assert!(json_text.contains(JSON_KSP_TARGET)); assert!(!json_text.contains("store warning marker")); assert!(!human_text.contains("external marker must remain silent")); assert!(!compact_text.contains("external marker must remain silent")); assert!(!pretty_text.contains("external marker must remain silent")); assert!(!json_text.contains("external marker must remain silent")); assert_eq!(guard.dropped_file_lines("file.human.logging"), std::option::Option::Some(0)); assert_eq!(guard.dropped_file_lines("file.compact.store"), std::option::Option::Some(0)); assert_eq!(guard.dropped_file_lines("file.pretty.error"), std::option::Option::Some(0)); assert_eq!(guard.dropped_file_lines("file.json.logging"), std::option::Option::Some(0)); assert_eq!(guard.dropped_file_lines("file.unknown"), std::option::Option::None); let domain_logging_directory = root.join("domain-logging"); let domain_store_directory = root.join("domain-store"); let domain_any_directory = root.join("domain-any"); let domain_settings = ksp_logging_lib::LoggingSettings::new( ksp_logging_lib::LogFilterLevel::Trace, ksp_logging_lib::SpanEvents::Full, std::option::Option::None, std::vec![ ksp_logging_lib::FileSettings::new( "file.domain.logging", true, domain_logging_directory.as_path(), "logging.log", ksp_logging_lib::FileRotation::Never, ksp_logging_lib::LogFormat::Human, ksp_logging_lib::OutputFilter::new( ksp_logging_lib::LogFilterLevel::Trace, std::vec![LOGGING_TARGET.to_string()], std::vec!["logging".to_string()], ), ), ksp_logging_lib::FileSettings::new( "file.domain.store", true, domain_store_directory.as_path(), "store.log", ksp_logging_lib::FileRotation::Never, ksp_logging_lib::LogFormat::Human, ksp_logging_lib::OutputFilter::new( ksp_logging_lib::LogFilterLevel::Trace, std::vec![LOGGING_TARGET.to_string()], std::vec!["store".to_string()], ), ), ksp_logging_lib::FileSettings::new( "file.domain.any", true, domain_any_directory.as_path(), "any.log", ksp_logging_lib::FileRotation::Never, ksp_logging_lib::LogFormat::Human, ksp_logging_lib::OutputFilter::new(ksp_logging_lib::LogFilterLevel::Trace, std::vec![LOGGING_TARGET.to_string()], std::vec!["*".to_string()],), ), ], ); let domain_reload = ksp_logging_lib::reinitialize(&mut guard, &domain_settings); assert!(domain_reload.is_ok()); ksp_logging_lib::info!(target: LOGGING_TARGET, domain = "logging.runtime", "direct logging domain marker"); ksp_logging_lib::info!(target: LOGGING_TARGET, domain = "store", "direct store domain marker"); ksp_logging_lib::info!(target: LOGGING_TARGET, "undomained marker"); let logging_lifecycle = ksp_logging_lib::info_span!(target: LOGGING_TARGET, "logging_lifecycle_span", domain = "logging"); logging_lifecycle.in_scope(|| { return; }); let store_lifecycle = ksp_logging_lib::info_span!(target: LOGGING_TARGET, "store_lifecycle_span", domain = "store"); store_lifecycle.in_scope(|| { return; }); let logging_parent = ksp_logging_lib::info_span!(target: LOGGING_TARGET, "logging_parent_span", domain = "logging"); logging_parent.in_scope(|| { ksp_logging_lib::info!(target: LOGGING_TARGET, "inherited logging marker"); ksp_logging_lib::info!(target: LOGGING_TARGET, domain = "store", "event override store marker"); let inherited_child = ksp_logging_lib::info_span!(target: LOGGING_TARGET, "inherited_child_span"); inherited_child.in_scope(|| { ksp_logging_lib::info!(target: LOGGING_TARGET, "child inherited logging marker"); }); let store_child = ksp_logging_lib::info_span!(target: LOGGING_TARGET, "store_child_span", domain = "store"); store_child.in_scope(|| { ksp_logging_lib::info!(target: LOGGING_TARGET, "child explicit store marker"); }); }); let store_parent = ksp_logging_lib::info_span!(target: LOGGING_TARGET, "store_parent_span", domain = "store"); store_parent.in_scope(|| { ksp_logging_lib::info!(target: LOGGING_TARGET, "inherited store marker"); }); let disable_after_domain = ksp_logging_lib::reinitialize(&mut guard, &disabled); assert!(disable_after_domain.is_ok()); let domain_logging_text = read_directory_text(domain_logging_directory.as_path()); let domain_store_text = read_directory_text(domain_store_directory.as_path()); let domain_any_text = read_directory_text(domain_any_directory.as_path()); assert!(domain_logging_text.contains("direct logging domain marker")); assert!(domain_logging_text.contains("inherited logging marker")); assert!(domain_logging_text.contains("child inherited logging marker")); assert!(domain_logging_text.contains("logging_lifecycle_span")); assert!(!domain_logging_text.contains("direct store domain marker")); assert!(!domain_logging_text.contains("event override store marker")); assert!(!domain_logging_text.contains("child explicit store marker")); assert!(!domain_logging_text.contains("inherited store marker")); assert!(!domain_logging_text.contains("store_lifecycle_span")); assert!(!domain_logging_text.contains("undomained marker")); assert!(domain_store_text.contains("direct store domain marker")); assert!(domain_store_text.contains("event override store marker")); assert!(domain_store_text.contains("child explicit store marker")); assert!(domain_store_text.contains("inherited store marker")); assert!(domain_store_text.contains("store_lifecycle_span")); assert!(!domain_store_text.contains("direct logging domain marker")); assert!(!domain_store_text.contains("inherited logging marker")); assert!(!domain_store_text.contains("child inherited logging marker")); assert!(!domain_store_text.contains("logging_lifecycle_span")); assert!(!domain_store_text.contains("undomained marker")); assert!(domain_any_text.contains("direct logging domain marker")); assert!(domain_any_text.contains("direct store domain marker")); assert!(domain_any_text.contains("undomained marker")); assert!(domain_any_text.contains("inherited logging marker")); assert!(domain_any_text.contains("event override store marker")); assert!(domain_any_text.contains("child inherited logging marker")); assert!(domain_any_text.contains("child explicit store marker")); assert!(domain_any_text.contains("inherited store marker")); assert!(domain_any_text.contains("logging_lifecycle_span")); assert!(domain_any_text.contains("store_lifecycle_span")); assert_eq!(guard.dropped_file_lines("file.domain.logging"), std::option::Option::Some(0)); assert_eq!(guard.dropped_file_lines("file.domain.store"), std::option::Option::Some(0)); assert_eq!(guard.dropped_file_lines("file.domain.any"), std::option::Option::Some(0)); let dropped = guard.dropped_lines(); assert_eq!(dropped.total(), dropped.console().saturating_add(dropped.file())); let second_initialize = ksp_logging_lib::initialize(&disabled); assert!(second_initialize.is_err()); let error = match second_initialize { std::result::Result::Ok(_) => return, std::result::Result::Err(error) => error, }; assert_eq!(error.code(), ksp_logging_lib::ERROR_CODE_ALREADY_INITIALIZED); reset_directory(root.as_path()); }