#![allow(dead_code)] use std::{ fs::OpenOptions, io::{BufRead as _, BufReader}, path::{Path, PathBuf}, }; use ckb_logger::Level; use ckb_logger_config::{Config, ExtraLoggerConfig}; use tempfile::TempDir; const DEFAULT_LOG_FILE: &str = "default.log"; const DEFAULT_LOG_ENV: &str = "DEFAULT_LOG_ENV"; const LOG_LEVELS: &[Level] = &[ Level::Trace, Level::Debug, Level::Info, Level::Warn, Level::Error, ]; const LOG_TIMESTAMP_REGEX: &str = r"\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}([.]\d{1,3}) [+-]\d{2}:\d{2}"; pub fn has_line_in_log_file(log_file: &Path, log_level: Level, content_pattern: &str) -> bool { let full_line_pattern = format!(r"^{LOG_TIMESTAMP_REGEX} [^\s]+ {log_level} [^\s]+ {content_pattern}$",); let regex = regex::Regex::new(&full_line_pattern).unwrap(); let file = OpenOptions::new().read(true).open(log_file).unwrap(); for line in BufReader::new(file).lines() { let line_str = line.unwrap(); if regex.is_match(&line_str) { return true; } } false } pub fn test_if_log_file_exists(log_file: &Path, should_exist: bool) { if should_exist { assert!( log_file.exists(), "log file [{}] should exist", log_file.display() ); } else { assert!( !log_file.exists(), "log file [{}] shouldn't exist", log_file.display() ); } } pub fn do_tests<F>(config: Config, func: F) where F: Fn(), { let guard = ckb_logger_service::init(None, config).unwrap(); func(); drop(guard); } pub fn do_tests_with_env<F>(env_filter: &str, config: Config, func: F) where F: Fn(), { std::env::set_var(DEFAULT_LOG_ENV, env_filter); let guard = ckb_logger_service::init(Some(DEFAULT_LOG_ENV), config).unwrap(); func(); drop(guard); } pub fn do_tests_with_silent_logger<F>(func: F) where F: Fn(), { let guard = ckb_logger_service::init_silent().unwrap(); func(); drop(guard); } pub fn config_in_tempdir<F>(func: F) -> (Config, TempDir) where F: Fn(&mut Config), { let tmp_dir = tempfile::Builder::new().tempdir().unwrap(); let mut config = Config { filter: Some(Level::Trace.as_str().to_owned()), file: Path::new(DEFAULT_LOG_FILE).to_path_buf(), log_dir: tmp_dir.path().to_path_buf(), log_to_file: true, log_to_stdout: true, ..Default::default() }; func(&mut config); (config, tmp_dir) } pub fn output_log_for_all_log_levels(log_message: &str) { ckb_logger::error!("{}", log_message); ckb_logger::warn!("{}", log_message); ckb_logger::info!("{}", log_message); ckb_logger::debug!("{}", log_message); ckb_logger::trace!("{}", log_message); } pub fn all_log_levels() -> &'static [Level] { LOG_LEVELS } pub fn update_extra_logger(config: &mut Config, name: &str, filter: &str) { let value = ExtraLoggerConfig { filter: filter.to_owned(), }; config.extra.insert(name.to_owned(), value); } pub fn extra_logger_file(log_dir: &Path, logger_name: &str) -> PathBuf { log_dir.join(format!("{logger_name}.log")) } pub fn apply_new_config() { // waiting for the new configuration to be applied. std::thread::sleep(std::time::Duration::from_secs(1)); } pub fn test_log_to_file(enabled: bool) { let (config, _tmp_dir) = config_in_tempdir(|config| { let file_name = format!("test_log_to_file_{enabled}.log"); config.file = Path::new(&file_name).to_path_buf(); config.log_to_file = enabled; }); let log_file = config.log_dir.join(config.file.as_path()); let line_content = format!("test log_to_file = {enabled}"); do_tests(config, || { ckb_logger::error!("{line_content}"); }); test_if_log_file_exists(&log_file, enabled); if enabled { assert!( has_line_in_log_file(&log_file, Level::Error, &line_content), "line [{}] isn't found in the log [{}]", line_content, log_file.display() ); } }