use std::{
io::{self, Write},
fmt::Write as _,
ops::Deref,
sync::{Mutex, MutexGuard},
};
use log::{Level, Log, Metadata, Record, SetLoggerError};
#[cfg(feature = "target")]
use regex::Regex;
#[derive(Debug)]
struct Logger {
level: Level,
#[cfg(feature = "target")]
target: Regex,
buffer: Mutex<String>,
}
impl Logger {
fn lock(&self) -> MutexGuard<String> {
self.buffer
.lock()
.expect("inner lock poisoned")
}
}
impl Log for Logger {
fn enabled(&self, metadata: &Metadata) -> bool {
#[cfg(feature = "target")]
{
if !self.target.is_match(metadata.target()) {
return false;
}
}
metadata.level() <= self.level
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
let target =
if record.target().is_empty() {
record
.module_path()
.unwrap_or("?")
} else {
record.target()
};
let mut buffer = self.lock();
writeln!(
buffer,
"[{}] {:<5} | {}",
target,
record.level().to_string(),
record.args()
)
.expect("std::fmt::Write should never fail for String");
}
}
fn flush(&self) { }
}
#[derive(Debug)]
pub struct BufferLockGuard<'a>(MutexGuard<'a, String>);
impl<'a> Deref for BufferLockGuard<'a> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}
#[derive(Debug)]
pub struct MemoryLogger(Logger);
impl MemoryLogger {
pub fn setup(
level: Level,
#[cfg(feature = "target")]
target: Regex,
) -> Result<&'static Self, SetLoggerError> {
let logger = Box::leak(
Box::new(
Self(
Logger {
level,
buffer: Mutex::new(String::new()),
#[cfg(feature = "target")]
target
}
)
)
);
log::set_logger(&logger.0)?;
log::set_max_level(
level.to_level_filter()
);
Ok(logger)
}
pub fn dump<W>(&self, mut writer: W) -> io::Result<()>
where
W: Write
{
let buffer = &mut self.0.lock();
writer.write_all(
buffer.as_bytes()
)?;
buffer.clear();
Ok(())
}
pub fn read(&self) -> BufferLockGuard {
BufferLockGuard(self.0.lock())
}
pub fn clear(&self) {
self.0.lock().clear()
}
}