use crate::backtrace::async_log_capture_caller;
use log::{set_boxed_logger, LevelFilter, Log, Metadata, Record};
use std::thread;
#[derive(Debug)]
pub struct Logger<L: Log + 'static, F>
where
F: Fn() -> u64 + Send + Sync + 'static,
{
backtrace: bool,
logger: L,
with: F,
}
impl<L: Log + 'static, F> Logger<L, F>
where
F: Fn() -> u64 + Send + Sync + 'static,
{
pub fn wrap(logger: L, with: F) -> Self {
let backtrace = std::env::var_os("RUST_BACKTRACE")
.map(|x| &x != "0")
.unwrap_or(false);
Self {
logger,
backtrace,
with,
}
}
pub fn start(self, filter: LevelFilter) -> Result<(), log::SetLoggerError> {
let res = set_boxed_logger(Box::new(self));
if res.is_ok() {
log::set_max_level(filter);
}
res
}
fn with(&self) -> u64 {
(self.with)()
}
fn compute_stack_depth(&self, _record: &Record<'_>) -> u8 {
4
}
}
fn thread_id() -> u64 {
let mut string = format!("{:?}", thread::current().id());
string.replace_range(0..9, "");
string.pop();
string.parse().unwrap()
}
impl<L: Log, F> log::Log for Logger<L, F>
where
F: Fn() -> u64 + Send + Sync + 'static,
{
fn enabled(&self, metadata: &Metadata<'_>) -> bool {
self.logger.enabled(metadata)
}
fn log(&self, record: &Record<'_>) {
if self.enabled(record.metadata()) {
let depth = self.compute_stack_depth(&record);
let symbol = async_log_capture_caller(depth);
let key_values = KeyValues {
thread_id: thread_id(),
task_id: self.with(),
kvs: record.key_values(),
};
let (line, filename, fn_name) = if self.backtrace {
match symbol {
Some(symbol) => {
let line = symbol
.lineno
.map(|l| format!(", line={}", l))
.unwrap_or_else(|| String::from(""));
let filename = symbol
.filename
.map(|f| format!(", filename={}", f.to_string_lossy()))
.unwrap_or_else(|| String::from(""));
let fn_name = symbol
.name
.map(|l| format!(", fn_name={}", l))
.unwrap_or_else(|| String::from(""));
(line, filename, fn_name)
}
None => (String::from(""), String::from(""), String::from("")),
}
} else {
(String::from(""), String::from(""), String::from(""))
};
self.logger.log(
&log::Record::builder()
.args(format_args!(
"{}{}{}{}",
record.args(),
filename,
line,
fn_name,
))
.metadata(record.metadata().clone())
.key_values(&key_values)
.level(record.level())
.target(record.target())
.module_path(record.module_path())
.file(record.file())
.line(record.line())
.build(),
)
}
}
fn flush(&self) {}
}
struct KeyValues<'a> {
thread_id: u64,
task_id: u64,
kvs: &'a dyn log::kv::Source,
}
impl<'a> log::kv::Source for KeyValues<'a> {
fn visit<'kvs>(
&'kvs self,
visitor: &mut dyn log::kv::Visitor<'kvs>,
) -> Result<(), log::kv::Error> {
self.kvs.visit(visitor)?;
visitor.visit_pair("thread_id".into(), self.thread_id.into())?;
visitor.visit_pair("task_id".into(), self.task_id.into())?;
Ok(())
}
}