use log::Log as _;
use re_sdk_types::archetypes::TextLog;
use re_sdk_types::components::TextLogLevel;
use crate::RecordingStream;
#[derive(Debug)]
pub struct Logger {
rec: RecordingStream,
filter: Option<env_filter::Filter>,
path_prefix: Option<String>,
}
impl Drop for Logger {
fn drop(&mut self) {
self.flush();
}
}
impl Logger {
pub fn new(rec: RecordingStream) -> Self {
Self {
rec,
filter: None,
path_prefix: None,
}
}
#[inline]
pub fn with_path_prefix(mut self, path_prefix: impl Into<String>) -> Self {
self.path_prefix = Some(path_prefix.into());
self
}
#[inline]
pub fn with_filter(mut self, filter: impl AsRef<str>) -> Self {
use env_filter::Builder;
self.filter = Some(Builder::new().parse(filter.as_ref()).build());
self
}
pub fn init(mut self) -> Result<(), log::SetLoggerError> {
if self.filter.is_none() {
use env_filter::Builder;
self.filter = Some(Builder::new().parse(&re_log::default_log_filter()).build());
}
log::set_max_level(log::LevelFilter::max());
log::set_boxed_logger(Box::new(self))
}
}
impl log::Log for Logger {
#[inline]
fn enabled(&self, metadata: &log::Metadata<'_>) -> bool {
self.filter
.as_ref()
.is_none_or(|filter| filter.enabled(metadata))
}
#[inline]
fn log(&self, record: &log::Record<'_>) {
if !self
.filter
.as_ref()
.is_none_or(|filter| filter.matches(record))
{
return;
}
let target = record.metadata().target().replace("::", "/");
let ent_path = if let Some(path_prefix) = self.path_prefix.as_ref() {
format!("{path_prefix}/{target}")
} else {
target
};
let level = log_level_to_rerun_level(record.metadata().level());
let body = format!("{}", record.args());
self.rec
.log(ent_path, &TextLog::new(body).with_level(level))
.ok(); }
#[inline]
fn flush(&self) {
self.rec.flush_blocking().ok();
}
}
fn log_level_to_rerun_level(lvl: log::Level) -> TextLogLevel {
match lvl {
log::Level::Error => TextLogLevel::ERROR,
log::Level::Warn => TextLogLevel::WARN,
log::Level::Info => TextLogLevel::INFO,
log::Level::Debug => TextLogLevel::DEBUG,
log::Level::Trace => TextLogLevel::TRACE,
}
.into()
}