#[macro_use]
extern crate lazy_static;
extern crate chrono;
extern crate regex;
mod log_file;
mod logger;
mod stringifier;
mod table;
mod toml;
use crate::logger::Logger;
use crate::logger::LOGGER;
use crate::stringifier::Stringifier;
use crate::table::InternalTable;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::collections::VecDeque;
use std::fmt;
use std::io::{BufWriter, Write};
use std::sync::Mutex;
use std::thread;
#[cfg(windows)]
const NEW_LINE: &'static str = "\r\n";
#[cfg(not(windows))]
const NEW_LINE: &'static str = "\n";
pub const DEFAULT_LOG_LEVEL: Level = Level::Trace;
pub const DEFAULT_RETENTION_DAYS: i64 = 7;
pub const DEFAULT_TIMEOUT_SECS: u64 = 30;
pub const DEFAULT_OPTIMIZATION: Opt = Opt::BeginnersSupport;
#[derive(Clone, Copy, Debug)]
pub enum Level {
Fatal,
Error,
Warn,
Notice,
Info,
Debug,
Trace,
}
impl Level {
pub fn number(&self) -> usize {
match self {
Level::Fatal => 1,
Level::Error => 2,
Level::Warn => 3,
Level::Notice => 4,
Level::Info => 5,
Level::Debug => 6,
Level::Trace => 7,
}
}
}
impl fmt::Display for Level {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Level::Fatal => write!(f, "Fatal"),
Level::Error => write!(f, "Error"),
Level::Warn => write!(f, "Warn"),
Level::Notice => write!(f, "Notice"),
Level::Info => write!(f, "Info"),
Level::Debug => write!(f, "Debug"),
Level::Trace => write!(f, "Trace"),
}
}
}
lazy_static! {
static ref QUEUE_T: Mutex<VecDeque<InternalTable>> = Mutex::new(VecDeque::<InternalTable>::new());
static ref QUEUE_F: Mutex<VecDeque<InternalTable>> = Mutex::new(VecDeque::<InternalTable>::new());
static ref RESERVE_TARGET: Mutex<ReserveTarget> = Mutex::new(ReserveTarget::default());
static ref SIGNAL_CAN_FLUSH: Mutex<SignalCanFlush> = Mutex::new(SignalCanFlush::default());
static ref OPT_STATE: Mutex<OptState> = Mutex::new(OptState::default());
}
thread_local!(static SEQ: RefCell<u128> = {
RefCell::new(1)
});
#[derive(Clone)]
pub struct ArrayOfTable {
tables: Vec<Table>,
}
#[derive(Clone)]
pub struct Table {
base_name: String,
level: Level,
message: String,
message_trailing_newline: bool,
sorted_map: Option<BTreeMap<String, String>>,
sub_tables: Option<BTreeMap<String, InternalTable>>,
}
impl Table {
fn new(level: Level, message: &str, trailing_newline: bool, base_name: &str) -> Self {
Table {
base_name: base_name.to_string(),
level: level,
message: message.to_string(),
message_trailing_newline: trailing_newline,
sorted_map: None,
sub_tables: None,
}
}
fn get_sorted_map<F>(&mut self, mut callback: F)
where
F: FnMut(&mut BTreeMap<String, String>),
{
if let None = self.sorted_map {
self.sorted_map = Some(BTreeMap::new());
}
if let Some(sorted_map) = &mut self.sorted_map {
callback(sorted_map);
}
}
fn get_sub_tables<F>(&mut self, mut callback: F)
where
F: FnMut(&mut BTreeMap<String, InternalTable>),
{
if let None = self.sub_tables {
self.sub_tables = Some(BTreeMap::new());
}
if let Some(sub_tables) = &mut self.sub_tables {
callback(sub_tables);
}
}
}
pub struct Log {}
impl Log {
pub fn set_file_name(prefix: &str) {
if let Ok(mut logger) = LOGGER.lock() {
if !logger.file_name_important {
logger.file_prefix = prefix.to_string();
}
}
}
pub fn get_file_name() -> Result<String, String> {
match LOGGER.lock() {
Ok(logger) => Ok(logger.file_prefix.to_string()),
Err(e) => Err(e.to_string()),
}
}
pub fn set_file_name_important(prefix: &str) {
Log::set_file_name(prefix);
if let Ok(mut logger) = LOGGER.lock() {
logger.file_name_important = true;
}
}
pub fn set_file_ext(ext: Extension) {
if let Ok(mut logger) = LOGGER.lock() {
if !logger.file_ext_important {
match ext {
Extension::LogToml => {
logger.file_extension = ".log.toml".to_string();
}
Extension::Log => {
logger.file_extension = ".log".to_string();
}
}
}
}
}
pub fn set_file_ext_important(ext: Extension) {
Log::set_file_ext(ext);
if let Ok(mut logger) = LOGGER.lock() {
logger.file_ext_important = true;
}
}
pub fn get_file_ext_str() -> Result<String, String> {
match LOGGER.lock() {
Ok(logger) => Ok(logger.file_extension.to_string()),
Err(e) => Err(e.to_string()),
}
}
pub fn set_level(level: Level) {
if let Ok(mut logger) = LOGGER.lock() {
if !logger.level_important {
logger.level = level;
}
}
}
pub fn set_level_important(level: Level) {
Log::set_level(level);
if let Ok(mut logger) = LOGGER.lock() {
logger.level_important = true;
}
}
pub fn get_level() -> Result<Level, String> {
match LOGGER.lock() {
Ok(logger) => Ok(logger.level),
Err(e) => Err(e.to_string()),
}
}
pub fn set_retention_days(days: i64) {
if let Ok(mut logger) = LOGGER.lock() {
if !logger.retention_days_important {
logger.retention_days = days;
}
}
}
pub fn set_retention_days_important(retention_days: i64) {
Log::set_retention_days(retention_days);
if let Ok(mut logger) = LOGGER.lock() {
logger.retention_days_important = true;
}
}
pub fn get_retention_days() -> Result<i64, String> {
match LOGGER.lock() {
Ok(logger) => Ok(logger.retention_days),
Err(e) => Err(e.to_string()),
}
}
pub fn set_timeout_secs(secs: u64) {
if let Ok(mut logger) = LOGGER.lock() {
if !logger.timeout_secs_important {
logger.timeout_secs = secs;
}
}
}
pub fn set_timeout_secs_important(secs: u64) {
Log::set_timeout_secs(secs);
if let Ok(mut logger) = LOGGER.lock() {
logger.timeout_secs_important = true;
}
}
pub fn get_timeout_secs() -> Result<u64, String> {
match LOGGER.lock() {
Ok(logger) => Ok(logger.timeout_secs),
Err(e) => Err(e.to_string()),
}
}
pub fn set_opt(optimization: Opt) {
if let Ok(mut opt_state) = OPT_STATE.lock() {
if !opt_state.opt_important {
opt_state.set(optimization);
}
}
}
pub fn set_opt_important(optimization: Opt) {
Log::set_opt(optimization);
if let Ok(mut opt_state) = OPT_STATE.lock() {
opt_state.opt_important = true;
}
}
pub fn get_opt() -> Result<Opt, String> {
match OPT_STATE.lock() {
Ok(opt_state) => Ok(opt_state.opt),
Err(e) => Err(e.to_string()),
}
}
pub fn remove_old_logs() -> usize {
let remove_num = if let Ok(logger) = LOGGER.lock() {
let remove_num = logger.remove_old_logs();
match Logger::get_optimization() {
Opt::BeginnersSupport | Opt::Development => {
println!(
"casual_logger | Remove {} log file(s).
| If you don't want this message, set `Log::set_opt(Opt::Release);`.",
remove_num
);
}
Opt::Release => {}
}
remove_num
} else {
0
};
remove_num
}
#[deprecated(
since = "0.5.1",
note = "Please use the casual_logger::Log::flush() method instead"
)]
pub fn wait() {
Log::flush();
}
pub fn flush() {
let (timeout_secs, opt) = if let Ok(logger) = LOGGER.lock() {
(logger.timeout_secs, Logger::get_optimization())
} else {
(0, Opt::BeginnersSupport)
};
Log::wait_for_logging_to_complete(timeout_secs, |secs, message| {
match opt {
Opt::Development => {
println!("casual_logger | {} sec(s). {}", secs, message,);
}
Opt::Release | Opt::BeginnersSupport => {}
}
});
}
fn print_message(queue_len: Option<usize>) -> String {
format!(
"{}",
if let Some(queue_len_val) = queue_len {
if 0 < queue_len_val {
format!("{} table(s) left. ", queue_len_val)
} else {
"".to_string()
}
} else {
"".to_string()
},
)
.trim_end()
.to_string()
}
fn wait_for_logging_to_complete<F>(timeout_secs: u64, count_down: F)
where
F: Fn(u64, String),
{
let mut elapsed_milli_secs = 0;
thread::sleep(std::time::Duration::from_millis(20));
elapsed_milli_secs += 20;
let mut empty_que_count = 0;
while empty_que_count < 2 && elapsed_milli_secs < timeout_secs * 1000 {
let mut queue_len = None;
if let Ok(reserve_target) = RESERVE_TARGET.lock() {
if reserve_target.is_t() {
if let Ok(queue) = QUEUE_T.lock() {
if queue.is_empty() {
break;
}
queue_len = Some(queue.len());
}
} else {
if let Ok(queue) = QUEUE_F.lock() {
if queue.is_empty() {
break;
}
queue_len = Some(queue.len());
}
}
}
if let Some(completed) = Log::flush_target_queue() {
if completed {
empty_que_count = 0;
} else {
empty_que_count += 1;
}
} else {
empty_que_count = 0;
}
if elapsed_milli_secs % 1000 == 0 {
count_down(elapsed_milli_secs / 1000, Log::print_message(queue_len));
}
thread::sleep(std::time::Duration::from_millis(20));
elapsed_milli_secs += 20;
}
}
pub fn enabled(level: Level) -> bool {
if let Ok(logger) = LOGGER.lock() {
if logger.enabled(level) {
return true;
}
}
false
}
pub fn trace(message: &str) {
if Log::enabled(Level::Trace) {
Log::reserve(&InternalTable::from_table(&Table::new(
Level::Trace,
message,
false,
&Stringifier::create_identify_table_name(Logger::create_seq()),
)));
}
}
pub fn traceln(message: &str) {
if Log::enabled(Level::Trace) {
Log::reserve(&InternalTable::from_table(&Table::new(
Level::Trace,
message,
true,
&Stringifier::create_identify_table_name(Logger::create_seq()),
)));
}
}
pub fn trace_t(message: &str, table: &mut Table) {
if Log::enabled(Level::Trace) {
table.base_name = Stringifier::create_identify_table_name(Logger::create_seq());
table.level = Level::Trace;
table.message = message.to_string();
table.message_trailing_newline = false;
Log::reserve(&InternalTable::from_table(table));
}
}
pub fn traceln_t(message: &str, table: &mut Table) {
if Log::enabled(Level::Trace) {
table.base_name = Stringifier::create_identify_table_name(Logger::create_seq());
table.level = Level::Trace;
table.message = message.to_string();
table.message_trailing_newline = true;
Log::reserve(&InternalTable::from_table(table));
}
}
pub fn debug(message: &str) {
if Log::enabled(Level::Debug) {
Log::reserve(&InternalTable::from_table(&Table::new(
Level::Debug,
message,
false,
&Stringifier::create_identify_table_name(Logger::create_seq()),
)));
}
}
pub fn debugln(message: &str) {
if Log::enabled(Level::Debug) {
Log::reserve(&InternalTable::from_table(&Table::new(
Level::Debug,
message,
true,
&Stringifier::create_identify_table_name(Logger::create_seq()),
)));
}
}
pub fn debug_t(message: &str, table: &mut Table) {
if Log::enabled(Level::Debug) {
table.base_name = Stringifier::create_identify_table_name(Logger::create_seq());
table.level = Level::Debug;
table.message = message.to_string();
table.message_trailing_newline = false;
Log::reserve(&InternalTable::from_table(table));
}
}
pub fn debugln_t(message: &str, table: &mut Table) {
if Log::enabled(Level::Debug) {
table.base_name = Stringifier::create_identify_table_name(Logger::create_seq());
table.level = Level::Debug;
table.message = message.to_string();
table.message_trailing_newline = true;
Log::reserve(&InternalTable::from_table(table));
}
}
pub fn info(message: &str) {
if Log::enabled(Level::Info) {
Log::reserve(&InternalTable::from_table(&Table::new(
Level::Info,
message,
false,
&Stringifier::create_identify_table_name(Logger::create_seq()),
)));
}
}
pub fn infoln(message: &str) {
if Log::enabled(Level::Info) {
Log::reserve(&InternalTable::from_table(&Table::new(
Level::Info,
message,
true,
&Stringifier::create_identify_table_name(Logger::create_seq()),
)));
}
}
pub fn info_t(message: &str, table: &mut Table) {
if Log::enabled(Level::Info) {
table.base_name = Stringifier::create_identify_table_name(Logger::create_seq());
table.level = Level::Info;
table.message = message.to_string();
table.message_trailing_newline = false;
Log::reserve(&InternalTable::from_table(table));
}
}
pub fn infoln_t(message: &str, table: &mut Table) {
if Log::enabled(Level::Info) {
table.base_name = Stringifier::create_identify_table_name(Logger::create_seq());
table.level = Level::Info;
table.message = message.to_string();
table.message_trailing_newline = true;
Log::reserve(&InternalTable::from_table(table));
}
}
pub fn notice(message: &str) {
if Log::enabled(Level::Notice) {
Log::reserve(&InternalTable::from_table(&Table::new(
Level::Notice,
message,
false,
&Stringifier::create_identify_table_name(Logger::create_seq()),
)));
}
}
pub fn noticeln(message: &str) {
if Log::enabled(Level::Notice) {
Log::reserve(&InternalTable::from_table(&Table::new(
Level::Notice,
message,
true,
&Stringifier::create_identify_table_name(Logger::create_seq()),
)));
}
}
pub fn notice_t(message: &str, table: &mut Table) {
if Log::enabled(Level::Notice) {
table.base_name = Stringifier::create_identify_table_name(Logger::create_seq());
table.level = Level::Notice;
table.message = message.to_string();
table.message_trailing_newline = false;
Log::reserve(&InternalTable::from_table(table));
}
}
pub fn noticeln_t(message: &str, table: &mut Table) {
if Log::enabled(Level::Notice) {
table.base_name = Stringifier::create_identify_table_name(Logger::create_seq());
table.level = Level::Notice;
table.message = message.to_string();
table.message_trailing_newline = true;
Log::reserve(&InternalTable::from_table(table));
}
}
pub fn warn(message: &str) {
if Log::enabled(Level::Warn) {
Log::reserve(&InternalTable::from_table(&Table::new(
Level::Warn,
message,
false,
&Stringifier::create_identify_table_name(Logger::create_seq()),
)));
}
}
pub fn warnln(message: &str) {
if Log::enabled(Level::Warn) {
Log::reserve(&InternalTable::from_table(&Table::new(
Level::Warn,
message,
true,
&Stringifier::create_identify_table_name(Logger::create_seq()),
)));
}
}
pub fn warn_t(message: &str, table: &mut Table) {
if Log::enabled(Level::Warn) {
table.base_name = Stringifier::create_identify_table_name(Logger::create_seq());
table.level = Level::Warn;
table.message = message.to_string();
table.message_trailing_newline = false;
Log::reserve(&InternalTable::from_table(table));
}
}
pub fn warnln_t(message: &str, table: &mut Table) {
if Log::enabled(Level::Warn) {
table.base_name = Stringifier::create_identify_table_name(Logger::create_seq());
table.level = Level::Warn;
table.message = message.to_string();
table.message_trailing_newline = true;
Log::reserve(&InternalTable::from_table(table));
}
}
pub fn error(message: &str) {
if Log::enabled(Level::Error) {
Log::reserve(&InternalTable::from_table(&Table::new(
Level::Error,
message,
false,
&Stringifier::create_identify_table_name(Logger::create_seq()),
)));
}
}
pub fn errorln(message: &str) {
if Log::enabled(Level::Error) {
Log::reserve(&InternalTable::from_table(&Table::new(
Level::Error,
message,
true,
&Stringifier::create_identify_table_name(Logger::create_seq()),
)));
}
}
pub fn error_t(message: &str, table: &mut Table) {
if Log::enabled(Level::Error) {
table.base_name = Stringifier::create_identify_table_name(Logger::create_seq());
table.level = Level::Error;
table.message = message.to_string();
table.message_trailing_newline = false;
Log::reserve(&InternalTable::from_table(table));
}
}
pub fn errorln_t(message: &str, table: &mut Table) {
if Log::enabled(Level::Error) {
table.base_name = Stringifier::create_identify_table_name(Logger::create_seq());
table.level = Level::Error;
table.message = message.to_string();
table.message_trailing_newline = true;
Log::reserve(&InternalTable::from_table(table));
}
}
pub fn fatal(message: &str) -> String {
Log::reserve(&InternalTable::from_table(&Table::new(
Level::Fatal,
message,
false,
&Stringifier::create_identify_table_name(Logger::create_seq()),
)));
Log::flush();
message.to_string()
}
pub fn fatalln(message: &str) -> String {
Log::reserve(&InternalTable::from_table(&Table::new(
Level::Fatal,
message,
true,
&Stringifier::create_identify_table_name(Logger::create_seq()),
)));
Log::flush();
format!("{}{}", message, NEW_LINE).to_string()
}
pub fn fatal_t(message: &str, table: &mut Table) -> String {
table.base_name = Stringifier::create_identify_table_name(Logger::create_seq());
table.level = Level::Fatal;
table.message = message.to_string();
table.message_trailing_newline = false;
Log::reserve(&InternalTable::from_table(table));
Log::flush();
message.to_string()
}
pub fn fatalln_t(message: &str, table: &mut Table) -> String {
table.base_name = Stringifier::create_identify_table_name(Logger::create_seq());
table.level = Level::Fatal;
table.message = message.to_string();
table.message_trailing_newline = true;
Log::reserve(&InternalTable::from_table(table));
Log::flush();
format!("{}{}", message, NEW_LINE).to_string()
}
fn reserve(i_table: &InternalTable) {
if let Ok(reseve_target) = RESERVE_TARGET.lock() {
if reseve_target.is_t() {
if let Ok(mut queue) = QUEUE_T.lock() {
queue.push_front(i_table.clone());
}
} else {
if let Ok(mut queue) = QUEUE_F.lock() {
queue.push_front(i_table.clone());
}
}
} else {
return;
}
if let Ok(mut signal) = SIGNAL_CAN_FLUSH.lock() {
if signal.can_flush() {
signal.set_can_flush(false);
thread::spawn(move || {
Log::flush_target_queue();
});
}
}
}
fn flush_target_queue() -> Option<bool> {
let mut str_buf = String::new();
let flush_target = if let Ok(mut reserve_target) = RESERVE_TARGET.lock() {
let old = reserve_target.is_t();
reserve_target.switch();
old
} else {
return None;
};
let mut count = 0;
if flush_target {
if let Ok(mut queue) = QUEUE_T.lock() {
loop {
if let Some(internal_table) = queue.pop_back() {
str_buf.push_str(&internal_table.stringify());
count += 1;
} else {
break;
}
}
} else {
}
} else {
if let Ok(mut queue) = QUEUE_F.lock() {
loop {
if let Some(internal_table) = queue.pop_back() {
str_buf.push_str(&internal_table.stringify());
count += 1;
} else {
break;
}
}
} else {
}
}
if let Ok(mut logger) = LOGGER.lock() {
let mut file_buf = BufWriter::new(logger.current_file());
if let Err(_why) = file_buf.write_all(str_buf.as_bytes()) {
return None;
}
if let Ok(mut signal) = SIGNAL_CAN_FLUSH.lock() {
signal.set_can_flush(true);
}
}
Some(0 < count)
}
}
pub enum Extension {
Log,
LogToml,
}
#[derive(Clone, Copy)]
struct ReserveTarget {
target: bool,
}
impl Default for ReserveTarget {
fn default() -> Self {
ReserveTarget { target: false }
}
}
impl ReserveTarget {
fn is_t(self) -> bool {
self.target
}
fn switch(&mut self) {
self.target = !self.target;
}
}
struct SignalCanFlush {
can_flush: bool,
}
impl Default for SignalCanFlush {
fn default() -> Self {
SignalCanFlush { can_flush: true }
}
}
impl SignalCanFlush {
pub fn can_flush(&self) -> bool {
self.can_flush
}
pub fn set_can_flush(&mut self, val: bool) {
self.can_flush = val;
}
}
#[derive(Clone, Copy, Debug)]
pub enum Opt {
Development,
BeginnersSupport,
Release,
}
struct OptState {
opt_important: bool,
opt: Opt,
}
impl Default for OptState {
fn default() -> Self {
OptState {
opt_important: false,
opt: Opt::BeginnersSupport,
}
}
}
impl OptState {
fn get(&self) -> Opt {
self.opt
}
fn set(&mut self, val: Opt) {
self.opt = val;
}
}