extern crate async_logger;
extern crate log;
#[cfg(feature="time")]
extern crate time;
use log::{Log, Metadata, Record};
use async_logger::{AsyncLoggerNB, FileWriter, Error};
use std::sync::Arc;
#[cfg(feature="time")]
use time::OffsetDateTime;
const DEFAULT_BUF_SIZE: usize = 256;
const DEFAULT_LOG_FILE_SIZE: usize = 10*1024*1024;
pub struct Logger {
async_logger: Arc<AsyncLoggerNB<Box<String>>>,
formatter: fn(&Record) -> String,
}
impl Logger {
pub fn new(log_dir: &str, buf_sz: usize, file_size: usize) -> Result<Logger, Error> {
let writer = FileWriter::new(log_dir, file_size)?;
let async_logger = Arc::new(AsyncLoggerNB::new(Box::new(writer), buf_sz)?);
let formatter = Logger::format_msg;
Ok(Logger {
async_logger,
formatter,
})
}
pub fn builder() -> LoggerBuilder {
LoggerBuilder {
buf_sz: None,
writer: None,
formatter: None,
}
}
fn format_msg(record: &Record) -> String {
let time;
#[cfg(feature="time")]
{
time = OffsetDateTime::now_local().format("%Y-%m-%d %H:%M:%S.%N%z");
}
#[cfg(not(feature="time"))]
{
time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or(std::time::Duration::new(0,0))
.as_secs();
}
format!("[{} {} {}]: {}\n", time, record.level(), record.target(), record.args())
}
}
impl Log for Logger {
fn enabled(&self, metadata: &Metadata) -> bool {
return metadata.level() <= log::max_level();
}
fn log(&self, record: &Record) {
let msg = (self.formatter)(record);
let _ = self.async_logger.write_value(Box::new(msg));
}
fn flush(&self) {
AsyncLoggerNB::flush(&self.async_logger);
}
}
pub struct LoggerBuilder {
buf_sz: Option<usize>,
writer: Option<Box<dyn async_logger::Writer<Box<String>>>>,
formatter: Option<fn(&Record) -> String>,
}
impl LoggerBuilder {
pub fn buf_size(mut self, size: usize) -> Self {
self.buf_sz = Some(size);
self
}
pub fn formatter(mut self, formatter: fn(&Record) -> String) -> Self {
self.formatter = Some(formatter);
self
}
pub fn writer(mut self, writer: Box<dyn async_logger::Writer<Box<String>>>) -> Self {
self.writer = Some(writer);
self
}
pub fn build(self) -> Result<Logger,Error> {
let buf_sz = match self.buf_sz {
Some(buf_sz) => buf_sz,
None => DEFAULT_BUF_SIZE,
};
let writer = match self.writer {
Some(writer) => writer,
None => Box::new(FileWriter::new(".", DEFAULT_LOG_FILE_SIZE)?),
};
let formatter = match self.formatter {
Some(formatter) => formatter,
None => Logger::format_msg,
};
let async_logger = Arc::new(AsyncLoggerNB::new(writer, buf_sz)?);
Ok(Logger {
async_logger,
formatter,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use async_logger::ErrorKind;
use std::path::Path;
const LOG_DIR: &str = "/tmp/AsyncLoggerNBTest_000239400377";
const NONEXISTING_LOG_DIR: &str = "/tmp/AsyncLoggerNBTest_85003857407";
const LOG_FILE_SIZE: usize = 4096;
#[test]
fn test_error() {
if Path::new(LOG_DIR).exists() {
std::fs::remove_dir_all(LOG_DIR).expect("Failed to delete test dir on cleanup");
}
std::fs::create_dir(LOG_DIR).expect("Failed to create test dir");
match Logger::new(LOG_DIR, 0, LOG_FILE_SIZE) {
Err(e) if e.kind() == ErrorKind::IncorrectBufferSize => {},
_ => panic!("Expected error, got Ok!"),
}
std::fs::remove_dir_all(LOG_DIR).expect("Failed to delete test dir on cleanup");
std::fs::create_dir(LOG_DIR).expect("Failed to create test dir");
match Logger::new(LOG_DIR, std::usize::MAX, LOG_FILE_SIZE) {
Err(e) if e.kind() == ErrorKind::IncorrectBufferSize => {},
_ => panic!("Expected error, got Ok!"),
}
std::fs::remove_dir_all(LOG_DIR).expect("Failed to delete test dir on cleanup");
match Logger::new(NONEXISTING_LOG_DIR, 100, LOG_FILE_SIZE) {
Err(e) if e.kind() == ErrorKind::IoError => {
},
_ => panic!("Expected error, got Ok!"),
}
std::fs::create_dir(LOG_DIR).expect("Failed to create test dir");
let writer = FileWriter::new(LOG_DIR, LOG_FILE_SIZE).expect("Failed to create file writer");
match Logger::builder()
.buf_size(0)
.writer(Box::new(writer))
.build()
{
Err(e) if e.kind() == ErrorKind::IncorrectBufferSize => {},
_ => panic!("Expected error, got Ok!"),
}
std::fs::remove_dir_all(LOG_DIR).expect("Failed to delete test dir on cleanup");
std::fs::create_dir(LOG_DIR).expect("Failed to create test dir");
let writer = FileWriter::new(LOG_DIR, LOG_FILE_SIZE).expect("Failed to create file writer");
match Logger::builder()
.buf_size(std::usize::MAX)
.writer(Box::new(writer))
.build()
{
Err(e) if e.kind() == ErrorKind::IncorrectBufferSize => {},
_ => panic!("Expected error, got Ok!"),
}
std::fs::remove_dir_all(LOG_DIR).expect("Failed to delete test dir on cleanup");
}
}