diff --git a/defmt/src/export/mod.rs b/defmt/src/export/mod.rs index c0cd7be3..88e4ac2c 100644 --- a/defmt/src/export/mod.rs +++ b/defmt/src/export/mod.rs @@ -209,20 +209,28 @@ pub fn display(val: &dyn core::fmt::Display) { } #[inline(never)] -pub unsafe fn acquire_and_header(s: &Str) { - acquire(); - istr(s); - timestamp(make_formatter()); +#[must_use] +pub unsafe fn check_acquire_and_header(s: &Str) -> bool { + if s.level_above_global_log_level() { + acquire(); + istr(s); + timestamp(make_formatter()); + true + } else { + false + } } #[inline(never)] pub fn acquire_header_and_release(s: &Str) { - // safety: will be released a few lines further down - unsafe { acquire() }; - istr(s); - timestamp(make_formatter()); - // safety: acquire() was called a few lines above - unsafe { release() }; + if s.level_above_global_log_level() { + // safety: will be released a few lines further down + unsafe { acquire() }; + istr(s); + timestamp(make_formatter()); + // safety: acquire() was called a few lines above + unsafe { release() }; + } } struct FmtWrite; diff --git a/defmt/src/formatter.rs b/defmt/src/formatter.rs index d153a7bc..60e14c8a 100644 --- a/defmt/src/formatter.rs +++ b/defmt/src/formatter.rs @@ -1,3 +1,4 @@ +use crate::{get_global_log_level, IdRanges, LogLevel}; use core::marker::PhantomData; /// Handle to a defmt logger. @@ -14,3 +15,24 @@ pub struct Str { /// 16-bit address pub(crate) address: u16, } + +impl Str { + /// If the interned string is a log message, returns whether its level is above the global log level. + /// If the interned string is not a log message, returns `false`. + /// See [`set_global_log_level`] to change the log level + pub(crate) fn level_above_global_log_level(&self) -> bool { + let ranges = IdRanges::get(); + if self.address >= ranges.trace.start && self.address < ranges.error.end { + let min_id = match get_global_log_level() { + LogLevel::Trace => ranges.trace.start, + LogLevel::Debug => ranges.debug.start, + LogLevel::Info => ranges.info.start, + LogLevel::Warn => ranges.warn.start, + LogLevel::Error => ranges.error.start, + }; + self.address >= min_id + } else { + false + } + } +} diff --git a/defmt/src/lib.rs b/defmt/src/lib.rs index abffdf8f..7d36eb15 100644 --- a/defmt/src/lib.rs +++ b/defmt/src/lib.rs @@ -415,6 +415,40 @@ pub fn flush() { } } +/// Log Level of a defmt message +/// Use `set_global_log_level` to set the global log level used to filter messages at runtime. +/// Note: Use the environment variable `DEFMT_LOG` to disable certain log levels already at compile time +#[repr(u8)] +pub enum LogLevel { + /// Trace level + Trace = 0, + /// Debug level + Debug = 1, + /// Info level + Info = 2, + /// Warn level + Warn = 3, + /// Error level + Error = 4, +} + +static LOG_LEVEL: core::sync::atomic::AtomicU8 = + core::sync::atomic::AtomicU8::new(LogLevel::Trace as u8); + +/// Sets the global log level used to filter messages at runtime. +/// Messages below the passed level will never reach the defmt `global_logger` +/// Note: Use the environment variable `DEFMT_LOG` to disable certain log levels already at compile time +pub fn set_global_log_level(min_level: LogLevel) { + LOG_LEVEL.store(min_level as u8, core::sync::atomic::Ordering::Relaxed); +} + +/// Gets the global log level used to filter messages at runtime. +/// Messages below the returned level will never reach the defmt `global_logger` +pub fn get_global_log_level() -> LogLevel { + let level = LOG_LEVEL.load(core::sync::atomic::Ordering::Relaxed); + unsafe { core::mem::transmute(level) } +} + #[cfg(not(feature = "unstable-test"))] #[doc(hidden)] pub struct IdRanges { diff --git a/macros/src/function_like/log.rs b/macros/src/function_like/log.rs index 7507177b..f69ca002 100644 --- a/macros/src/function_like/log.rs +++ b/macros/src/function_like/log.rs @@ -48,10 +48,11 @@ pub(crate) fn expand_parsed(level: Level, args: Args) -> TokenStream2 { } else { quote!( // safety: will be released a few lines further down - unsafe { defmt::export::acquire_and_header(&#header); }; - #(#exprs;)* - // safety: acquire() was called a few lines above - unsafe { defmt::export::release() } + if( unsafe { defmt::export::check_acquire_and_header(&#header) }) { + #(#exprs;)* + // safety: acquire() was called a few lines above + unsafe { defmt::export::release() } + } ) }; diff --git a/macros/src/function_like/println.rs b/macros/src/function_like/println.rs index b6385831..9c1f8305 100644 --- a/macros/src/function_like/println.rs +++ b/macros/src/function_like/println.rs @@ -42,10 +42,11 @@ pub(crate) fn expand_parsed(args: Args) -> TokenStream2 { } else { quote!( // safety: will be released a few lines further down - unsafe { defmt::export::acquire_and_header(&#header); }; - #(#exprs;)* - // safety: acquire() was called a few lines above - unsafe { defmt::export::release() } + if (unsafe { defmt::export::check_acquire_and_header(&#header) }){ + #(#exprs;)* + // safety: acquire() was called a few lines above + unsafe { defmt::export::release() } + } ) }; quote!({