use std::env;
use std::sync::atomic::{AtomicBool, Ordering};
static INITIALIZED: AtomicBool = AtomicBool::new(false);
pub fn init(crate_name: &str) {
if INITIALIZED.load(Ordering::Relaxed) {
return;
}
if !should_enable(crate_name) {
return;
}
if INITIALIZED
.compare_exchange(false, true, Ordering::SeqCst, Ordering::Relaxed)
.is_err()
{
return;
}
let mut builder = env_logger::Builder::from_default_env();
let level = determine_level();
if env::var("RUST_LOG").is_err() {
builder.filter_level(level);
}
builder.is_test(true);
if env::var("SQRY_TEST_VERBOSE_ARTIFACTS").is_ok() {
if let Some(writer) = super::artifacts::maybe_writer(crate_name) {
builder.target(env_logger::Target::Pipe(Box::new(writer)));
} else {
eprintln!(
"Warning: Failed to create artifact writer for {crate_name}. Continuing without artifacts."
);
}
}
match builder.try_init() {
Ok(()) => {
log::info!("sqry test verbose logging enabled for {crate_name} (level: {level})");
}
Err(e) => {
eprintln!("Info: Test logging already initialized for {crate_name}: {e}");
}
}
}
pub fn should_enable(crate_name: &str) -> bool {
let Ok(verbose) = env::var("SQRY_TEST_VERBOSE") else {
return false;
};
if verbose.trim().is_empty() {
return false;
}
if verbose.trim().eq_ignore_ascii_case("all") {
return true;
}
let short_name = crate_name.strip_prefix("sqry-").unwrap_or(crate_name);
verbose.split(',').map(str::trim).any(|target| {
target.eq_ignore_ascii_case(crate_name) || target.eq_ignore_ascii_case(short_name)
})
}
#[must_use]
pub fn determine_level() -> log::LevelFilter {
if env::var("RUST_LOG").is_ok() {
return log::LevelFilter::Trace;
}
let Ok(level_str) = env::var("SQRY_TEST_VERBOSE_LEVEL") else {
return log::LevelFilter::Info;
};
match level_str.trim().to_lowercase().as_str() {
"trace" => log::LevelFilter::Trace,
"debug" => log::LevelFilter::Debug,
"info" => log::LevelFilter::Info,
"warn" => log::LevelFilter::Warn,
"error" => log::LevelFilter::Error,
"off" => log::LevelFilter::Off,
invalid => {
eprintln!(
"Warning: Invalid SQRY_TEST_VERBOSE_LEVEL '{invalid}'. Valid values: trace, debug, info, warn, error, off. Defaulting to 'info'."
);
log::LevelFilter::Info
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
use std::sync::Mutex;
static ENV_MUTEX: Mutex<()> = Mutex::new(());
#[test]
fn test_should_enable_with_all() {
let _lock = ENV_MUTEX.lock().unwrap();
unsafe {
env::set_var("SQRY_TEST_VERBOSE", "all");
}
assert!(should_enable("sqry-core"));
assert!(should_enable("sqry-cli"));
assert!(should_enable("any-crate"));
unsafe {
env::remove_var("SQRY_TEST_VERBOSE");
}
}
#[test]
fn test_should_enable_with_comma_list() {
let _lock = ENV_MUTEX.lock().unwrap();
unsafe {
env::set_var("SQRY_TEST_VERBOSE", "core,cli");
}
assert!(should_enable("sqry-core"));
assert!(should_enable("sqry-cli"));
assert!(!should_enable("sqry-plugin"));
assert!(!should_enable("sqry-mcp"));
unsafe {
env::remove_var("SQRY_TEST_VERBOSE");
}
}
#[test]
fn test_should_enable_with_short_names() {
let _lock = ENV_MUTEX.lock().unwrap();
unsafe {
env::set_var("SQRY_TEST_VERBOSE", "core");
}
assert!(should_enable("sqry-core"));
unsafe {
env::remove_var("SQRY_TEST_VERBOSE");
}
}
#[test]
fn test_should_enable_case_insensitive() {
let _lock = ENV_MUTEX.lock().unwrap();
unsafe {
env::set_var("SQRY_TEST_VERBOSE", "CORE,CLI");
}
assert!(should_enable("sqry-core"));
assert!(should_enable("sqry-cli"));
unsafe {
env::remove_var("SQRY_TEST_VERBOSE");
}
}
#[test]
fn test_should_enable_disabled_by_default() {
let _lock = ENV_MUTEX.lock().unwrap();
unsafe {
env::remove_var("SQRY_TEST_VERBOSE");
}
assert!(!should_enable("sqry-core"));
}
#[test]
fn test_should_enable_with_whitespace() {
let _lock = ENV_MUTEX.lock().unwrap();
unsafe {
env::set_var("SQRY_TEST_VERBOSE", " core , cli ");
}
assert!(should_enable("sqry-core"));
assert!(should_enable("sqry-cli"));
unsafe {
env::remove_var("SQRY_TEST_VERBOSE");
}
}
#[test]
fn test_determine_level_defaults_to_info() {
let _lock = ENV_MUTEX.lock().unwrap();
unsafe {
env::remove_var("RUST_LOG");
}
unsafe {
env::remove_var("SQRY_TEST_VERBOSE_LEVEL");
}
assert_eq!(determine_level(), log::LevelFilter::Info);
}
#[test]
fn test_determine_level_respects_rust_log() {
let _lock = ENV_MUTEX.lock().unwrap();
unsafe {
env::set_var("RUST_LOG", "debug");
}
assert_eq!(determine_level(), log::LevelFilter::Trace);
unsafe {
env::remove_var("RUST_LOG");
}
}
#[test]
fn test_determine_level_with_override() {
let _lock = ENV_MUTEX.lock().unwrap();
unsafe {
env::remove_var("RUST_LOG");
}
unsafe {
env::set_var("SQRY_TEST_VERBOSE_LEVEL", "trace");
}
assert_eq!(determine_level(), log::LevelFilter::Trace);
unsafe {
env::set_var("SQRY_TEST_VERBOSE_LEVEL", "debug");
}
assert_eq!(determine_level(), log::LevelFilter::Debug);
unsafe {
env::set_var("SQRY_TEST_VERBOSE_LEVEL", "warn");
}
assert_eq!(determine_level(), log::LevelFilter::Warn);
unsafe {
env::set_var("SQRY_TEST_VERBOSE_LEVEL", "error");
}
assert_eq!(determine_level(), log::LevelFilter::Error);
unsafe {
env::set_var("SQRY_TEST_VERBOSE_LEVEL", "off");
}
assert_eq!(determine_level(), log::LevelFilter::Off);
unsafe {
env::remove_var("SQRY_TEST_VERBOSE_LEVEL");
}
}
#[test]
fn test_determine_level_case_insensitive() {
let _lock = ENV_MUTEX.lock().unwrap();
unsafe {
env::remove_var("RUST_LOG");
}
unsafe {
env::set_var("SQRY_TEST_VERBOSE_LEVEL", "DEBUG");
}
assert_eq!(determine_level(), log::LevelFilter::Debug);
unsafe {
env::set_var("SQRY_TEST_VERBOSE_LEVEL", "TrAcE");
}
assert_eq!(determine_level(), log::LevelFilter::Trace);
unsafe {
env::remove_var("SQRY_TEST_VERBOSE_LEVEL");
}
}
#[test]
fn test_determine_level_invalid_value_defaults_to_info() {
let _lock = ENV_MUTEX.lock().unwrap();
unsafe {
env::remove_var("RUST_LOG");
}
unsafe {
env::set_var("SQRY_TEST_VERBOSE_LEVEL", "invalid");
}
assert_eq!(determine_level(), log::LevelFilter::Info);
unsafe {
env::remove_var("SQRY_TEST_VERBOSE_LEVEL");
}
}
#[test]
fn test_init_is_idempotent() {
let _lock = ENV_MUTEX.lock().unwrap();
unsafe {
env::set_var("SQRY_TEST_VERBOSE", "all");
}
init("test-crate");
init("test-crate");
init("test-crate");
unsafe {
env::remove_var("SQRY_TEST_VERBOSE");
}
}
#[test]
fn test_init_with_invalid_crate_does_not_panic() {
let _lock = ENV_MUTEX.lock().unwrap();
unsafe {
env::set_var("SQRY_TEST_VERBOSE", "other-crate");
}
init("sqry-core");
unsafe {
env::remove_var("SQRY_TEST_VERBOSE");
}
}
}