use std::{ fs::OpenOptions, io::{self, BufWriter, Stdout, Write}, path::{Path, PathBuf}, str::FromStr, sync::{mpsc, Arc, RwLock} }; use tracing::{error, info}; use tracing_subscriber::fmt::format::FmtSpan; use tracing_subscriber::{Layer, Registry, layer::SubscriberExt, util::SubscriberInitExt}; use winit::event_loop::EventLoop; #[derive(Debug, Default)] struct App; struct BufferWriter { buffer: Arc>>, } impl Write for BufferWriter { fn write(&mut self, buf: &[u8]) -> io::Result { if let Ok(mut guard) = self.buffer.write() { guard.extend_from_slice(buf); Ok(buf.len()) } else { Err(io::Error::new( io::ErrorKind::Other, "Failed to acquire write lock on log buffer", )) } } fn flush(&mut self) -> io::Result<()> { Ok(()) } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)] enum LogLevel { Error, #[default] Info, Debug, Trace, } impl std::fmt::Display for LogLevel { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f,"{self:?}") } } impl FromStr for LogLevel { type Err = (); fn from_str(s: &str) -> Result { match s { "trace" => Ok(Self::Trace), "debug" => Ok(Self::Debug), "info" => Ok(Self::Info), "error" => Ok(Self::Error), _ => Err(()), } } } #[derive(Debug)] pub struct LoggerConfig { pub log_level: LogLevel, pub log_to_file: bool, pub log_file_path: PathBuf, pub log_to_stdout: bool, pub stdout_color: bool, pub log_to_buffer: bool, } impl LoggerConfig { pub fn level(mut self,level: LogLevel) -> Self { self.log_level = level; self } pub fn log_to_file(mut self,log_to_file: bool ) -> Self { self.log_to_file = log_to_file; self } pub fn colored_stdout(mut self,show_color: bool ) -> Self { self.stdout_color = show_color; self } pub fn log_to_stdout(mut self,stdout: bool ) -> Self { self.log_to_stdout = stdout; self } pub fn log_path>(mut self,path: P ) -> Self { self.log_file_path = path.as_ref().to_path_buf(); self } } impl Default for LoggerConfig { fn default() -> Self { Self { log_level: LogLevel::Debug, log_to_file: true, log_file_path: "zenyx.log".into(), log_to_stdout: true, stdout_color: true, log_to_buffer: true, } } } pub struct Logger { config: LoggerConfig, log_buffer: Arc>>, } enum Log { All, From(std::time::SystemTime), } impl Logger { pub fn new(config: LoggerConfig) -> Self { Self { config, log_buffer: Arc::new(RwLock::new(Vec::new())), } } pub fn init(&self) { let env_filter = tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| { format!("{}={}", env!("CARGO_CRATE_NAME"), self.config.log_level).into() }); let mut layers = Vec::new(); if self.config.log_to_stdout { let fmt_layer_stdout = tracing_subscriber::fmt::layer() .with_level(true) .compact() .with_ansi(self.config.stdout_color) .log_internal_errors(false) .without_time() .with_thread_names(true) .with_span_events(FmtSpan::CLOSE) .with_writer(std::io::stdout) .boxed(); layers.push(fmt_layer_stdout); } if self.config.log_to_file { let log_file_path = self.config.log_file_path.clone(); let fmt_layer_file = tracing_subscriber::fmt::layer() .with_level(true) .compact() .with_ansi(false) .log_internal_errors(false) .without_time() .with_thread_names(true) .with_span_events(FmtSpan::CLOSE) .with_writer(move || { let file = OpenOptions::new() .append(true) .open(&log_file_path) .unwrap_or_else(|_| { OpenOptions::new() .write(true) .create(true) .truncate(true) .open(&log_file_path) .expect("Failed to create log file") }); BufWriter::new(file) }) .boxed(); layers.push(fmt_layer_file); } // let (tx,rx) = mpsc::channel(); // std::thread::spawn(move || { // for log in rx.iter() { // println!("{log:#?}") // } // }); if self.config.log_to_buffer { let log_buffer = Arc::clone(&self.log_buffer); let fmt_layer_buffer = tracing_subscriber::fmt::layer() .with_level(true) .compact() .with_ansi(false) .log_internal_errors(false) .without_time() .with_thread_names(true) .with_span_events(FmtSpan::CLOSE) .with_writer(move || BufferWriter { buffer: log_buffer.clone(), }) .boxed(); layers.push(fmt_layer_buffer); } if !layers.is_empty() { let subscriber = Registry::default().with(env_filter).with(layers); subscriber.init(); } } pub fn get_logs(&self) -> Option { if !self.config.log_to_buffer { return None; } self.log_buffer .read() .ok() .map(|guard| String::from_utf8_lossy(&guard).into_owned()) } } fn main() -> Result<(), Box> { let logger_config = LoggerConfig::default() .colored_stdout(true) .level(LogLevel::Debug) .log_to_file(true) .log_path(format!("{}.log",env!("CARGO_PKG_NAME"))); let logger = Logger::new(logger_config); logger.init(); let event_loop = EventLoop::new()?; info!("Application initialized"); let app = App::default(); info!("This is a test log message"); error!("Another log message with value: {}", 42); if let Some(logs) = logger.get_logs() { println!("\n--- Logs from Buffer ---"); println!("{}", logs); println!("------------------------"); } else { eprintln!("Failed to read logs from buffer or buffer logging is disabled"); } Ok(()) }