use file_handler::file_manager::FileManager;
use formatter::{LogColor, LogFormatter};
use from_env::load_config_from_env;
use set_errors::ReadFromConfigFileError;
use set_errors::{
AccessError, AddRotationError, SetArchiveDirError, SetColorizedError, SetCompressionError,
SetFileError, SetLevelFormattingError, SetLogLevelError, SetPrintToTerminalError,
};
use std::sync::Once;
use std::{
path::PathBuf,
sync::{Arc, Mutex, RwLockReadGuard, RwLockWriteGuard},
};
use crate::{
helper::{get_current_date_in_string, get_current_time_in_string},
Config, Level, CONFIG,
};
pub mod archivation;
pub mod file_handler;
pub mod formatter;
pub mod from_env;
pub mod from_file_config;
pub mod set_errors;
struct LogInfo {
module_path: String,
file: String,
line: u32,
message: String,
level: Level,
}
fn with_fm<T, E, F>(f: F) -> Result<T, E>
where
F: FnOnce(&mut FileManager) -> Result<T, E>,
E: From<AccessError>,
{
let fm_arc = {
let cfg_lock = CONFIG.read().map_err(|_| AccessError::LoadConfig)?;
cfg_lock
.file_manager
.as_ref()
.ok_or(AccessError::FileNotSet)?
.clone()
};
let mut guard = fm_arc.lock().unwrap(); f(&mut guard)
}
fn get_log_level() -> Level {
get_config().level
}
fn get_config() -> RwLockReadGuard<'static, Config> {
let config_lock = match CONFIG.read() {
Ok(r) => r,
Err(e) => {
eprintln!("Problem with getting config, here's an error: {}", e);
panic!("Poisoned lock")
}
};
config_lock
}
fn get_log_format(level: Level) -> LogFormatter {
let tmp_cfg = get_config();
match level {
Level::TRACE => tmp_cfg.trace_log_format.clone(),
Level::DEBUG => tmp_cfg.debug_log_format.clone(),
Level::INFO => tmp_cfg.info_log_format.clone(),
Level::WARN => tmp_cfg.warn_log_format.clone(),
Level::ERROR => tmp_cfg.error_log_format.clone(),
}
}
fn get_write_config() -> Option<RwLockWriteGuard<'static, Config>> {
match CONFIG.write() {
Ok(guard) => Some(guard),
Err(e) => {
eprintln!(
"An error while getting the config to write, here's an error: {}",
e
);
None
} }
}
pub fn set_file(format: &str) -> Result<(), SetFileError> {
let file_manager = match FileManager::init_from_string(format, get_config().clone()) {
Ok(r) => r,
Err(e) => {
return Err(SetFileError::UnableToLoadFromString(e));
}
};
let config_lock = get_write_config();
if config_lock.is_none() {
return Err(SetFileError::UnableToLoadConfig);
}
let mut config_lock = config_lock.unwrap();
config_lock.file_manager = Some(Arc::new(Mutex::new(file_manager)));
Ok(())
}
pub fn set_archive_dir(dir: &str) -> Result<PathBuf, SetArchiveDirError> {
let config_lock = get_write_config();
if config_lock.is_none() {
return Err(SetArchiveDirError::UnableToLoadConfig);
}
let path = PathBuf::from(dir);
archivation::ensure_archivable_dir(&path)?;
let mut config_lock = config_lock.unwrap();
config_lock.archive_dir = Some(path.clone());
Ok(path)
}
pub fn load_config_from_file(path: &str) -> Result<(), ReadFromConfigFileError> {
let curr_conf = get_config().clone();
match crate::logger::from_file_config::load_config_from_file(path) {
Ok(_) => Ok(()),
Err(e) => {
let wc = get_write_config(); if wc.is_some() {
let mut wc_c = wc.unwrap();
*wc_c = curr_conf;
}
Err(e)
}
}
}
pub fn set_compression(ctype: &str) -> Result<(), SetCompressionError> {
with_fm(|fm| {
if fm.set_compression(ctype) {
Ok(())
} else {
Err(SetCompressionError::IncorrectCompressionValue)
}
})
}
pub fn add_rotation(constraint: &str) -> Result<(), AddRotationError> {
with_fm(|fm| {
if fm.add_rotation(constraint) {
Ok(())
} else {
Err(AddRotationError::IncorrectFormatGiven)
}
})
}
pub fn set_log_level(lvl: Level) -> Result<(), SetLogLevelError> {
let config_lock = get_write_config();
if config_lock.is_none() {
eprintln!("An error while getting the config to write!");
return Err(SetLogLevelError::UnableToLoadConfig);
}
let mut config_lock = config_lock.unwrap();
config_lock.level = lvl;
Ok(())
}
pub fn set_print_to_terminal(val: bool) -> Result<(), SetPrintToTerminalError> {
let config_lock = get_write_config();
if config_lock.is_none() {
eprintln!("An error while getting the config to write!");
return Err(SetPrintToTerminalError::UnableToLoadConfig);
}
let mut config_lock = config_lock.unwrap();
config_lock.print_to_terminal = val;
Ok(())
}
pub fn set_colorized(val: bool) -> Result<(), SetColorizedError> {
let config_lock = get_write_config();
if config_lock.is_none() {
eprintln!("An error while getting the config to write!");
return Err(SetColorizedError::UnableToLoadConfig);
}
let mut config_lock = config_lock.unwrap();
config_lock.colorized = val;
Ok(())
}
pub fn set_global_formatting(format: &str) -> Result<(), SetLevelFormattingError> {
set_level_formatting(Level::TRACE, format)?;
set_level_formatting(Level::DEBUG, format)?;
set_level_formatting(Level::INFO, format)?;
set_level_formatting(Level::WARN, format)?;
set_level_formatting(Level::ERROR, format)?;
Ok(())
}
pub fn set_level_formatting(level: Level, format: &str) -> Result<(), SetLevelFormattingError> {
let config_lock = get_write_config();
if config_lock.is_none() {
eprintln!("An error while getting the config to write!");
return Err(SetLevelFormattingError::UnableToLoadConfig);
}
let mut config_lock = config_lock.unwrap();
match level {
Level::TRACE => config_lock.trace_log_format = LogFormatter::parse_from_string(format)?,
Level::DEBUG => config_lock.debug_log_format = LogFormatter::parse_from_string(format)?,
Level::INFO => config_lock.info_log_format = LogFormatter::parse_from_string(format)?,
Level::WARN => config_lock.warn_log_format = LogFormatter::parse_from_string(format)?,
Level::ERROR => config_lock.error_log_format = LogFormatter::parse_from_string(format)?,
}
Ok(())
}
fn string_log(log_info: &LogInfo, colorize: bool) -> String {
let mut mess_to_print = String::new();
let curr_time: String = get_current_time_in_string();
let curr_date = get_current_date_in_string();
for log_part in get_log_format(log_info.level).parts {
let str_to_push = match log_part.part {
formatter::LogPart::Message => &log_info.message,
formatter::LogPart::Time => &curr_time,
formatter::LogPart::File => &log_info.file,
formatter::LogPart::Line => &log_info.line.to_string(),
formatter::LogPart::Date => &curr_date,
formatter::LogPart::Level => &log_info.level.to_string(),
formatter::LogPart::Text(text) => &text.clone(),
formatter::LogPart::ModulePath => &log_info.module_path,
};
if colorize && log_part.color.is_some() {
let colored_str = LogColor::colorize_str(str_to_push, log_part.color.unwrap());
mess_to_print.push_str(&colored_str);
} else {
mess_to_print.push_str(str_to_push);
}
}
mess_to_print
}
fn print_log(log_info: &LogInfo) {
let mess_to_print = string_log(log_info, get_config().colorized);
match log_info.level {
Level::ERROR => eprintln!("{}", mess_to_print),
_ => println!("{}", mess_to_print),
};
}
fn write_file_log(log_info: &LogInfo) {
let mess_to_print = string_log(log_info, false);
let cfg_snapshot = get_config().clone();
let _ = with_fm::<(), AccessError, _>(|file_manager| {
let res = file_manager.write_log(&mess_to_print, cfg_snapshot);
match res {
Ok(_) => Ok(()),
Err(e) => {
eprintln!(
"Couldn't write a log to the file due to the next error: {}",
e
);
Ok(()) }
}
});
}
fn log_handler(log_info: LogInfo) {
if get_config().print_to_terminal {
print_log(&log_info);
}
if get_config().file_manager.is_some() {
write_file_log(&log_info);
}
}
fn macro_handler(module_path: &str, file: &str, line: u32, deb_str: String, level: Level) {
let log_info = LogInfo {
module_path: module_path.to_string(),
file: file.to_string(),
line,
message: deb_str,
level,
};
if level >= get_log_level() {
log_handler(log_info);
}
}
pub fn __debug_handler(module_path: &str, file: &str, line: u32, deb_str: String, level: Level) {
macro_handler(module_path, file, line, deb_str, level);
}
#[macro_export]
macro_rules! trace {
($($arg:tt)*) => {{
let res_str = format!($($arg)*);
$crate::logger::__debug_handler(module_path!(), file!(), line!(), res_str, $crate::Level::TRACE);
}};
}
#[macro_export]
macro_rules! debug {
($($arg:tt)*) => {{
let res_str = format!($($arg)*);
$crate::logger::__debug_handler(module_path!(), file!(), line!(), res_str, $crate::Level::DEBUG);
}};
}
#[macro_export]
macro_rules! info {
($($arg:tt)*) => {{
let res_str = format!($($arg)*);
$crate::logger::__debug_handler(module_path!(), file!(), line!(), res_str, $crate::Level::INFO);
}};
}
#[macro_export]
macro_rules! warn {
($($arg:tt)*) => {{
let res_str = format!($($arg)*);
$crate::logger::__debug_handler(module_path!(), file!(), line!(), res_str, $crate::Level::WARN);
}};
}
#[macro_export]
macro_rules! error {
($($arg:tt)*) => {{
let res_str = format!($($arg)*);
$crate::logger::__debug_handler(module_path!(), file!(), line!(), res_str, $crate::Level::ERROR);
}};
}
pub fn init() {
let mut config = CONFIG.write().unwrap();
*config = Config {
..Default::default()
}
}
pub fn init_with_imports() {
init();
let file_names = ["loggit.env", "loggit.ini", "loggit.json"];
for file_name in file_names {
if let Ok(r) = std::fs::exists(std::path::Path::new(file_name)) {
if !r {
continue;
}
let load_rs = load_config_from_file(file_name);
if load_rs.is_ok() {
break;
}
}
}
let _ = load_config_from_env();
}