use std::{
path::{PathBuf, Path},
result::Result,
io::{Write},
fs::{self, OpenOptions},
sync::Arc
};
use chrono::Local;
use tracing_subscriber::prelude::*;
use crate::error::CryptError;
use std::sync::Mutex;
use once_cell::sync::Lazy;
pub static LOGGER: Lazy<Mutex<Log>> = Lazy::new(|| Mutex::new(Log {
activated: false,
log: String::new(),
location: None,
}));
pub struct Log {
pub activated: bool,
pub log: String,
pub location: Option<PathBuf>,
}
pub fn initialize_logger(log_file: PathBuf) {
let Ok(mut logger) = LOGGER.lock() else { return; };
logger.activated = true;
logger.location = Some(log_file.to_owned());
if let (Some(parent), Some(file_name)) = (log_file.parent(), log_file.file_name().and_then(|s| s.to_str())) {
let _ = std::fs::create_dir_all(parent);
let appender = tracing_appender::rolling::never(parent, file_name);
let fmt_layer = tracing_subscriber::fmt::layer()
.with_ansi(false)
.with_target(false)
.without_time()
.with_level(false)
.with_writer(appender);
let subscriber = tracing_subscriber::registry().with(fmt_layer);
let _ = tracing::subscriber::set_global_default(subscriber);
}
}
impl Log {
pub fn activate(log_file: PathBuf) -> Self {
Log {
activated: true,
log: String::new(),
location: Some(log_file),
}
}
pub fn clear(&mut self) {
self.log.clear();
}
pub fn append_log(&mut self, process: &str, detail: &str) -> Result<(), CryptError> {
if self.activated {
let timestamp = Local::now();
let log_entry = format!("{}:\n\t{}{}\n", timestamp.format("%m/%d/%y %H:%M"), process, detail);
let log_entry: String = if !self.log.is_empty() {
format!("\n{}", log_entry)
} else {
log_entry
};
self.log.push_str(&log_entry);
tracing::info!(target: "crypt_guard", "{}", log_entry);
}
Ok(())
}
pub fn write_log_file(&mut self) -> Result<(), CryptError> {
if !self.activated {
return Ok(());
}
if let Some(ref location) = self.location {
let parent_dir = location.parent().unwrap_or_else(|| Path::new(""));
let file_stem = match location.file_stem().and_then(|s| s.to_str()) {
Some(s) => s,
None => return Err(CryptError::CustomError("Invalid log file name".to_string())),
};
let extension = match location.extension().and_then(|s| s.to_str()) {
Some(s) => s,
None => "log",
};
let log_dir = parent_dir.join(file_stem);
if !log_dir.exists() {
fs::create_dir_all(&log_dir).map_err(|e| CryptError::IOError(Arc::new(e)))?;
}
let mut file_path = log_dir.join(format!("{}.{}", file_stem, extension));
let mut counter = 1;
while file_path.exists() {
file_path = log_dir.join(format!("{}_{}.{}", file_stem, counter, extension));
counter += 1;
}
let mut file = OpenOptions::new().create(true).write(true).truncate(true).open(file_path)?;
write!(file, "{}", self.log)?;
self.log.clear();
} else {
return Err(CryptError::CustomError("Log location not set.".to_string()));
}
Ok(())
}
}