1#[cfg(feature = "rerun")]
6use crate::Rerun;
7
8use env_logger::Builder;
9use log::Log;
10#[cfg(feature = "rerun")]
11use re_sdk::{external::re_log, RecordingStream};
12#[cfg(feature = "rerun")]
13use re_types::{archetypes::TextLog, components::TextLogLevel};
14
15#[derive(Debug)]
19pub struct Logger {
20 logger: Option<env_logger::Logger>,
21 path_prefix: Option<String>,
22}
23impl Logger {
24 pub fn new() -> Self {
26 Self {
27 logger: None,
28 path_prefix: None,
29 }
30 }
31
32 #[inline]
34 pub fn with_path_prefix(mut self, path_prefix: impl Into<String>) -> Self {
35 self.path_prefix = Some(path_prefix.into());
36 self
37 }
38
39 #[inline]
48 pub fn with_filter(mut self, filter: impl AsRef<str>) -> Self {
49 self.logger = Some(Builder::new().parse_filters(filter.as_ref()).build());
50 self
51 }
52
53 pub fn init(mut self) -> Result<(), log::SetLoggerError> {
57 if self.logger.is_none() {
58 #[cfg(feature = "rerun")]
59 {
60 self.logger = Some(
61 Builder::new()
62 .parse_filters(&re_log::default_log_filter())
63 .build(),
64 );
65 }
66 #[cfg(not(feature = "rerun"))]
67 {
68 self.logger = Some(Builder::new().parse_default_env().build());
69 }
70 }
71
72 log::set_max_level(log::LevelFilter::max());
75 log::set_boxed_logger(Box::new(self))
76 }
77}
78impl log::Log for Logger {
79 #[inline]
80 fn enabled(&self, metadata: &log::Metadata<'_>) -> bool {
81 self.logger
82 .as_ref()
83 .map_or(true, |filter| filter.enabled(metadata))
84 }
85
86 #[inline]
87 fn log(&self, record: &log::Record<'_>) {
88 if !self
89 .logger
90 .as_ref()
91 .map_or(true, |filter| filter.matches(record))
92 {
93 return;
94 }
95
96 self.logger.as_ref().map(|logger| logger.log(record));
98
99 #[cfg(feature = "rerun")]
101 {
102 let target = record.metadata().target().replace("::", "/");
103 let ent_path = if let Some(path_prefix) = self.path_prefix.as_ref() {
104 format!("{path_prefix}/{target}")
105 } else {
106 target
107 };
108
109 let level = log_level_to_rerun_level(record.metadata().level());
110
111 let body = format!("{}", record.args());
112
113 Rerun
114 .log(ent_path, &TextLog::new(body).with_level(level))
115 .ok(); }
117 }
118
119 #[inline]
120 fn flush(&self) {
121 #[cfg(feature = "rerun")]
122 Rerun.flush_blocking();
123 }
124}
125impl Drop for Logger {
126 fn drop(&mut self) {
127 self.flush();
128 }
129}
130
131#[cfg(feature = "rerun")]
134fn log_level_to_rerun_level(lvl: log::Level) -> TextLogLevel {
135 match lvl {
136 log::Level::Error => TextLogLevel::ERROR,
137 log::Level::Warn => TextLogLevel::WARN,
138 log::Level::Info => TextLogLevel::INFO,
139 log::Level::Debug => TextLogLevel::DEBUG,
140 log::Level::Trace => TextLogLevel::TRACE,
141 }
142 .into()
143}