log: differ formatted args evaluation

When the `format_args!` macro is used, formatted args are evaluated
in order to form the argument collection that ends up being formatted
when requested to do so.

For the log macro, this means that any argument that is an expression
was evaluated even if the log level was below current threshold. This
can be examplified by the following code:

```rust
    gst::debug!(CAT, "{}", {
        println!("fmt arg executed");
        "A debug log"
    });
```

This used to print "fmt arg executed" even when the log threshold for
`CAT` was filtering `debug` out.

Note that this could break existing code that would incorrectly rely
on the side effect.
This commit is contained in:
François Laignel 2022-09-13 19:25:14 +02:00
parent 7fa76eac1b
commit f294339240

View file

@ -214,6 +214,64 @@ impl DebugCategory {
} }
} }
// rustdoc-stripper-ignore-next
/// Logs without checking the log level.
#[inline]
#[doc(alias = "gst_debug_log")]
#[doc(alias = "gst_debug_log_literal")]
pub fn log_unfiltered<O: IsA<glib::Object>>(
self,
obj: Option<&O>,
level: crate::DebugLevel,
file: &str,
module: &str,
line: u32,
args: fmt::Arguments,
) {
let cat = match self.0 {
Some(cat) => cat,
None => return,
};
let obj_ptr = match obj {
Some(obj) => obj.to_glib_none().0 as *mut glib::gobject_ffi::GObject,
None => ptr::null_mut(),
};
let mut w = glib::GStringBuilder::default();
// Can't really happen but better safe than sorry
if fmt::write(&mut w, args).is_err() {
return;
}
#[cfg(feature = "v1_20")]
unsafe {
ffi::gst_debug_log_literal(
cat.as_ptr(),
level.into_glib(),
file.to_glib_none().0,
module.to_glib_none().0,
line as i32,
obj_ptr,
w.into_string().to_glib_none().0,
);
}
#[cfg(not(feature = "v1_20"))]
unsafe {
ffi::gst_debug_log(
cat.as_ptr(),
level.into_glib(),
file.to_glib_none().0,
module.to_glib_none().0,
line as i32,
obj_ptr,
b"%s\0".as_ptr() as *const _,
ToGlibPtr::<*const i8>::to_glib_none(&w.into_string()).0,
);
}
}
#[doc(alias = "get_all_categories")] #[doc(alias = "get_all_categories")]
#[doc(alias = "gst_debug_get_all_categories")] #[doc(alias = "gst_debug_get_all_categories")]
pub fn all_categories() -> glib::SList<DebugCategory> { pub fn all_categories() -> glib::SList<DebugCategory> {
@ -406,12 +464,20 @@ macro_rules! memdump(
#[macro_export] #[macro_export]
macro_rules! log_with_level( macro_rules! log_with_level(
($cat:expr, level: $level:expr, obj: $obj:expr, $($args:tt)*) => { { ($cat:expr, level: $level:expr, obj: $obj:expr, $($args:tt)*) => { {
$crate::DebugCategory::log($cat.clone(), Some($obj), $level, file!(), // Check the log level before using `format_args!` otherwise
module_path!(), line!(), format_args!($($args)*)) // formatted arguments are evaluated even if we end up not logging.
if $level <= $cat.threshold() {
$crate::DebugCategory::log_unfiltered($cat.clone(), Some($obj),
$level, file!(), module_path!(), line!(), format_args!($($args)*))
}
}}; }};
($cat:expr, level: $level:expr, $($args:tt)*) => { { ($cat:expr, level: $level:expr, $($args:tt)*) => { {
$crate::DebugCategory::log($cat.clone(), None as Option<&$crate::glib::Object>, $level, file!(), // Check the log level before using `format_args!` otherwise
module_path!(), line!(), format_args!($($args)*)) // formatted arguments are evaluated even if we end up not logging.
if $level <= $cat.threshold() {
$crate::DebugCategory::log_unfiltered($cat.clone(), None as Option<&$crate::glib::Object>,
$level, file!(), module_path!(), line!(), format_args!($($args)*))
}
}}; }};
); );