// src/util/logger.rs use chrono::Local; use once_cell::sync::Lazy; use tokio::sync::Mutex; use std::fs::{self, OpenOptions}; use std::io::Write; use std::path::PathBuf; static LOGGER: Lazy>> = Lazy::new(|| Mutex::new(None)); pub struct DebugLogger { file: std::fs::File, log_path: PathBuf, } impl DebugLogger { fn new(log_dir: &std::path::Path) -> std::io::Result { fs::create_dir_all(log_dir)?; let filename = format!("backtest_{}.log", Local::now().format("%Y%m%d_%H%M%S")); let log_path = log_dir.join(&filename); let file = OpenOptions::new() .create(true) .append(true) .open(&log_path)?; Ok(Self { file, log_path }) } async fn log(&mut self, msg: &str) { let line = format!("[{}] {}\n", Local::now().format("%H:%M:%S"), msg); let _ = self.file.write_all(line.as_bytes()); let _ = self.file.flush(); println!("{}", line.trim_end()); } } pub async fn init_debug_logger(log_dir: &std::path::Path) -> Result<(), String> { let mut logger = LOGGER.lock().await; match DebugLogger::new(log_dir) { Ok(l) => { let log_path = l.log_path.clone(); *logger = Some(l); println!("✓ Logger initialized at: {:?}", log_path); Ok(()) } Err(e) => { let err_msg = format!("Failed to initialize logger: {}", e); eprintln!("{}", err_msg); Err(err_msg) } } } pub async fn log_message(msg: &str) { let mut logger = LOGGER.lock().await; if let Some(l) = logger.as_mut() { l.log(msg).await; } else { println!("[LOG] {}", msg); } } pub async fn log_detailed(level: &str, msg: &str) { let formatted = format!("[{}] {}", level, msg); log_message(&formatted).await; } pub async fn log_info(msg: &str) { log_detailed("INFO", msg).await; } pub async fn log_warn(msg: &str) { log_detailed("WARN", msg).await; } pub async fn log_error(msg: &str) { log_detailed("ERROR", msg).await; }