1 // Take a look at the license at the top of the repository in the LICENSE file.
2 
3 use crate::DebugLevel;
4 
5 use libc::c_char;
6 use std::borrow::Cow;
7 use std::ffi::CStr;
8 use std::fmt;
9 use std::ptr;
10 
11 use once_cell::sync::Lazy;
12 
13 use glib::ffi::gpointer;
14 use glib::prelude::*;
15 use glib::translate::*;
16 
17 #[derive(PartialEq, Eq)]
18 #[doc(alias = "GstDebugMessage")]
19 pub struct DebugMessage(ptr::NonNull<ffi::GstDebugMessage>);
20 
21 impl fmt::Debug for DebugMessage {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result22     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
23         f.debug_tuple("DebugMessage").field(&self.get()).finish()
24     }
25 }
26 
27 impl DebugMessage {
28     #[doc(alias = "gst_debug_message_get")]
get(&self) -> Option<Cow<str>>29     pub fn get(&self) -> Option<Cow<str>> {
30         unsafe {
31             let message = ffi::gst_debug_message_get(self.0.as_ptr());
32 
33             if message.is_null() {
34                 None
35             } else {
36                 Some(CStr::from_ptr(message).to_string_lossy())
37             }
38         }
39     }
40 }
41 
42 #[derive(PartialEq, Eq, Clone, Copy)]
43 #[doc(alias = "GstDebugCategory")]
44 pub struct DebugCategory(Option<ptr::NonNull<ffi::GstDebugCategory>>);
45 
46 impl DebugCategory {
new( name: &str, color: crate::DebugColorFlags, description: Option<&str>, ) -> DebugCategory47     pub fn new(
48         name: &str,
49         color: crate::DebugColorFlags,
50         description: Option<&str>,
51     ) -> DebugCategory {
52         skip_assert_initialized!();
53         extern "C" {
54             fn _gst_debug_category_new(
55                 name: *const c_char,
56                 color: ffi::GstDebugColorFlags,
57                 description: *const c_char,
58             ) -> *mut ffi::GstDebugCategory;
59         }
60 
61         // Gets the category if it exists already
62         unsafe {
63             let ptr = _gst_debug_category_new(
64                 name.to_glib_none().0,
65                 color.into_glib(),
66                 description.to_glib_none().0,
67             );
68             // Can be NULL if the debug system is compiled out
69             DebugCategory(ptr::NonNull::new(ptr))
70         }
71     }
72 
get(name: &str) -> Option<DebugCategory>73     pub fn get(name: &str) -> Option<DebugCategory> {
74         skip_assert_initialized!();
75         unsafe {
76             extern "C" {
77                 fn _gst_debug_get_category(name: *const c_char) -> *mut ffi::GstDebugCategory;
78             }
79 
80             let cat = _gst_debug_get_category(name.to_glib_none().0);
81 
82             if cat.is_null() {
83                 None
84             } else {
85                 Some(DebugCategory(Some(ptr::NonNull::new_unchecked(cat))))
86             }
87         }
88     }
89 
90     #[doc(alias = "get_threshold")]
91     #[doc(alias = "gst_debug_category_get_threshold")]
threshold(self) -> crate::DebugLevel92     pub fn threshold(self) -> crate::DebugLevel {
93         match self.0 {
94             Some(cat) => unsafe { from_glib(ffi::gst_debug_category_get_threshold(cat.as_ptr())) },
95             None => crate::DebugLevel::None,
96         }
97     }
98 
99     #[doc(alias = "gst_debug_category_set_threshold")]
set_threshold(self, threshold: crate::DebugLevel)100     pub fn set_threshold(self, threshold: crate::DebugLevel) {
101         if let Some(cat) = self.0 {
102             unsafe { ffi::gst_debug_category_set_threshold(cat.as_ptr(), threshold.into_glib()) }
103         }
104     }
105 
106     #[doc(alias = "gst_debug_category_reset_threshold")]
reset_threshold(self)107     pub fn reset_threshold(self) {
108         if let Some(cat) = self.0 {
109             unsafe { ffi::gst_debug_category_reset_threshold(cat.as_ptr()) }
110         }
111     }
112 
113     #[doc(alias = "get_color")]
114     #[doc(alias = "gst_debug_category_get_color")]
color(self) -> crate::DebugColorFlags115     pub fn color(self) -> crate::DebugColorFlags {
116         match self.0 {
117             Some(cat) => unsafe { from_glib(ffi::gst_debug_category_get_color(cat.as_ptr())) },
118             None => crate::DebugColorFlags::empty(),
119         }
120     }
121 
122     #[doc(alias = "get_name")]
123     #[doc(alias = "gst_debug_category_get_name")]
name<'a>(self) -> &'a str124     pub fn name<'a>(self) -> &'a str {
125         match self.0 {
126             Some(cat) => unsafe {
127                 CStr::from_ptr(ffi::gst_debug_category_get_name(cat.as_ptr()))
128                     .to_str()
129                     .unwrap()
130             },
131             None => "",
132         }
133     }
134 
135     #[doc(alias = "get_description")]
136     #[doc(alias = "gst_debug_category_get_description")]
description<'a>(self) -> Option<&'a str>137     pub fn description<'a>(self) -> Option<&'a str> {
138         match self.0 {
139             Some(cat) => unsafe {
140                 let ptr = ffi::gst_debug_category_get_description(cat.as_ptr());
141 
142                 if ptr.is_null() {
143                     None
144                 } else {
145                     Some(CStr::from_ptr(ptr).to_str().unwrap())
146                 }
147             },
148             None => None,
149         }
150     }
151 
152     #[inline]
153     #[doc(alias = "gst_debug_log")]
log<O: IsA<glib::Object>>( self, obj: Option<&O>, level: crate::DebugLevel, file: &str, module: &str, line: u32, args: fmt::Arguments, )154     pub fn log<O: IsA<glib::Object>>(
155         self,
156         obj: Option<&O>,
157         level: crate::DebugLevel,
158         file: &str,
159         module: &str,
160         line: u32,
161         args: fmt::Arguments,
162     ) {
163         let cat = match self.0 {
164             Some(cat) => cat,
165             None => return,
166         };
167 
168         unsafe {
169             if level.into_glib() as i32 > cat.as_ref().threshold {
170                 return;
171             }
172         }
173 
174         let obj_ptr = match obj {
175             Some(obj) => obj.to_glib_none().0 as *mut glib::gobject_ffi::GObject,
176             None => ptr::null_mut(),
177         };
178 
179         unsafe {
180             ffi::gst_debug_log(
181                 cat.as_ptr(),
182                 level.into_glib(),
183                 file.to_glib_none().0,
184                 module.to_glib_none().0,
185                 line as i32,
186                 obj_ptr,
187                 fmt::format(args).replace("%", "%%").to_glib_none().0,
188             );
189         }
190     }
191 }
192 
193 unsafe impl Sync for DebugCategory {}
194 unsafe impl Send for DebugCategory {}
195 
196 impl fmt::Debug for DebugCategory {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result197     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
198         f.debug_tuple("DebugCategory").field(&self.name()).finish()
199     }
200 }
201 
202 pub static CAT_RUST: Lazy<DebugCategory> = Lazy::new(|| {
203     DebugCategory::new(
204         "GST_RUST",
205         crate::DebugColorFlags::UNDERLINE,
206         Some("GStreamer's Rust binding core"),
207     )
208 });
209 
210 macro_rules! declare_debug_category_from_name(
211     ($cat:ident, $cat_name:expr) => (
212         pub static $cat: Lazy<DebugCategory> = Lazy::new(|| DebugCategory::get($cat_name)
213             .expect(&format!("Unable to find `DebugCategory` with name {}", $cat_name)));
214     );
215 );
216 
217 declare_debug_category_from_name!(CAT_DEFAULT, "default");
218 declare_debug_category_from_name!(CAT_GST_INIT, "GST_INIT");
219 declare_debug_category_from_name!(CAT_MEMORY, "GST_MEMORY");
220 declare_debug_category_from_name!(CAT_PARENTAGE, "GST_PARENTAGE");
221 declare_debug_category_from_name!(CAT_STATES, "GST_STATES");
222 declare_debug_category_from_name!(CAT_SCHEDULING, "GST_SCHEDULING");
223 declare_debug_category_from_name!(CAT_BUFFER, "GST_BUFFER");
224 declare_debug_category_from_name!(CAT_BUFFER_LIST, "GST_BUFFER_LIST");
225 declare_debug_category_from_name!(CAT_BUS, "GST_BUS");
226 declare_debug_category_from_name!(CAT_CAPS, "GST_CAPS");
227 declare_debug_category_from_name!(CAT_CLOCK, "GST_CLOCK");
228 declare_debug_category_from_name!(CAT_ELEMENT_PADS, "GST_ELEMENT_PADS");
229 declare_debug_category_from_name!(CAT_PADS, "GST_PADS");
230 declare_debug_category_from_name!(CAT_PERFORMANCE, "GST_PERFORMANCE");
231 declare_debug_category_from_name!(CAT_PIPELINE, "GST_PIPELINE");
232 declare_debug_category_from_name!(CAT_PLUGIN_LOADING, "GST_PLUGIN_LOADING");
233 declare_debug_category_from_name!(CAT_PLUGIN_INFO, "GST_PLUGIN_INFO");
234 declare_debug_category_from_name!(CAT_PROPERTIES, "GST_PROPERTIES");
235 declare_debug_category_from_name!(CAT_NEGOTIATION, "GST_NEGOTIATION");
236 declare_debug_category_from_name!(CAT_REFCOUNTING, "GST_REFCOUNTING");
237 declare_debug_category_from_name!(CAT_ERROR_SYSTEM, "GST_ERROR_SYSTEM");
238 declare_debug_category_from_name!(CAT_EVENT, "GST_EVENT");
239 declare_debug_category_from_name!(CAT_MESSAGE, "GST_MESSAGE");
240 declare_debug_category_from_name!(CAT_PARAMS, "GST_PARAMS");
241 declare_debug_category_from_name!(CAT_CALL_TRACE, "GST_CALL_TRACE");
242 declare_debug_category_from_name!(CAT_SIGNAL, "GST_SIGNAL");
243 declare_debug_category_from_name!(CAT_PROBE, "GST_PROBE");
244 declare_debug_category_from_name!(CAT_REGISTRY, "GST_REGISTRY");
245 declare_debug_category_from_name!(CAT_QOS, "GST_QOS");
246 declare_debug_category_from_name!(CAT_META, "GST_META");
247 declare_debug_category_from_name!(CAT_LOCKING, "GST_LOCKING");
248 declare_debug_category_from_name!(CAT_CONTEXT, "GST_CONTEXT");
249 
250 #[macro_export]
251 macro_rules! gst_error(
252     ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
253         $crate::gst_log_with_level!($cat.clone(), level: $crate::DebugLevel::Error, obj: $obj, $($args)*)
254     }};
255     ($cat:expr, $($args:tt)*) => { {
256         $crate::gst_log_with_level!($cat.clone(), level: $crate::DebugLevel::Error, $($args)*)
257     }};
258 );
259 
260 #[macro_export]
261 macro_rules! gst_warning(
262     ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
263         $crate::gst_log_with_level!($cat.clone(), level: $crate::DebugLevel::Warning, obj: $obj, $($args)*)
264     }};
265     ($cat:expr, $($args:tt)*) => { {
266         $crate::gst_log_with_level!($cat.clone(), level: $crate::DebugLevel::Warning, $($args)*)
267     }};
268 );
269 
270 #[macro_export]
271 macro_rules! gst_fixme(
272     ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
273         $crate::gst_log_with_level!($cat.clone(), level: $crate::DebugLevel::Fixme, obj: $obj, $($args)*)
274     }};
275     ($cat:expr, $($args:tt)*) => { {
276         $crate::gst_log_with_level!($cat.clone(), level: $crate::DebugLevel::Fixme, $($args)*)
277     }};
278 );
279 
280 #[macro_export]
281 macro_rules! gst_info(
282     ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
283         $crate::gst_log_with_level!($cat.clone(), level: $crate::DebugLevel::Info, obj: $obj, $($args)*)
284     }};
285     ($cat:expr, $($args:tt)*) => { {
286         $crate::gst_log_with_level!($cat.clone(), level: $crate::DebugLevel::Info, $($args)*)
287     }};
288 );
289 
290 #[macro_export]
291 macro_rules! gst_debug(
292     ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
293         $crate::gst_log_with_level!($cat.clone(), level: $crate::DebugLevel::Debug, obj: $obj, $($args)*)
294     }};
295     ($cat:expr, $($args:tt)*) => { {
296         $crate::gst_log_with_level!($cat.clone(), level: $crate::DebugLevel::Debug, $($args)*)
297     }};
298 );
299 
300 #[macro_export]
301 macro_rules! gst_log(
302     ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
303         $crate::gst_log_with_level!($cat.clone(), level: $crate::DebugLevel::Log, obj: $obj, $($args)*)
304     }};
305     ($cat:expr, $($args:tt)*) => { {
306         $crate::gst_log_with_level!($cat.clone(), level: $crate::DebugLevel::Log, $($args)*)
307     }};
308 );
309 
310 #[macro_export]
311 macro_rules! gst_trace(
312     ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
313         $crate::gst_log_with_level!($cat.clone(), level: $crate::DebugLevel::Trace, obj: $obj, $($args)*)
314     }};
315     ($cat:expr, $($args:tt)*) => { {
316         $crate::gst_log_with_level!($cat.clone(), level: $crate::DebugLevel::Trace, $($args)*)
317     }};
318 );
319 
320 #[macro_export]
321 macro_rules! gst_memdump(
322     ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
323         $crate::gst_log_with_level!($cat.clone(), level: $crate::DebugLevel::Memdump, obj: $obj, $($args)*)
324     }};
325     ($cat:expr, $($args:tt)*) => { {
326         $crate::gst_log_with_level!($cat.clone(), level: $crate::DebugLevel::Memdump, $($args)*)
327     }};
328 );
329 
330 #[macro_export]
331 macro_rules! gst_log_with_level(
332     ($cat:expr, level: $level:expr, obj: $obj:expr, $($args:tt)*) => { {
333         $crate::DebugCategory::log($cat.clone(), Some($obj), $level, file!(),
334             module_path!(), line!(), format_args!($($args)*))
335     }};
336     ($cat:expr, level: $level:expr, $($args:tt)*) => { {
337         $crate::DebugCategory::log($cat.clone(), None as Option<&$crate::glib::Object>, $level, file!(),
338             module_path!(), line!(), format_args!($($args)*))
339     }};
340 );
341 
log_handler<T>( category: *mut ffi::GstDebugCategory, level: ffi::GstDebugLevel, file: *const c_char, function: *const c_char, line: i32, object: *mut glib::gobject_ffi::GObject, message: *mut ffi::GstDebugMessage, user_data: gpointer, ) where T: Fn(DebugCategory, DebugLevel, &str, &str, u32, Option<&LoggedObject>, &DebugMessage) + Send + Sync + 'static,342 unsafe extern "C" fn log_handler<T>(
343     category: *mut ffi::GstDebugCategory,
344     level: ffi::GstDebugLevel,
345     file: *const c_char,
346     function: *const c_char,
347     line: i32,
348     object: *mut glib::gobject_ffi::GObject,
349     message: *mut ffi::GstDebugMessage,
350     user_data: gpointer,
351 ) where
352     T: Fn(DebugCategory, DebugLevel, &str, &str, u32, Option<&LoggedObject>, &DebugMessage)
353         + Send
354         + Sync
355         + 'static,
356 {
357     if category.is_null() {
358         return;
359     }
360     let category = DebugCategory(Some(ptr::NonNull::new_unchecked(category)));
361     let level = from_glib(level);
362     let file = CStr::from_ptr(file).to_string_lossy();
363     let function = CStr::from_ptr(function).to_string_lossy();
364     let line = line as u32;
365     let object = ptr::NonNull::new(object).map(LoggedObject);
366     let message = DebugMessage(ptr::NonNull::new_unchecked(message));
367     let handler = &*(user_data as *mut T);
368     (handler)(
369         category,
370         level,
371         &file,
372         &function,
373         line,
374         object.as_ref(),
375         &message,
376     );
377 }
378 
log_handler_data_free<T>(data: gpointer)379 unsafe extern "C" fn log_handler_data_free<T>(data: gpointer) {
380     let data = Box::from_raw(data as *mut T);
381     drop(data);
382 }
383 
384 #[derive(Debug)]
385 pub struct DebugLogFunction(ptr::NonNull<std::os::raw::c_void>);
386 
387 // The contained pointer is never dereferenced and has no thread affinity.
388 // It may be convenient to send it or share it between threads to allow cleaning
389 // up log functions from other threads than the one that created it.
390 unsafe impl Send for DebugLogFunction {}
391 unsafe impl Sync for DebugLogFunction {}
392 
393 #[derive(Debug)]
394 #[doc(alias = "GObject")]
395 pub struct LoggedObject(ptr::NonNull<glib::gobject_ffi::GObject>);
396 
397 impl LoggedObject {
as_ptr(&self) -> *mut glib::gobject_ffi::GObject398     pub fn as_ptr(&self) -> *mut glib::gobject_ffi::GObject {
399         self.0.as_ptr()
400     }
401 }
402 
403 impl fmt::Display for LoggedObject {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result404     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405         unsafe {
406             let ptr = self.0.as_ptr();
407             let g_type_instance = &mut (*ptr).g_type_instance;
408             if glib::gobject_ffi::g_type_check_instance_is_fundamentally_a(
409                 g_type_instance,
410                 glib::gobject_ffi::g_object_get_type(),
411             ) != glib::ffi::GFALSE
412             {
413                 let type_ = (*g_type_instance.g_class).g_type;
414 
415                 if glib::gobject_ffi::g_type_is_a(type_, ffi::gst_pad_get_type())
416                     != glib::ffi::GFALSE
417                 {
418                     let name_ptr = (*(ptr as *mut ffi::GstObject)).name;
419                     let name = if name_ptr.is_null() {
420                         "<null>"
421                     } else {
422                         CStr::from_ptr(name_ptr)
423                             .to_str()
424                             .unwrap_or("<invalid name>")
425                     };
426 
427                     let parent_ptr = (*(ptr as *mut ffi::GstObject)).parent;
428                     let parent_name = if parent_ptr.is_null() {
429                         "<null>"
430                     } else {
431                         let name_ptr = (*(parent_ptr as *mut ffi::GstObject)).name;
432                         if name_ptr.is_null() {
433                             "<null>"
434                         } else {
435                             CStr::from_ptr(name_ptr)
436                                 .to_str()
437                                 .unwrap_or("<invalid name>")
438                         }
439                     };
440 
441                     write!(f, "{}:{}", parent_name, name)
442                 } else if glib::gobject_ffi::g_type_is_a(type_, ffi::gst_object_get_type())
443                     != glib::ffi::GFALSE
444                 {
445                     let name_ptr = (*(ptr as *mut ffi::GstObject)).name;
446                     let name = if name_ptr.is_null() {
447                         "<null>"
448                     } else {
449                         CStr::from_ptr(name_ptr)
450                             .to_str()
451                             .unwrap_or("<invalid name>")
452                     };
453                     write!(f, "{}", name)
454                 } else {
455                     let type_name = CStr::from_ptr(glib::gobject_ffi::g_type_name(type_));
456                     write!(
457                         f,
458                         "{}:{:?}",
459                         type_name.to_str().unwrap_or("<invalid type>"),
460                         ptr
461                     )
462                 }
463             } else {
464                 write!(f, "{:?}", ptr)
465             }
466         }
467     }
468 }
469 
470 #[doc(alias = "gst_debug_add_log_function")]
debug_add_log_function<T>(function: T) -> DebugLogFunction where T: Fn(DebugCategory, DebugLevel, &str, &str, u32, Option<&LoggedObject>, &DebugMessage) + Send + Sync + 'static,471 pub fn debug_add_log_function<T>(function: T) -> DebugLogFunction
472 where
473     T: Fn(DebugCategory, DebugLevel, &str, &str, u32, Option<&LoggedObject>, &DebugMessage)
474         + Send
475         + Sync
476         + 'static,
477 {
478     skip_assert_initialized!();
479     unsafe {
480         let user_data = Box::new(function);
481         let user_data_ptr = Box::into_raw(user_data) as gpointer;
482         ffi::gst_debug_add_log_function(
483             Some(log_handler::<T>),
484             user_data_ptr,
485             Some(log_handler_data_free::<T>),
486         );
487         DebugLogFunction(ptr::NonNull::new_unchecked(user_data_ptr))
488     }
489 }
490 
debug_remove_default_log_function()491 pub fn debug_remove_default_log_function() {
492     skip_assert_initialized!();
493     unsafe {
494         ffi::gst_debug_remove_log_function(None);
495     }
496 }
497 
498 #[doc(alias = "gst_debug_remove_log_function_by_data")]
debug_remove_log_function(log_fn: DebugLogFunction)499 pub fn debug_remove_log_function(log_fn: DebugLogFunction) {
500     skip_assert_initialized!();
501     unsafe {
502         ffi::gst_debug_remove_log_function_by_data(log_fn.0.as_ptr());
503     }
504 }
505 
506 #[cfg(test)]
507 mod tests {
508     use super::*;
509     use std::sync::mpsc;
510     use std::sync::{Arc, Mutex};
511 
512     #[test]
513     #[doc(alias = "get_existing")]
existing()514     fn existing() {
515         crate::init().unwrap();
516 
517         let perf_cat = DebugCategory::get("GST_PERFORMANCE")
518             .expect("Unable to find `DebugCategory` with name \"GST_PERFORMANCE\"");
519         assert_eq!(perf_cat.name(), CAT_PERFORMANCE.name());
520     }
521 
522     #[test]
new_and_log()523     fn new_and_log() {
524         crate::init().unwrap();
525 
526         let cat = DebugCategory::new(
527             "test-cat",
528             crate::DebugColorFlags::empty(),
529             Some("some debug category"),
530         );
531 
532         gst_error!(cat, "meh");
533         gst_warning!(cat, "meh");
534         gst_fixme!(cat, "meh");
535         gst_info!(cat, "meh");
536         gst_debug!(cat, "meh");
537         gst_log!(cat, "meh");
538         gst_trace!(cat, "meh");
539         gst_memdump!(cat, "meh");
540 
541         let obj = crate::Bin::new(Some("meh"));
542         gst_error!(cat, obj: &obj, "meh");
543         gst_warning!(cat, obj: &obj, "meh");
544         gst_fixme!(cat, obj: &obj, "meh");
545         gst_info!(cat, obj: &obj, "meh");
546         gst_debug!(cat, obj: &obj, "meh");
547         gst_log!(cat, obj: &obj, "meh");
548         gst_trace!(cat, obj: &obj, "meh");
549         gst_memdump!(cat, obj: &obj, "meh");
550     }
551 
552     #[test]
log_handler()553     fn log_handler() {
554         crate::init().unwrap();
555 
556         let cat = DebugCategory::new(
557             "test-cat-log",
558             crate::DebugColorFlags::empty(),
559             Some("some debug category"),
560         );
561         cat.set_threshold(DebugLevel::Info);
562         let obj = crate::Bin::new(Some("meh"));
563 
564         let (sender, receiver) = mpsc::channel();
565 
566         let sender = Arc::new(Mutex::new(sender));
567 
568         let handler = move |category: DebugCategory,
569                             level: DebugLevel,
570                             _file: &str,
571                             _function: &str,
572                             _line: u32,
573                             _object: Option<&LoggedObject>,
574                             message: &DebugMessage| {
575             let cat = DebugCategory::get("test-cat-log").unwrap();
576 
577             if category != cat {
578                 // This test can run in parallel with other tests, including new_and_log above.
579                 // We cannot be certain we only see our own messages.
580                 return;
581             }
582 
583             assert_eq!(level, DebugLevel::Info);
584             assert_eq!(&message.get().unwrap(), "meh");
585             let _ = sender.lock().unwrap().send(());
586         };
587 
588         debug_remove_default_log_function();
589         let log_fn = debug_add_log_function(handler);
590         gst_info!(cat, obj: &obj, "meh");
591 
592         receiver.recv().unwrap();
593 
594         debug_remove_log_function(log_fn);
595 
596         gst_info!(cat, obj: &obj, "meh2");
597         assert!(receiver.recv().is_err());
598     }
599 }
600