use log::{self, LevelFilter, Log, Metadata, Record};
use std::fs::{self, File};
use std::io::{BufRead, BufReader, Write};
use std::sync::RwLock;
pub enum LogTimeFormat {
TimeStamp,
TimeLocal,
}
pub struct Jlogger {
log_console: bool,
log_file: Option<RwLock<File>>,
log_mark: bool,
mark: String,
log_time: bool,
time_format: LogTimeFormat,
system_start: i64,
}
impl Log for Jlogger {
fn enabled(&self, metadata: &Metadata) -> bool {
if let Ok(level) = std::env::var("JLOGGER_LEVEL") {
let level = match level.as_str() {
"off" => LevelFilter::Off,
"error" => LevelFilter::Error,
"warn" => LevelFilter::Warn,
"info" => LevelFilter::Info,
"debug" => LevelFilter::Debug,
"trace" => LevelFilter::Trace,
_ => LevelFilter::Off,
};
return metadata.level() <= level;
}
true
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
let mark = std::env::var("JLOGGER_MARK").unwrap_or_else(|_| self.mark.clone());
let log_mark = self.log_mark && !mark.trim().is_empty();
let mut log_message = String::new();
if self.log_time {
let now = chrono::Local::now();
match self.time_format {
LogTimeFormat::TimeStamp => log_message.push_str({
format!(
"{}.{:<09} ",
now.timestamp() - self.system_start,
now.timestamp_nanos() % 1000000000
)
.as_str()
}),
LogTimeFormat::TimeLocal => log_message.push_str({
let now = chrono::Local::now();
format!("{} ", now.format("%Y-%m-%d %H:%M:%S")).as_str()
}),
}
}
log_message.push_str(format!("{:5} ", record.level()).as_str());
if log_mark {
log_message.push_str(format!("{} ", mark).as_str());
}
log_message.push_str(format!(": {}", record.args()).as_str());
if self.log_console {
println!("{}", log_message);
}
if let Some(f) = &self.log_file {
let mut fw = f.write().unwrap();
writeln!(fw, "{}", log_message).unwrap();
}
}
}
fn flush(&self) {}
}
pub struct JloggerBuilder {
max_level: LevelFilter,
log_console: bool,
log_file: Option<RwLock<File>>,
log_mark: bool,
mark: String,
log_time: bool,
time_format: LogTimeFormat,
}
impl Default for JloggerBuilder {
fn default() -> Self {
JloggerBuilder::new()
}
}
impl JloggerBuilder {
pub fn new() -> Self {
let exe_cmd = std::env::current_exe().unwrap();
let mark = exe_cmd
.as_path()
.file_name()
.unwrap()
.to_str()
.unwrap()
.to_string();
JloggerBuilder {
max_level: LevelFilter::Info,
log_console: true,
log_file: None,
log_mark: false,
mark,
log_time: true,
time_format: LogTimeFormat::TimeStamp,
}
}
pub fn max_level(mut self, max_level: LevelFilter) -> Self {
self.max_level = max_level;
self
}
pub fn log_console(mut self, log_console: bool) -> Self {
self.log_console = log_console;
self
}
pub fn log_file(mut self, log_file: &str) -> Self {
self.log_file = Some(RwLock::new(
fs::OpenOptions::new()
.create(true)
.append(true)
.write(true)
.open(log_file)
.unwrap(),
));
self
}
pub fn log_mark(mut self, log_mark: bool, mark: Option<&str>) -> Self {
self.log_mark = log_mark;
if let Some(m) = mark {
self.mark = m.to_string();
}
self
}
pub fn log_time(mut self, log_time: bool) -> Self {
self.log_time = log_time;
self
}
pub fn log_time_format(mut self, time_format: LogTimeFormat) -> Self {
self.time_format = time_format;
self
}
pub fn build(mut self) {
let now = chrono::Local::now().timestamp();
let system_start = {
if let Ok(f) = fs::OpenOptions::new()
.create(false)
.write(false)
.read(true)
.open("/proc/stat")
{
let mut br = BufReader::new(f);
loop {
let mut buf = String::new();
if let Ok(n) = br.read_line(&mut buf) {
if n == 0 {
break now;
}
if buf.starts_with("btime") {
let v: Vec<&str> = buf.split_whitespace().into_iter().collect();
break v[1].parse::<i64>().unwrap();
}
}
}
} else {
now
}
};
let logger = Box::new(Jlogger {
log_console: self.log_console,
log_file: self.log_file.take(),
log_mark: self.log_mark,
mark: self.mark,
log_time: self.log_time,
time_format: self.time_format,
system_start,
});
log::set_max_level(self.max_level);
log::set_boxed_logger(logger).unwrap();
}
}
#[macro_export]
macro_rules! jerror{
() => {
log::error!(
"{}-{} : arrived.",
file!(),
line!(),
);
};
($val:tt) => {
log::error!(
"{}-{} : {}",
file!(),
line!(),
$val
);
};
($fmt:expr,$($val:expr),*) => {{
log::error!(
"{}-{} : {}",
file!(),
line!(),
format!($fmt, $($val),*)
);
}};
}
#[macro_export]
macro_rules! jwarn{
() => {
log::warn!(
"{}-{} : arrived.",
file!(),
line!(),
);
};
($val:tt) => {
log::warn!(
"{}-{} : {}",
file!(),
line!(),
$val
);
};
($fmt:expr,$($val:expr),*) => {{
log::warn!(
"{}-{} : {}",
file!(),
line!(),
format!($fmt, $($val),*)
);
}};
}
#[macro_export]
macro_rules! jinfo{
() => {
log::info!(
"{}-{} : arrived.",
file!(),
line!(),
);
};
($val:tt) => {
log::info!(
"{}-{} : {}",
file!(),
line!(),
$val
);
};
($fmt:expr,$($val:expr),*) => {{
log::info!(
"{}-{} : {}",
file!(),
line!(),
format!($fmt, $($val),*)
);
}};
}
#[macro_export]
macro_rules! jdebug {
() => {
log::debug!(
"{}-{} : arrived.",
file!(),
line!(),
);
};
($val:tt) => {
log::debug!(
"{}-{} : {}",
file!(),
line!(),
$val
);
};
($fmt:expr,$($val:expr),*) => {{
log::debug!(
"{}-{} : {}",
file!(),
line!(),
format!($fmt, $($val),*)
);
}};
}
#[test]
fn test_debug_macro() {
use log::{debug, info};
JloggerBuilder::new()
.max_level(LevelFilter::Debug)
.log_console(true)
.log_mark(true, Some("test_debug_macro"))
.log_time(true)
.log_time_format(LogTimeFormat::TimeLocal)
.log_file("/tmp/abc")
.build();
jdebug!("test: {}", String::from("hello"));
jdebug!("this is debug");
jwarn!("this is warn");
jerror!("this is error");
jinfo!("this is info");
info!("this is info");
jdebug!();
debug!("default");
}