use anyhow::{Context, Result};
use env_logger::Builder;
use log::{debug, warn, LevelFilter};
use rolling_file::{BasicRollingFileAppender, RollingConditionBasic};
use std::fs::{create_dir_all, read_dir, remove_file};
use std::path::{Path, PathBuf};
use std::sync::Mutex;
pub fn initialize_logger(log_level: LevelFilter) -> Result<()> {
let max_log_files = 2;
let log_dir = directories::UserDirs::new()
.and_then(|dirs| dirs.document_dir().map(|d| d.join("frames_exporter_logs")))
.unwrap_or_else(|| PathBuf::from("logs"));
debug!("Initializing logger with log directory: {:?}", log_dir);
create_dir_all(&log_dir).context("Failed to create log directory")?;
let log_file_path = log_dir.join("app.log");
let size_limit = 5 * 1024 * 1024;
let rolling_condition = RollingConditionBasic::new().max_size(size_limit);
let rolling_appender =
BasicRollingFileAppender::new(log_file_path.clone(), rolling_condition, max_log_files)
.context("Failed to create rolling file appender")?;
debug!(
"Rolling file appender created successfully with max size: {} bytes",
size_limit
);
let rolling_appender = Mutex::new(rolling_appender);
manage_log_files(&log_dir, max_log_files).context("Failed to manage log files")?;
let mut builder = Builder::new();
builder.filter(None, log_level);
builder.format(move |buf, record| {
use console::style;
use std::io::Write;
let ts = buf.timestamp();
let level = record.level();
let msg = record.args();
let color = match record.level() {
log::Level::Error => console::Color::Red,
log::Level::Warn => console::Color::Yellow,
log::Level::Info => console::Color::Green,
log::Level::Debug => console::Color::Blue,
log::Level::Trace => console::Color::Cyan,
};
let styled_level = style(record.level()).fg(color);
writeln!(buf, "[{:<5}] {} - {}", styled_level, ts, msg)?;
let log_entry = format!("{} - {} - {}\n", ts, level, msg);
if let Ok(mut appender) = rolling_appender.lock() {
if let Err(e) = appender.write(log_entry.as_bytes()) {
warn!("Failed to write log entry to file: {:?}", e);
}
}
Ok(())
});
builder.try_init().context("Failed to initialize logger")?;
debug!(
"Logger initialized successfully. Logs will be written to {:?}",
log_file_path
);
Ok(())
}
fn manage_log_files(log_dir: &Path, max_log_files: usize) -> Result<()> {
debug!("Managing log files in directory: {:?}", log_dir);
let entries = read_dir(log_dir).context("Failed to read log directory")?;
debug!("Successfully read log directory: {:?}", log_dir);
let mut log_files: Vec<PathBuf> = entries
.filter_map(|entry| {
let path = entry.ok().map(|e| e.path());
if let Some(ref p) = path {
debug!("Found file: {:?}", p);
}
path
})
.filter(|path| {
let is_log = path.is_file() && path.extension().map_or(false, |ext| ext == "log");
if is_log {
debug!("Identified as log file: {:?}", path);
}
is_log
})
.collect();
debug!("Total log files found: {}", log_files.len());
log_files.sort_by_key(|path| path.metadata().and_then(|m| m.modified()).ok());
debug!("Log files sorted by modification time.");
while log_files.len() > max_log_files {
if let Some(old_file) = log_files.get(0).cloned() {
debug!("Attempting to delete old log file: {:?}", old_file);
if let Err(e) = remove_file(&old_file) {
warn!("Failed to delete old log file {:?}: {}", old_file, e);
} else {
debug!("Successfully deleted old log file: {:?}", old_file);
log_files.remove(0);
debug!("Remaining log files: {}", log_files.len());
}
}
}
debug!("Log file management completed successfully.");
Ok(())
}