Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 121 additions & 55 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use notify_rust::Notification;
use backtrace::Backtrace;
use chrono::{Local, NaiveDate};
use notify_rust::Notification;
use std::env;
use std::fs;
use std::io::Write;
use backtrace::Backtrace;
use std::sync::mpsc::{channel, Sender};
use std::thread;
use std::panic;
use std::sync::mpsc::{Sender, channel};
use std::thread;

pub struct VersaLog {
enum_mode: String,
Expand Down Expand Up @@ -44,12 +44,25 @@ static RESET: &str = "\x1b[0m";
static VALID_MODES: &[&str] = &["simple", "simple2", "detailed", "file"];
static VALID_SAVE_LEVELS: &[&str] = &["INFO", "ERROR", "WARNING", "DEBUG", "CRITICAL"];

pub fn NewVersaLog(enum_mode: &str, show_file: bool, show_tag: bool, tag: &str, enable_all: bool, notice: bool, all_save: bool, save_levels: Vec<String>, catch_exceptions: bool) -> VersaLog {
pub fn NewVersaLog(
enum_mode: &str,
show_file: bool,
show_tag: bool,
tag: &str,
enable_all: bool,
notice: bool,
all_save: bool,
save_levels: Vec<String>,
catch_exceptions: bool,
) -> VersaLog {
let mode = enum_mode.to_lowercase();
let tag = tag.to_string();

if !VALID_MODES.contains(&enum_mode) {
panic!("Invalid mode '{}' specified. Valid modes are: simple, simple2, detailed, file", enum_mode);
panic!(
"Invalid mode '{}' specified. Valid modes are: simple, simple2, detailed, file",
enum_mode
);
}

let mut showFile = show_file;
Expand All @@ -75,7 +88,10 @@ pub fn NewVersaLog(enum_mode: &str, show_file: bool, show_tag: bool, tag: &str,
} else {
for level in &savelevels {
if !VALID_SAVE_LEVELS.contains(&level.as_str()) {
panic!("Invalid saveLevels specified. Valid levels are: {:?}", VALID_SAVE_LEVELS);
panic!(
"Invalid saveLevels specified. Valid levels are: {:?}",
VALID_SAVE_LEVELS
);
}
}
}
Expand All @@ -102,7 +118,9 @@ pub fn NewVersaLog(enum_mode: &str, show_file: bool, show_tag: bool, tag: &str,
}
});
Some(tx)
} else { None };
} else {
None
};

VersaLog {
enum_mode: enum_mode.to_string(),
Expand All @@ -121,45 +139,70 @@ pub fn NewVersaLog(enum_mode: &str, show_file: bool, show_tag: bool, tag: &str,
}

pub fn NewVersaLogSimple(enum_mode: &str, tag: &str) -> VersaLog {
NewVersaLog(enum_mode, false, false, tag, false, false, false, Vec::new(), false)
NewVersaLog(
enum_mode,
false,
false,
tag,
false,
false,
false,
Vec::new(),
false,
)
}

pub fn NewVersaLogSimple2(enum_mode: &str, tag: &str, enable_all: bool) -> VersaLog {
NewVersaLog(enum_mode, false, false, tag, enable_all, false, false, Vec::new(), false)
NewVersaLog(
enum_mode,
false,
false,
tag,
enable_all,
false,
false,
Vec::new(),
false,
)
}

impl VersaLog {
pub fn log(&self, msg: String, level: String, tags: &[&str]) {
let level = level.to_uppercase();

let color = COLORS.iter()

let color = COLORS
.iter()
.find(|(l, _)| *l == level)
.map(|(_, c)| *c)
.unwrap_or("");
let symbol = SYMBOLS.iter()
let symbol = SYMBOLS
.iter()
.find(|(l, _)| *l == level)
.map(|(_, s)| *s)
.unwrap_or("");

let caller = if self.showFile || self.enum_mode == "file" {
self.get_caller()
} else {
String::new()
};

let final_tag = if !tags.is_empty() && !tags[0].is_empty() {
tags[0].to_string()
} else if self.showTag && !self.tag.is_empty() {
self.tag.clone()
} else {
String::new()
};

let (output, plain) = match self.enum_mode.as_str() {
"simple" => {
if self.showFile {
if !final_tag.is_empty() {
let output = format!("[{}][{}]{}{}{} {}", caller, final_tag, color, symbol, RESET, msg);
let output = format!(
"[{}][{}]{}{}{} {}",
caller, final_tag, color, symbol, RESET, msg
);
let plain = format!("[{}][{}]{} {}", caller, final_tag, symbol, msg);
(output, plain)
} else {
Expand All @@ -178,16 +221,25 @@ impl VersaLog {
(output, plain)
}
}
},
}
"simple2" => {
let timestamp = self.get_time();
if self.showFile {
if !final_tag.is_empty() {
let output = format!("[{}] [{}][{}]{}{}{} {}", timestamp, caller, final_tag, color, symbol, RESET, msg);
let plain = format!("[{}] [{}][{}]{} {}", timestamp, caller, final_tag, symbol, msg);
let output = format!(
"[{}] [{}][{}]{}{}{} {}",
timestamp, caller, final_tag, color, symbol, RESET, msg
);
let plain = format!(
"[{}] [{}][{}]{} {}",
timestamp, caller, final_tag, symbol, msg
);
(output, plain)
} else {
let output = format!("[{}] [{}]{}{}{} {}", timestamp, caller, color, symbol, RESET, msg);
let output = format!(
"[{}] [{}]{}{}{} {}",
timestamp, caller, color, symbol, RESET, msg
);
let plain = format!("[{}] [{}]{} {}", timestamp, caller, symbol, msg);
(output, plain)
}
Expand All @@ -196,39 +248,39 @@ impl VersaLog {
let plain = format!("[{}] {} {}", timestamp, symbol, msg);
(output, plain)
}
},
}
"file" => {
let output = format!("[{}]{}{}[{}]{}", caller, color, level, RESET, msg);
let plain = format!("[{}][{}] {}", caller, level, msg);
(output, plain)
},
}
_ => {
let timestamp = self.get_time();
let mut output = format!("[{}]{}{}[{}]", timestamp, color, level, RESET);
let mut output = format!("[{}]{}{}[{}]{}", timestamp, color, level, RESET);
let mut plain = format!("[{}][{}]", timestamp, level);

if !final_tag.is_empty() {
output.push_str(&format!("[{}]", final_tag));
plain.push_str(&format!("[{}]", final_tag));
}

if self.showFile {
output.push_str(&format!("[{}]", caller));
plain.push_str(&format!("[{}]", caller));
}

output.push_str(&format!(" : {}", msg));
plain.push_str(&format!(" : {}", msg));

(output, plain)
}
};

if !self.silent {
println!("{}", output);
}
self.save_log(plain, level.clone());

if self.notice && (level == "ERROR" || level == "CRITICAL") {
let _ = Notification::new()
.summary(&format!("{} Log notice", level))
Expand All @@ -255,7 +307,12 @@ impl VersaLog {

let mut details = String::new();
if let Some(loc) = info.location() {
details.push_str(&format!("at {}:{}:{}\n", loc.file(), loc.line(), loc.column()));
details.push_str(&format!(
"at {}:{}:{}\n",
loc.file(),
loc.line(),
loc.column()
));
}
let bt = Backtrace::new();
details.push_str(&format!("{:?}", bt));
Expand All @@ -265,75 +322,77 @@ impl VersaLog {
}

pub fn handle_exception(&self, exc_type: &str, exc_value: &str, exc_traceback: &str) {
let tb_str = format!("Exception Type: {}\nException Value: {}\nTraceback:\n{}",
exc_type, exc_value, exc_traceback);
let tb_str = format!(
"Exception Type: {}\nException Value: {}\nTraceback:\n{}",
exc_type, exc_value, exc_traceback
);
self.Critical_no_tag(&format!("Unhandled exception:\n{}", tb_str));
}

pub fn Info(&self, msg: &str, tags: &[&str]) {
self.log(msg.to_string(), "INFO".to_string(), tags);
}

pub fn Error(&self, msg: &str, tags: &[&str]) {
self.log(msg.to_string(), "ERROR".to_string(), tags);
}

pub fn Warning(&self, msg: &str, tags: &[&str]) {
self.log(msg.to_string(), "WARNING".to_string(), tags);
}

pub fn Debug(&self, msg: &str, tags: &[&str]) {
self.log(msg.to_string(), "DEBUG".to_string(), tags);
}

pub fn Critical(&self, msg: &str, tags: &[&str]) {
self.log(msg.to_string(), "CRITICAL".to_string(), tags);
}

pub fn info(&self, msg: &str, tags: &[&str]) {
self.Info(msg, tags);
}

pub fn error(&self, msg: &str, tags: &[&str]) {
self.Error(msg, tags);
}

pub fn warning(&self, msg: &str, tags: &[&str]) {
self.Warning(msg, tags);
}

pub fn debug(&self, msg: &str, tags: &[&str]) {
self.Debug(msg, tags);
}

pub fn critical(&self, msg: &str, tags: &[&str]) {
self.Critical(msg, tags);
}

pub fn Info_no_tag(&self, msg: &str) {
self.log(msg.to_string(), "INFO".to_string(), &[]);
}

pub fn Error_no_tag(&self, msg: &str) {
self.log(msg.to_string(), "ERROR".to_string(), &[]);
}

pub fn Warning_no_tag(&self, msg: &str) {
self.log(msg.to_string(), "WARNING".to_string(), &[]);
}

pub fn Debug_no_tag(&self, msg: &str) {
self.log(msg.to_string(), "DEBUG".to_string(), &[]);
}

pub fn Critical_no_tag(&self, msg: &str) {
self.log(msg.to_string(), "CRITICAL".to_string(), &[]);
}

fn get_time(&self) -> String {
Local::now().format("%Y-%m-%d %H:%M:%S").to_string()
}

fn get_caller(&self) -> String {
let bt = Backtrace::new();
if let Some(frame) = bt.frames().get(3) {
Expand All @@ -349,28 +408,35 @@ impl VersaLog {
}
"unknown:0".to_string()
}

fn cleanup_old_logs(&self, days: i64) {
let cwd = env::current_dir().unwrap_or_else(|_| env::current_dir().unwrap());
let log_dir = cwd.join("log");

if !log_dir.exists() {
return;
}

let now = Local::now().naive_local().date();

if let Ok(entries) = fs::read_dir(&log_dir) {
for entry in entries {
if let Ok(entry) = entry {
let path = entry.path();
if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("log") {
if let Some(file_name) = path.file_name().and_then(|n| n.to_str()) {
if let Ok(file_date) = NaiveDate::parse_from_str(&file_name.replace(".log", ""), "%Y-%m-%d") {
if let Ok(file_date) = NaiveDate::parse_from_str(
&file_name.replace(".log", ""),
"%Y-%m-%d",
) {
if (now - file_date).num_days() >= days {
if let Err(e) = fs::remove_file(&path) {
if !self.silent {
println!("[LOG CLEANUP WARNING] {} cannot be removed: {}", path.display(), e);
println!(
"[LOG CLEANUP WARNING] {} cannot be removed: {}",
path.display(),
e
);
}
} else if !self.silent {
println!("[LOG CLEANUP] removed: {}", path.display());
Expand Down Expand Up @@ -422,4 +488,4 @@ impl VersaLog {
self.cleanup_old_logs(7);
}
}
}
}
Loading