use std::fmt;
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::sync::Mutex;
use std::time::{SystemTime, UNIX_EPOCH};
use super::color;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Level {
Debug,
Info,
Warn,
Error,
}
impl fmt::Display for Level {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if !color::supports_color() {
return write!(f, "{}", self.plain_text());
}
match self {
Level::Debug => write!(f, "\x1b[36mDEBUG\x1b[0m"),
Level::Info => write!(f, "\x1b[32mINFO\x1b[0m"),
Level::Warn => write!(f, "\x1b[33mWARN\x1b[0m"),
Level::Error => write!(f, "\x1b[31mERROR\x1b[0m"),
}
}
}
impl Level {
fn plain_text(&self) -> &'static str {
match self {
Level::Debug => "DEBUG",
Level::Info => "INFO",
Level::Warn => "WARN",
Level::Error => "ERROR",
}
}
}
pub struct Logger {
level: Level,
file: Option<Mutex<File>>,
supports_color: bool,
}
impl Logger {
pub fn new(level: Level) -> Self {
Logger {
level,
file: None,
supports_color: color::supports_color(),
}
}
pub fn set_level(&mut self, level: Level) {
self.level = level;
}
pub fn set_file(&mut self, path: &str) -> std::io::Result<()> {
let file = OpenOptions::new()
.create(true)
.write(true)
.append(true)
.open(path)?;
self.file = Some(Mutex::new(file));
Ok(())
}
fn get_timestamp() -> String {
let start = SystemTime::now();
let since_epoch = start
.duration_since(UNIX_EPOCH)
.unwrap_or_else(|_| std::time::Duration::from_secs(0));
let secs = since_epoch.as_secs();
let nanos = since_epoch.subsec_nanos();
format!("{}.{:09}", secs, nanos)
}
fn log(&self, level: Level, message: &str) {
if level as u8 >= self.level as u8 {
let timestamp = Self::get_timestamp();
let console_message = if self.supports_color {
format!("[{}] {} {}\n", timestamp, level, message)
} else {
format!("[{}] {} {}\n", timestamp, level.plain_text(), message)
};
print!("{}", console_message);
if let Some(file) = &self.file {
if let Ok(mut file) = file.lock() {
let file_message = format!("[{}] {} {}\n", timestamp, level.plain_text(), message);
let _ = file.write_all(file_message.as_bytes());
let _ = file.flush();
}
}
}
}
pub fn debug(&self, message: &str) {
self.log(Level::Debug, message);
}
pub fn info(&self, message: &str) {
self.log(Level::Info, message);
}
pub fn warn(&self, message: &str) {
self.log(Level::Warn, message);
}
pub fn error(&self, message: &str) {
self.log(Level::Error, message);
}
}