use log::{LevelFilter, SetLoggerError};
use log4rs::append::console::ConsoleAppender;
use log4rs::append::rolling_file::policy::compound::CompoundPolicy;
use log4rs::append::rolling_file::policy::compound::roll::fixed_window::FixedWindowRoller;
use log4rs::append::rolling_file::policy::compound::trigger::size::SizeTrigger;
use log4rs::config::{Appender, Config, Root};
use log4rs::encode::pattern::PatternEncoder;
use log4rs::filter::threshold::ThresholdFilter;
use log4rs::{self, Handle};
use std::fs;
use std::path::{Path, PathBuf};
use std::process;
const TRIGGER_FILE_SIZE: u64 = 20 * 1024 * 1024;
const LOG_FILE_COUNT: u32 = 1;
const LOG_ENTRY_PATTERN: &str = "{d} [{l}] {f}:{L} {m}\n";
const LOG_LEVEL: log::LevelFilter = log::LevelFilter::Info;
pub struct Logger {
pub log_directory: PathBuf,
pub log_path: PathBuf,
pub archive_prefix: String,
pub handle: Handle,
pub keep_logs: bool,
}
impl Drop for Logger {
fn drop(&mut self) {
if !self.keep_logs {
silence(&mut self.handle);
for maybe_entry in fs::read_dir(&self.log_directory).unwrap() {
let entry = maybe_entry.unwrap();
let entry_path = entry.path();
if entry_path.is_file()
&& (entry_path == self.log_path
|| entry_path
.to_str()
.unwrap()
.starts_with(&self.archive_prefix))
{
fs::remove_file(entry_path).unwrap();
}
}
}
}
}
pub fn silence(handle: &mut Handle) {
let silent_config = Config::builder()
.appender(
Appender::builder()
.filter(Box::new(ThresholdFilter::new(log::LevelFilter::Off)))
.build("off", Box::new(ConsoleAppender::builder().build())),
)
.build(Root::builder().appender("off").build(LevelFilter::Off))
.unwrap();
handle.set_config(silent_config);
}
pub fn init_logger(
log_directory: &str,
log_basename: &str,
keep_logs: bool,
) -> Result<Logger, SetLoggerError> {
let pid = process::id();
let log_filename = format!("{}_PID{}.log", log_basename, pid);
let archive_filename = format!("{}_PID{}_prev{{}}.log", log_basename, pid);
let log_path = Path::new(log_directory).join(log_filename);
let archive_path = Path::new(log_directory).join(archive_filename);
let archive_prefix = archive_path
.to_str()
.unwrap()
.strip_suffix("{}.log")
.unwrap()
.to_owned();
let trigger = SizeTrigger::new(TRIGGER_FILE_SIZE);
let roller = FixedWindowRoller::builder()
.build(archive_path.to_str().unwrap(), LOG_FILE_COUNT)
.unwrap();
let policy = CompoundPolicy::new(Box::new(trigger), Box::new(roller));
let logfile = log4rs::append::rolling_file::RollingFileAppender::builder()
.encoder(Box::new(PatternEncoder::new(LOG_ENTRY_PATTERN)))
.build(&log_path, Box::new(policy))
.unwrap();
let config = Config::builder()
.appender(
Appender::builder()
.filter(Box::new(ThresholdFilter::new(LOG_LEVEL)))
.build("logfile", Box::new(logfile)),
)
.build(
Root::builder()
.appender("logfile")
.build(LevelFilter::Trace),
)
.unwrap();
let handle = log4rs::init_config(config)?;
Ok(Logger {
log_directory: Path::new(log_directory).to_path_buf(),
log_path,
archive_prefix,
handle,
keep_logs,
})
}
pub fn init_console_logger() -> Result<Handle, SetLoggerError> {
let console_config = Config::builder()
.appender(
Appender::builder()
.filter(Box::new(ThresholdFilter::new(log::LevelFilter::Info)))
.build(
"console",
Box::new(
ConsoleAppender::builder()
.encoder(Box::new(PatternEncoder::new(LOG_ENTRY_PATTERN)))
.build(),
),
),
)
.build(
Root::builder()
.appender("console")
.build(LevelFilter::Trace),
)
.unwrap();
let handle = log4rs::init_config(console_config)?;
Ok(handle)
}