1use core::fmt::Write;
3
4use alloc::collections::BTreeMap;
5use alloc::string::String;
6
7use ::log::{Level, LevelFilter, Metadata, Record};
8
9use crate::private::common::*;
10use crate::private::cstr::*;
11use crate::private::mutex::Mutex;
12use crate::sys::*;
13
14extern crate alloc;
15
16const RUST_LOG: Option<&str> = option_env!("RUST_LOG");
17
18struct EspStdout(*mut FILE);
24
25impl EspStdout {
26 fn new() -> Self {
27 #[cfg(not(esp_idf_libc_picolibc))]
28 let stdout_ptr = unsafe { __getreent().as_mut() }.unwrap()._stdout;
29 #[cfg(esp_idf_libc_picolibc)]
30 let stdout_ptr = unsafe { stdout };
31
32 let file = unsafe { stdout_ptr.as_mut() }.unwrap();
33
34 #[cfg(not(esp_idf_libc_picolibc))]
35 if (file._flags2 & __SNLK as i32) == 0 && (file._flags & __SSTR as i16) == 0 {
39 unsafe {
40 _lock_acquire_recursive(&mut file._lock);
41 }
42 }
43
44 #[cfg(esp_idf_libc_picolibc)]
45 unsafe {
46 _lock_acquire_recursive(&mut file.lock);
47 }
48
49 Self(stdout_ptr)
50 }
51}
52
53impl Drop for EspStdout {
54 fn drop(&mut self) {
55 let file = unsafe { self.0.as_mut() }.unwrap();
56
57 #[cfg(not(esp_idf_libc_picolibc))]
58 if (file._flags2 & __SNLK as i32) == 0 && (file._flags & __SSTR as i16) == 0 {
62 unsafe {
63 _lock_release_recursive(&mut file._lock);
64 }
65 }
66
67 #[cfg(esp_idf_libc_picolibc)]
68 unsafe {
69 _lock_release_recursive(&mut file.lock);
70 }
71 }
72}
73
74impl core::fmt::Write for EspStdout {
75 fn write_str(&mut self, s: &str) -> core::fmt::Result {
76 let slice = s.as_bytes();
77 unsafe {
78 fwrite(slice.as_ptr() as *const _, 1, slice.len() as u32, self.0);
79 }
80
81 Ok(())
82 }
83}
84
85#[allow(non_upper_case_globals)]
86#[allow(non_snake_case)]
87impl From<Newtype<esp_log_level_t>> for LevelFilter {
88 fn from(level: Newtype<esp_log_level_t>) -> Self {
89 match level.0 {
90 esp_log_level_t_ESP_LOG_NONE => LevelFilter::Off,
91 esp_log_level_t_ESP_LOG_ERROR => LevelFilter::Error,
92 esp_log_level_t_ESP_LOG_WARN => LevelFilter::Warn,
93 esp_log_level_t_ESP_LOG_INFO => LevelFilter::Info,
94 esp_log_level_t_ESP_LOG_DEBUG => LevelFilter::Debug,
95 esp_log_level_t_ESP_LOG_VERBOSE => LevelFilter::Trace,
96 _ => LevelFilter::Trace,
97 }
98 }
99}
100
101impl From<LevelFilter> for Newtype<esp_log_level_t> {
102 fn from(level: LevelFilter) -> Self {
103 Newtype(match level {
104 LevelFilter::Off => esp_log_level_t_ESP_LOG_NONE,
105 LevelFilter::Error => esp_log_level_t_ESP_LOG_ERROR,
106 LevelFilter::Warn => esp_log_level_t_ESP_LOG_WARN,
107 LevelFilter::Info => esp_log_level_t_ESP_LOG_INFO,
108 LevelFilter::Debug => esp_log_level_t_ESP_LOG_DEBUG,
109 LevelFilter::Trace => esp_log_level_t_ESP_LOG_VERBOSE,
110 })
111 }
112}
113
114#[allow(non_upper_case_globals)]
115#[allow(non_snake_case)]
116impl From<Newtype<esp_log_level_t>> for Level {
117 fn from(level: Newtype<esp_log_level_t>) -> Self {
118 match level.0 {
119 esp_log_level_t_ESP_LOG_ERROR => Level::Error,
120 esp_log_level_t_ESP_LOG_WARN => Level::Warn,
121 esp_log_level_t_ESP_LOG_INFO => Level::Info,
122 esp_log_level_t_ESP_LOG_DEBUG => Level::Debug,
123 esp_log_level_t_ESP_LOG_VERBOSE => Level::Trace,
124 _ => Level::Trace,
125 }
126 }
127}
128
129impl From<Level> for Newtype<esp_log_level_t> {
130 fn from(level: Level) -> Self {
131 Newtype(match level {
132 Level::Error => esp_log_level_t_ESP_LOG_ERROR,
133 Level::Warn => esp_log_level_t_ESP_LOG_WARN,
134 Level::Info => esp_log_level_t_ESP_LOG_INFO,
135 Level::Debug => esp_log_level_t_ESP_LOG_DEBUG,
136 Level::Trace => esp_log_level_t_ESP_LOG_VERBOSE,
137 })
138 }
139}
140
141pub trait LogFilterBackend {
143 fn initialize(&self) {}
145
146 fn enabled(&self, metadata: &Metadata) -> bool;
148}
149
150impl<T> LogFilterBackend for &T
151where
152 T: LogFilterBackend,
153{
154 fn initialize(&self) {
155 (**self).initialize()
156 }
157
158 fn enabled(&self, metadata: &Metadata) -> bool {
159 (**self).enabled(metadata)
160 }
161}
162
163pub struct EspIdfLogFilter {
175 cache: Mutex<BTreeMap<String, CString>>,
176}
177
178impl EspIdfLogFilter {
179 pub const fn new() -> Self {
181 Self {
182 cache: Mutex::new(BTreeMap::new()),
183 }
184 }
185
186 pub fn initialize(&self) {
188 ::log::set_max_level(self.get_max_level());
189 }
190
191 pub fn get_max_level(&self) -> LevelFilter {
193 LevelFilter::from(Newtype(CONFIG_LOG_MAXIMUM_LEVEL))
194 }
195
196 pub fn set_target_level(
202 &self,
203 target: impl AsRef<str>,
204 level_filter: LevelFilter,
205 ) -> Result<(), EspError> {
206 let target = target.as_ref();
207
208 let mut cache = self.cache.lock();
209
210 let ctarget = loop {
211 if let Some(ctarget) = cache.get(target) {
212 break ctarget;
213 }
214
215 let ctarget = to_cstring_arg(target)?;
216
217 cache.insert(target.into(), ctarget);
218 };
219
220 unsafe {
221 esp_log_level_set(
222 ctarget.as_c_str().as_ptr(),
223 Newtype::<esp_log_level_t>::from(level_filter).0,
224 );
225 }
226
227 Ok(())
228 }
229
230 pub fn enabled(&self, metadata: &Metadata) -> bool {
233 let level = Newtype::<esp_log_level_t>::from(metadata.level()).0;
234
235 let mut cache = self.cache.lock();
236
237 let ctarget = loop {
238 if let Some(ctarget) = cache.get(metadata.target()) {
239 break ctarget;
240 }
241
242 if let Ok(ctarget) = to_cstring_arg(metadata.target()) {
243 cache.insert(metadata.target().into(), ctarget);
244 } else {
245 return true;
246 }
247 };
248
249 let max_level = unsafe { esp_log_level_get(ctarget.as_c_str().as_ptr()) };
250 level <= max_level
251 }
252}
253
254impl Default for EspIdfLogFilter {
255 fn default() -> Self {
256 Self::new()
257 }
258}
259
260impl LogFilterBackend for EspIdfLogFilter {
261 fn initialize(&self) {
262 self.initialize();
263 }
264
265 fn enabled(&self, metadata: &Metadata) -> bool {
266 self.enabled(metadata)
267 }
268}
269
270impl LogFilterBackend for () {
276 fn enabled(&self, _metadata: &Metadata) -> bool {
277 true
279 }
280}
281
282static INTEGRATED_LOGGER: EspIdfLogger<EspIdfLogFilter> = EspIdfLogger::new(EspIdfLogFilter::new());
283static LOGGER: EspIdfLogger = EspIdfLogger::new(());
284
285pub type EspLogger = EspIdfLogger<EspIdfLogFilter>;
289
290impl EspIdfLogger<EspIdfLogFilter> {
291 pub fn initialize_default() {
295 init_from_esp_idf();
296 }
297}
298
299#[derive(Debug)]
306pub struct EspIdfLogger<T = ()> {
307 filter: T,
308}
309
310impl<T> EspIdfLogger<T> {
311 pub const fn new(filter: T) -> Self {
316 Self { filter }
317 }
318
319 pub fn filter(&self) -> &T {
321 &self.filter
322 }
323
324 fn get_marker(level: Level) -> &'static str {
325 match level {
326 Level::Error => "E",
327 Level::Warn => "W",
328 Level::Info => "I",
329 Level::Debug => "D",
330 Level::Trace => "V",
331 }
332 }
333
334 fn get_color(_level: Level) -> Option<u8> {
335 #[cfg(esp_idf_log_colors)]
336 {
337 match _level {
338 Level::Error => Some(31), Level::Warn => Some(33), Level::Info => Some(32), _ => None,
342 }
343 }
344
345 #[cfg(not(esp_idf_log_colors))]
346 {
347 None
348 }
349 }
350}
351
352impl<T> ::log::Log for EspIdfLogger<T>
353where
354 T: LogFilterBackend + Send + Sync,
355{
356 fn enabled(&self, metadata: &Metadata) -> bool {
357 self.filter.enabled(metadata)
358 }
359
360 fn log(&self, record: &Record) {
361 let metadata = record.metadata();
362
363 if self.enabled(metadata) {
364 let marker = Self::get_marker(metadata.level());
365 let target = record.metadata().target();
366 let args = record.args();
367 let color = Self::get_color(record.level());
368
369 let mut stdout_writer = EspStdout::new();
370
371 if let Some(color) = color {
372 write!(stdout_writer, "\x1b[0;{color}m").unwrap();
373 }
374 write!(stdout_writer, "{marker} (").unwrap();
375 if cfg!(esp_idf_log_timestamp_source_rtos) {
376 let timestamp = unsafe { esp_log_timestamp() };
377 write!(stdout_writer, "{timestamp}").unwrap();
378 } else if cfg!(esp_idf_log_timestamp_source_system) {
379 let timestamp =
384 unsafe { CStr::from_ptr(esp_log_system_timestamp()).to_str().unwrap() };
385 write!(stdout_writer, "{timestamp}").unwrap();
386 }
387 write!(stdout_writer, ") {target}: {args}").unwrap();
388 if color.is_some() {
389 write!(stdout_writer, "\x1b[0m").unwrap();
390 }
391 writeln!(stdout_writer).unwrap();
392 }
393 }
394
395 fn flush(&self) {}
396}
397
398pub fn init(filter: LevelFilter) -> &'static EspIdfLogger<()> {
404 init_with_logger(&LOGGER);
405
406 ::log::set_max_level(filter);
407
408 &LOGGER
409}
410
411pub fn init_from_env() -> &'static EspIdfLogger<()> {
416 let level = match RUST_LOG.unwrap_or("info").to_ascii_lowercase().as_str() {
417 "off" | "none" => LevelFilter::Off,
418 "error" => LevelFilter::Error,
419 "warn" | "warning" => LevelFilter::Warn,
420 "info" => LevelFilter::Info,
421 "debug" => LevelFilter::Debug,
422 "trace" => LevelFilter::Trace,
423 _ => LevelFilter::Info, };
425
426 init(level)
427}
428
429pub fn init_from_esp_idf() -> &'static EspIdfLogger<EspIdfLogFilter> {
432 init_with_logger(&INTEGRATED_LOGGER);
433
434 &INTEGRATED_LOGGER
435}
436
437fn init_with_logger<T>(logger: &'static EspIdfLogger<T>)
439where
440 T: LogFilterBackend + Send + Sync,
441{
442 ::log::set_logger(logger)
443 .map(|()| logger.filter().initialize())
444 .unwrap();
445}