gstreamer/
log.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{borrow::Cow, ffi::CStr, fmt, ptr};
4
5use glib::{ffi::gpointer, prelude::*, translate::*};
6use libc::c_char;
7#[cfg(feature = "log")]
8use log;
9use once_cell::sync::Lazy;
10
11use crate::{ffi, DebugLevel};
12
13// import and rename those so they are namespaced as log::*
14pub use crate::auto::functions::debug_add_ring_buffer_logger as add_ring_buffer_logger;
15pub use crate::auto::functions::debug_get_default_threshold as get_default_threshold;
16pub use crate::auto::functions::debug_get_stack_trace as get_stack_trace;
17pub use crate::auto::functions::debug_is_active as is_active;
18pub use crate::auto::functions::debug_is_colored as is_colored;
19pub use crate::auto::functions::debug_print_stack_trace as print_stack_trace;
20pub use crate::auto::functions::debug_remove_ring_buffer_logger as remove_ring_buffer_logger;
21pub use crate::auto::functions::debug_ring_buffer_logger_get_logs as ring_buffer_logger_get_logs;
22pub use crate::auto::functions::debug_set_active as set_active;
23pub use crate::auto::functions::debug_set_colored as set_colored;
24pub use crate::auto::functions::debug_set_default_threshold as set_default_threshold;
25pub use crate::auto::functions::debug_set_threshold_for_name as set_threshold_for_name;
26pub use crate::auto::functions::debug_set_threshold_from_string as set_threshold_from_string;
27pub use crate::auto::functions::debug_unset_threshold_for_name as unset_threshold_for_name;
28
29#[derive(PartialEq, Eq)]
30#[doc(alias = "GstDebugMessage")]
31pub struct DebugMessage(ptr::NonNull<ffi::GstDebugMessage>);
32
33impl fmt::Debug for DebugMessage {
34    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
35        f.debug_tuple("DebugMessage").field(&self.get()).finish()
36    }
37}
38
39impl DebugMessage {
40    #[doc(alias = "gst_debug_message_get")]
41    #[inline]
42    pub fn get(&self) -> Option<Cow<glib::GStr>> {
43        unsafe {
44            let message = ffi::gst_debug_message_get(self.0.as_ptr());
45
46            if message.is_null() {
47                None
48            } else {
49                Some(glib::GStr::from_ptr_lossy(message))
50            }
51        }
52    }
53
54    #[cfg(feature = "v1_22")]
55    #[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))]
56    #[doc(alias = "gst_debug_message_get_id")]
57    #[inline]
58    pub fn id(&self) -> Option<&glib::GStr> {
59        unsafe {
60            let id = ffi::gst_debug_message_get_id(self.0.as_ptr());
61
62            if id.is_null() {
63                None
64            } else {
65                Some(glib::GStr::from_ptr(id))
66            }
67        }
68    }
69
70    #[inline]
71    pub fn as_ptr(&self) -> *mut ffi::GstDebugMessage {
72        self.0.as_ptr()
73    }
74}
75
76#[derive(PartialEq, Eq, Clone, Copy, Hash)]
77#[doc(alias = "GstDebugCategory")]
78#[repr(transparent)]
79pub struct DebugCategory(Option<ptr::NonNull<ffi::GstDebugCategory>>);
80
81impl DebugCategory {
82    #[doc(alias = "gst_debug_category_new")]
83    #[doc(alias = "GST_DEBUG_CATEGORY")]
84    #[doc(alias = "GST_DEBUG_CATEGORY_INIT")]
85    pub fn new(
86        name: &str,
87        color: crate::DebugColorFlags,
88        description: Option<&str>,
89    ) -> DebugCategory {
90        skip_assert_initialized!();
91        extern "C" {
92            fn _gst_debug_category_new(
93                name: *const c_char,
94                color: ffi::GstDebugColorFlags,
95                description: *const c_char,
96            ) -> *mut ffi::GstDebugCategory;
97        }
98
99        // Gets the category if it exists already
100        unsafe {
101            let ptr = name.run_with_gstr(|name| {
102                description.run_with_gstr(|description| {
103                    _gst_debug_category_new(
104                        name.to_glib_none().0,
105                        color.into_glib(),
106                        description.to_glib_none().0,
107                    )
108                })
109            });
110
111            // Can be NULL if the debug system is compiled out
112            DebugCategory(ptr::NonNull::new(ptr))
113        }
114    }
115
116    #[doc(alias = "gst_debug_get_category")]
117    #[inline]
118    pub fn get(name: &str) -> Option<DebugCategory> {
119        skip_assert_initialized!();
120        unsafe {
121            extern "C" {
122                fn _gst_debug_get_category(name: *const c_char) -> *mut ffi::GstDebugCategory;
123            }
124
125            let cat = name.run_with_gstr(|name| _gst_debug_get_category(name.to_glib_none().0));
126
127            if cat.is_null() {
128                None
129            } else {
130                Some(DebugCategory(Some(ptr::NonNull::new_unchecked(cat))))
131            }
132        }
133    }
134
135    #[doc(alias = "get_threshold")]
136    #[doc(alias = "gst_debug_category_get_threshold")]
137    #[inline]
138    pub fn threshold(self) -> crate::DebugLevel {
139        match self.0 {
140            Some(cat) => unsafe { from_glib(cat.as_ref().threshold) },
141            None => crate::DebugLevel::None,
142        }
143    }
144
145    #[doc(alias = "gst_debug_category_set_threshold")]
146    #[inline]
147    pub fn set_threshold(self, threshold: crate::DebugLevel) {
148        if let Some(cat) = self.0 {
149            unsafe { ffi::gst_debug_category_set_threshold(cat.as_ptr(), threshold.into_glib()) }
150        }
151    }
152
153    #[doc(alias = "gst_debug_category_reset_threshold")]
154    #[inline]
155    pub fn reset_threshold(self) {
156        if let Some(cat) = self.0 {
157            unsafe { ffi::gst_debug_category_reset_threshold(cat.as_ptr()) }
158        }
159    }
160
161    #[inline]
162    pub fn above_threshold(self, level: crate::DebugLevel) -> bool {
163        match self.0 {
164            Some(cat) => unsafe { cat.as_ref().threshold >= level.into_glib() },
165            None => false,
166        }
167    }
168
169    #[doc(alias = "get_color")]
170    #[doc(alias = "gst_debug_category_get_color")]
171    #[inline]
172    pub fn color(self) -> crate::DebugColorFlags {
173        match self.0 {
174            Some(cat) => unsafe { from_glib(cat.as_ref().color) },
175            None => crate::DebugColorFlags::empty(),
176        }
177    }
178
179    #[doc(alias = "get_name")]
180    #[doc(alias = "gst_debug_category_get_name")]
181    #[inline]
182    pub fn name<'a>(self) -> &'a str {
183        match self.0 {
184            Some(cat) => unsafe { CStr::from_ptr(cat.as_ref().name).to_str().unwrap() },
185            None => "",
186        }
187    }
188
189    #[doc(alias = "get_description")]
190    #[doc(alias = "gst_debug_category_get_description")]
191    #[inline]
192    pub fn description<'a>(self) -> Option<&'a str> {
193        let cat = self.0?;
194
195        unsafe {
196            let ptr = cat.as_ref().description;
197
198            if ptr.is_null() {
199                None
200            } else {
201                Some(CStr::from_ptr(ptr).to_str().unwrap())
202            }
203        }
204    }
205
206    #[inline]
207    #[doc(alias = "gst_debug_log")]
208    #[doc(alias = "gst_debug_log_literal")]
209    pub fn log(
210        self,
211        obj: Option<&impl IsA<glib::Object>>,
212        level: crate::DebugLevel,
213        file: &glib::GStr,
214        function: &str,
215        line: u32,
216        args: fmt::Arguments,
217    ) {
218        if !self.above_threshold(level) {
219            return;
220        }
221
222        self.log_unfiltered_internal(
223            obj.map(|obj| obj.as_ref()),
224            level,
225            file,
226            function,
227            line,
228            args,
229        )
230    }
231
232    #[inline]
233    #[doc(alias = "gst_debug_log_literal")]
234    pub fn log_literal(
235        self,
236        obj: Option<&impl IsA<glib::Object>>,
237        level: crate::DebugLevel,
238        file: &glib::GStr,
239        function: &str,
240        line: u32,
241        msg: &glib::GStr,
242    ) {
243        if !self.above_threshold(level) {
244            return;
245        }
246
247        self.log_literal_unfiltered_internal(
248            obj.map(|obj| obj.as_ref()),
249            level,
250            file,
251            function,
252            line,
253            msg,
254        )
255    }
256
257    // rustdoc-stripper-ignore-next
258    /// Logs without checking the log level.
259    #[inline]
260    #[doc(alias = "gst_debug_log")]
261    pub fn log_unfiltered(
262        self,
263        obj: Option<&impl IsA<glib::Object>>,
264        level: crate::DebugLevel,
265        file: &glib::GStr,
266        function: &str,
267        line: u32,
268        args: fmt::Arguments,
269    ) {
270        self.log_unfiltered_internal(
271            obj.map(|obj| obj.as_ref()),
272            level,
273            file,
274            function,
275            line,
276            args,
277        )
278    }
279
280    // rustdoc-stripper-ignore-next
281    /// Logs without checking the log level.
282    #[inline]
283    #[doc(alias = "gst_debug_log_literal")]
284    pub fn log_literal_unfiltered(
285        self,
286        obj: Option<&impl IsA<glib::Object>>,
287        level: crate::DebugLevel,
288        file: &glib::GStr,
289        function: &str,
290        line: u32,
291        msg: &glib::GStr,
292    ) {
293        self.log_literal_unfiltered_internal(
294            obj.map(|obj| obj.as_ref()),
295            level,
296            file,
297            function,
298            line,
299            msg,
300        )
301    }
302
303    #[inline(never)]
304    fn log_unfiltered_internal(
305        self,
306        obj: Option<&glib::Object>,
307        level: crate::DebugLevel,
308        file: &glib::GStr,
309        function: &str,
310        line: u32,
311        args: fmt::Arguments,
312    ) {
313        let mut w = smallvec::SmallVec::<[u8; 256]>::new();
314
315        // Can't really happen but better safe than sorry
316        if std::io::Write::write_fmt(&mut w, args).is_err() {
317            return;
318        }
319        w.push(0);
320
321        self.log_literal_unfiltered_internal(obj, level, file, function, line, unsafe {
322            glib::GStr::from_utf8_with_nul_unchecked(&w)
323        });
324    }
325
326    #[inline(never)]
327    fn log_literal_unfiltered_internal(
328        self,
329        obj: Option<&glib::Object>,
330        level: crate::DebugLevel,
331        file: &glib::GStr,
332        function: &str,
333        line: u32,
334        msg: &glib::GStr,
335    ) {
336        let cat = match self.0 {
337            Some(cat) => cat,
338            None => return,
339        };
340
341        let obj_ptr = match obj {
342            Some(obj) => obj.as_ptr(),
343            None => ptr::null_mut(),
344        };
345
346        function.run_with_gstr(|function| {
347            #[cfg(feature = "v1_20")]
348            unsafe {
349                ffi::gst_debug_log_literal(
350                    cat.as_ptr(),
351                    level.into_glib(),
352                    file.as_ptr(),
353                    function.as_ptr(),
354                    line as i32,
355                    obj_ptr,
356                    msg.as_ptr(),
357                );
358            }
359            #[cfg(not(feature = "v1_20"))]
360            unsafe {
361                ffi::gst_debug_log(
362                    cat.as_ptr(),
363                    level.into_glib(),
364                    file.as_ptr(),
365                    function.as_ptr(),
366                    line as i32,
367                    obj_ptr,
368                    b"%s\0".as_ptr() as *const _,
369                    msg.as_ptr(),
370                );
371            }
372        });
373    }
374
375    #[cfg(feature = "v1_22")]
376    #[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))]
377    #[inline]
378    #[doc(alias = "gst_debug_log_id")]
379    pub fn log_id(
380        self,
381        id: impl AsRef<glib::GStr>,
382        level: crate::DebugLevel,
383        file: &glib::GStr,
384        function: &str,
385        line: u32,
386        args: fmt::Arguments,
387    ) {
388        if !self.above_threshold(level) {
389            return;
390        }
391
392        self.log_id_unfiltered_internal(id.as_ref(), level, file, function, line, args);
393    }
394
395    #[cfg(feature = "v1_22")]
396    #[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))]
397    #[inline]
398    #[doc(alias = "gst_debug_log_id_literal")]
399    pub fn log_id_literal(
400        self,
401        id: impl AsRef<glib::GStr>,
402        level: crate::DebugLevel,
403        file: &glib::GStr,
404        function: &str,
405        line: u32,
406        msg: &glib::GStr,
407    ) {
408        if !self.above_threshold(level) {
409            return;
410        }
411
412        self.log_id_literal_unfiltered_internal(id.as_ref(), level, file, function, line, msg);
413    }
414
415    #[cfg(feature = "v1_22")]
416    #[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))]
417    // rustdoc-stripper-ignore-next
418    /// Logs without checking the log level.
419    #[inline]
420    #[doc(alias = "gst_debug_log_id")]
421    pub fn log_id_unfiltered(
422        self,
423        id: impl AsRef<glib::GStr>,
424        level: crate::DebugLevel,
425        file: &glib::GStr,
426        function: &str,
427        line: u32,
428        args: fmt::Arguments,
429    ) {
430        self.log_id_unfiltered_internal(id.as_ref(), level, file, function, line, args)
431    }
432
433    #[cfg(feature = "v1_22")]
434    #[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))]
435    // rustdoc-stripper-ignore-next
436    /// Logs without checking the log level.
437    #[inline]
438    #[doc(alias = "gst_debug_log_id_literal")]
439    pub fn log_id_literal_unfiltered(
440        self,
441        id: impl AsRef<glib::GStr>,
442        level: crate::DebugLevel,
443        file: &glib::GStr,
444        function: &str,
445        line: u32,
446        msg: &glib::GStr,
447    ) {
448        self.log_id_literal_unfiltered_internal(id.as_ref(), level, file, function, line, msg)
449    }
450
451    #[cfg(feature = "v1_22")]
452    #[inline(never)]
453    fn log_id_unfiltered_internal(
454        self,
455        id: &glib::GStr,
456        level: crate::DebugLevel,
457        file: &glib::GStr,
458        function: &str,
459        line: u32,
460        args: fmt::Arguments,
461    ) {
462        let mut w = smallvec::SmallVec::<[u8; 256]>::new();
463
464        // Can't really happen but better safe than sorry
465        if std::io::Write::write_fmt(&mut w, args).is_err() {
466            return;
467        }
468        w.push(0);
469
470        self.log_id_literal_unfiltered_internal(id, level, file, function, line, unsafe {
471            glib::GStr::from_utf8_with_nul_unchecked(&w)
472        });
473    }
474
475    #[cfg(feature = "v1_22")]
476    #[inline(never)]
477    fn log_id_literal_unfiltered_internal(
478        self,
479        id: &glib::GStr,
480        level: crate::DebugLevel,
481        file: &glib::GStr,
482        function: &str,
483        line: u32,
484        msg: &glib::GStr,
485    ) {
486        let cat = match self.0 {
487            Some(cat) => cat,
488            None => return,
489        };
490
491        function.run_with_gstr(|function| unsafe {
492            ffi::gst_debug_log_id_literal(
493                cat.as_ptr(),
494                level.into_glib(),
495                file.as_ptr(),
496                function.as_ptr(),
497                line as i32,
498                id.as_ptr(),
499                msg.as_ptr(),
500            );
501        });
502    }
503
504    #[doc(alias = "get_all_categories")]
505    #[doc(alias = "gst_debug_get_all_categories")]
506    #[inline]
507    pub fn all_categories() -> glib::SList<DebugCategory> {
508        unsafe { glib::SList::from_glib_container(ffi::gst_debug_get_all_categories()) }
509    }
510
511    #[cfg(feature = "v1_18")]
512    #[cfg_attr(docsrs, doc(cfg(feature = "v1_18")))]
513    #[doc(alias = "gst_debug_log_get_line")]
514    #[inline]
515    pub fn get_line(
516        &self,
517        level: crate::DebugLevel,
518        file: &glib::GStr,
519        function: &glib::GStr,
520        line: u32,
521        object: Option<&LoggedObject>,
522        message: &DebugMessage,
523    ) -> Option<glib::GString> {
524        let cat = self.0?;
525
526        unsafe {
527            from_glib_full(ffi::gst_debug_log_get_line(
528                cat.as_ptr(),
529                level.into_glib(),
530                file.as_ptr(),
531                function.as_ptr(),
532                line as i32,
533                object.map(|o| o.as_ptr()).unwrap_or(ptr::null_mut()),
534                message.0.as_ptr(),
535            ))
536        }
537    }
538
539    #[inline]
540    pub fn as_ptr(&self) -> *mut ffi::GstDebugCategory {
541        self.0.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut())
542    }
543}
544
545unsafe impl Sync for DebugCategory {}
546unsafe impl Send for DebugCategory {}
547
548impl fmt::Debug for DebugCategory {
549    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
550        f.debug_tuple("DebugCategory").field(&self.name()).finish()
551    }
552}
553
554impl GlibPtrDefault for DebugCategory {
555    type GlibType = *mut ffi::GstDebugCategory;
556}
557
558unsafe impl TransparentPtrType for DebugCategory {}
559
560impl FromGlibPtrNone<*mut ffi::GstDebugCategory> for DebugCategory {
561    #[inline]
562    unsafe fn from_glib_none(ptr: *mut ffi::GstDebugCategory) -> Self {
563        debug_assert!(!ptr.is_null());
564        DebugCategory(Some(ptr::NonNull::new_unchecked(ptr)))
565    }
566}
567
568impl FromGlibPtrFull<*mut ffi::GstDebugCategory> for DebugCategory {
569    #[inline]
570    unsafe fn from_glib_full(ptr: *mut ffi::GstDebugCategory) -> Self {
571        debug_assert!(!ptr.is_null());
572        DebugCategory(Some(ptr::NonNull::new_unchecked(ptr)))
573    }
574}
575
576pub static CAT_RUST: Lazy<DebugCategory> = Lazy::new(|| {
577    DebugCategory::new(
578        "GST_RUST",
579        crate::DebugColorFlags::UNDERLINE,
580        Some("GStreamer's Rust binding core"),
581    )
582});
583
584macro_rules! declare_debug_category_from_name(
585    ($cat:ident, $cat_name:expr) => (
586        pub static $cat: Lazy<DebugCategory> = Lazy::new(|| DebugCategory::get($cat_name)
587            .expect(&format!("Unable to find `DebugCategory` with name {}", $cat_name)));
588    );
589);
590
591declare_debug_category_from_name!(CAT_DEFAULT, "default");
592declare_debug_category_from_name!(CAT_GST_INIT, "GST_INIT");
593declare_debug_category_from_name!(CAT_MEMORY, "GST_MEMORY");
594declare_debug_category_from_name!(CAT_PARENTAGE, "GST_PARENTAGE");
595declare_debug_category_from_name!(CAT_STATES, "GST_STATES");
596declare_debug_category_from_name!(CAT_SCHEDULING, "GST_SCHEDULING");
597declare_debug_category_from_name!(CAT_BUFFER, "GST_BUFFER");
598declare_debug_category_from_name!(CAT_BUFFER_LIST, "GST_BUFFER_LIST");
599declare_debug_category_from_name!(CAT_BUS, "GST_BUS");
600declare_debug_category_from_name!(CAT_CAPS, "GST_CAPS");
601declare_debug_category_from_name!(CAT_CLOCK, "GST_CLOCK");
602declare_debug_category_from_name!(CAT_ELEMENT_PADS, "GST_ELEMENT_PADS");
603declare_debug_category_from_name!(CAT_PADS, "GST_PADS");
604declare_debug_category_from_name!(CAT_PERFORMANCE, "GST_PERFORMANCE");
605declare_debug_category_from_name!(CAT_PIPELINE, "GST_PIPELINE");
606declare_debug_category_from_name!(CAT_PLUGIN_LOADING, "GST_PLUGIN_LOADING");
607declare_debug_category_from_name!(CAT_PLUGIN_INFO, "GST_PLUGIN_INFO");
608declare_debug_category_from_name!(CAT_PROPERTIES, "GST_PROPERTIES");
609declare_debug_category_from_name!(CAT_NEGOTIATION, "GST_NEGOTIATION");
610declare_debug_category_from_name!(CAT_REFCOUNTING, "GST_REFCOUNTING");
611declare_debug_category_from_name!(CAT_ERROR_SYSTEM, "GST_ERROR_SYSTEM");
612declare_debug_category_from_name!(CAT_EVENT, "GST_EVENT");
613declare_debug_category_from_name!(CAT_MESSAGE, "GST_MESSAGE");
614declare_debug_category_from_name!(CAT_PARAMS, "GST_PARAMS");
615declare_debug_category_from_name!(CAT_CALL_TRACE, "GST_CALL_TRACE");
616declare_debug_category_from_name!(CAT_SIGNAL, "GST_SIGNAL");
617declare_debug_category_from_name!(CAT_PROBE, "GST_PROBE");
618declare_debug_category_from_name!(CAT_REGISTRY, "GST_REGISTRY");
619declare_debug_category_from_name!(CAT_QOS, "GST_QOS");
620declare_debug_category_from_name!(CAT_META, "GST_META");
621declare_debug_category_from_name!(CAT_LOCKING, "GST_LOCKING");
622declare_debug_category_from_name!(CAT_CONTEXT, "GST_CONTEXT");
623
624#[macro_export]
625macro_rules! error(
626    ($cat:expr, obj = $obj:expr, $($args:tt)*) => { {
627        $crate::log_with_level!($cat, $crate::DebugLevel::Error, obj = $obj, $($args)*)
628    }};
629    ($cat:expr, imp = $imp:expr, $($args:tt)*) => { {
630        $crate::log_with_level!($cat, $crate::DebugLevel::Error, imp = $imp, $($args)*)
631    }};
632    ($cat:expr, id = $id:expr, $($args:tt)*) => { {
633        $crate::log_with_level!($cat, $crate::DebugLevel::Error, id = $id, $($args)*)
634    }};
635
636    ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
637        {
638            #[deprecated = "Using old-style obj format. Use `obj = ` instead of `obj: ` for better tooling support"]
639            macro_rules! error(
640                () => {}
641            );
642            error!();
643        }
644        $crate::log_with_level!($cat, $crate::DebugLevel::Error, obj = $obj, $($args)*)
645    }};
646    ($cat:expr, imp: $imp:expr, $($args:tt)*) => { {
647        {
648            #[deprecated = "Using old-style imp format. Use `imp = ` instead of `imp: ` for better tooling support"]
649            macro_rules! error(
650                () => {}
651            );
652            error!();
653        }
654        $crate::log_with_level!($cat, $crate::DebugLevel::Error, imp = $imp, $($args)*)
655    }};
656    ($cat:expr, id: $id:expr, $($args:tt)*) => { {
657        {
658            #[deprecated = "Using old-style id format. Use `id = ` instead of `id: ` for better tooling support"]
659            macro_rules! error(
660                () => {}
661            );
662            error!();
663        }
664        $crate::log_with_level!($cat, $crate::DebugLevel::Error, id = $id, $($args)*)
665    }};
666    ($cat:expr, $($args:tt)*) => { {
667        $crate::log_with_level!($cat, $crate::DebugLevel::Error, $($args)*)
668    }};
669);
670
671#[macro_export]
672macro_rules! warning(
673    ($cat:expr, obj = $obj:expr, $($args:tt)*) => { {
674        $crate::log_with_level!($cat, $crate::DebugLevel::Warning, obj = $obj, $($args)*)
675    }};
676    ($cat:expr, imp = $imp:expr, $($args:tt)*) => { {
677        $crate::log_with_level!($cat, $crate::DebugLevel::Warning, imp = $imp, $($args)*)
678    }};
679    ($cat:expr, id = $id:expr, $($args:tt)*) => { {
680        $crate::log_with_level!($cat, $crate::DebugLevel::Warning, id = $id, $($args)*)
681    }};
682
683    ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
684        {
685            #[deprecated = "Using old-style obj format. Use `obj = ` instead of `obj: ` for better tooling support"]
686            macro_rules! warning(
687                () => {}
688            );
689            warning!();
690        }
691        $crate::log_with_level!($cat, $crate::DebugLevel::Warning, obj = $obj, $($args)*)
692    }};
693    ($cat:expr, imp: $imp:expr, $($args:tt)*) => { {
694        {
695            #[deprecated = "Using old-style imp format. Use `imp = ` instead of `imp: ` for better tooling support"]
696            macro_rules! warning(
697                () => {}
698            );
699            warning!();
700        }
701        $crate::log_with_level!($cat, $crate::DebugLevel::Warning, imp = $imp, $($args)*)
702    }};
703    ($cat:expr, id: $id:expr, $($args:tt)*) => { {
704        {
705            #[deprecated = "Using old-style id format. Use `id = ` instead of `id: ` for better tooling support"]
706            macro_rules! warning(
707                () => {}
708            );
709            warning!();
710        }
711        $crate::log_with_level!($cat, $crate::DebugLevel::Warning, id = $id, $($args)*)
712    }};
713    ($cat:expr, $($args:tt)*) => { {
714        $crate::log_with_level!($cat, $crate::DebugLevel::Warning, $($args)*)
715    }};
716);
717
718#[macro_export]
719macro_rules! fixme(
720    ($cat:expr, obj = $obj:expr, $($args:tt)*) => { {
721        $crate::log_with_level!($cat, $crate::DebugLevel::Fixme, obj = $obj, $($args)*)
722    }};
723    ($cat:expr, imp = $imp:expr, $($args:tt)*) => { {
724        $crate::log_with_level!($cat, $crate::DebugLevel::Fixme, imp = $imp, $($args)*)
725    }};
726    ($cat:expr, id = $id:expr, $($args:tt)*) => { {
727        $crate::log_with_level!($cat, $crate::DebugLevel::Fixme, id = $id, $($args)*)
728    }};
729
730    ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
731        {
732            #[deprecated = "Using old-style obj format. Use `obj = ` instead of `obj: ` for better tooling support"]
733            macro_rules! fixme(
734                () => {}
735            );
736            fixme!();
737        }
738        $crate::log_with_level!($cat, $crate::DebugLevel::Fixme, obj = $obj, $($args)*)
739    }};
740    ($cat:expr, imp: $imp:expr, $($args:tt)*) => { {
741        {
742            #[deprecated = "Using old-style imp format. Use `imp = ` instead of `imp: ` for better tooling support"]
743            macro_rules! fixme(
744                () => {}
745            );
746            fixme!();
747        }
748        $crate::log_with_level!($cat, $crate::DebugLevel::Fixme, imp = $imp, $($args)*)
749    }};
750    ($cat:expr, id: $id:expr, $($args:tt)*) => { {
751        {
752            #[deprecated = "Using old-style id format. Use `id = ` instead of `id: ` for better tooling support"]
753            macro_rules! fixme(
754                () => {}
755            );
756            fixme!();
757        }
758        $crate::log_with_level!($cat, $crate::DebugLevel::Fixme, id = $id, $($args)*)
759    }};
760    ($cat:expr, $($args:tt)*) => { {
761        $crate::log_with_level!($cat, $crate::DebugLevel::Fixme, $($args)*)
762    }};
763);
764
765#[macro_export]
766macro_rules! info(
767    ($cat:expr, obj = $obj:expr, $($args:tt)*) => { {
768        $crate::log_with_level!($cat, $crate::DebugLevel::Info, obj = $obj, $($args)*)
769    }};
770    ($cat:expr, imp = $imp:expr, $($args:tt)*) => { {
771        $crate::log_with_level!($cat, $crate::DebugLevel::Info, imp = $imp, $($args)*)
772    }};
773    ($cat:expr, id = $id:expr, $($args:tt)*) => { {
774        $crate::log_with_level!($cat, $crate::DebugLevel::Info, id = $id, $($args)*)
775    }};
776
777    ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
778        {
779            #[deprecated = "Using old-style obj format. Use `obj = ` instead of `obj: ` for better tooling support"]
780            macro_rules! info(
781                () => {}
782            );
783            info!();
784        }
785        $crate::log_with_level!($cat, $crate::DebugLevel::Info, obj = $obj, $($args)*)
786    }};
787    ($cat:expr, imp: $imp:expr, $($args:tt)*) => { {
788        {
789            #[deprecated = "Using old-style imp format. Use `imp = ` instead of `imp: ` for better tooling support"]
790            macro_rules! info(
791                () => {}
792            );
793            info!();
794        }
795        $crate::log_with_level!($cat, $crate::DebugLevel::Info, imp = $imp, $($args)*)
796    }};
797    ($cat:expr, id: $id:expr, $($args:tt)*) => { {
798        {
799            #[deprecated = "Using old-style id format. Use `id = ` instead of `id: ` for better tooling support"]
800            macro_rules! info(
801                () => {}
802            );
803            info!();
804        }
805        $crate::log_with_level!($cat, $crate::DebugLevel::Info, id = $id, $($args)*)
806    }};
807    ($cat:expr, $($args:tt)*) => { {
808        $crate::log_with_level!($cat, $crate::DebugLevel::Info, $($args)*)
809    }};
810);
811
812#[macro_export]
813macro_rules! debug(
814    ($cat:expr, obj = $obj:expr, $($args:tt)*) => { {
815        $crate::log_with_level!($cat, $crate::DebugLevel::Debug, obj = $obj, $($args)*)
816    }};
817    ($cat:expr, imp = $imp:expr, $($args:tt)*) => { {
818        $crate::log_with_level!($cat, $crate::DebugLevel::Debug, imp = $imp, $($args)*)
819    }};
820    ($cat:expr, id = $id:expr, $($args:tt)*) => { {
821        $crate::log_with_level!($cat, $crate::DebugLevel::Debug, id = $id, $($args)*)
822    }};
823
824    ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
825        {
826            #[deprecated = "Using old-style obj format. Use `obj = ` instead of `obj: ` for better tooling support"]
827            macro_rules! debug(
828                () => {}
829            );
830            debug!();
831        }
832        $crate::log_with_level!($cat, $crate::DebugLevel::Debug, obj = $obj, $($args)*)
833    }};
834    ($cat:expr, imp: $imp:expr, $($args:tt)*) => { {
835        {
836            #[deprecated = "Using old-style imp format. Use `imp = ` instead of `imp: ` for better tooling support"]
837            macro_rules! debug(
838                () => {}
839            );
840            debug!();
841        }
842        $crate::log_with_level!($cat, $crate::DebugLevel::Debug, imp = $imp, $($args)*)
843    }};
844    ($cat:expr, id: $id:expr, $($args:tt)*) => { {
845        {
846            #[deprecated = "Using old-style id format. Use `id = ` instead of `id: ` for better tooling support"]
847            macro_rules! debug(
848                () => {}
849            );
850            debug!();
851        }
852        $crate::log_with_level!($cat, $crate::DebugLevel::Debug, id = $id, $($args)*)
853    }};
854    ($cat:expr, $($args:tt)*) => { {
855        $crate::log_with_level!($cat, $crate::DebugLevel::Debug, $($args)*)
856    }};
857);
858
859#[macro_export]
860macro_rules! log(
861    ($cat:expr, obj = $obj:expr, $($args:tt)*) => { {
862        $crate::log_with_level!($cat, $crate::DebugLevel::Log, obj = $obj, $($args)*)
863    }};
864    ($cat:expr, imp = $imp:expr, $($args:tt)*) => { {
865        $crate::log_with_level!($cat, $crate::DebugLevel::Log, imp = $imp, $($args)*)
866    }};
867    ($cat:expr, id = $id:expr, $($args:tt)*) => { {
868        $crate::log_with_level!($cat, $crate::DebugLevel::Log, id = $id, $($args)*)
869    }};
870
871    ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
872        {
873            #[deprecated = "Using old-style obj format. Use `obj = ` instead of `obj: ` for better tooling support"]
874            macro_rules! log(
875                () => {}
876            );
877            log!();
878        }
879        $crate::log_with_level!($cat, $crate::DebugLevel::Log, obj = $obj, $($args)*)
880    }};
881    ($cat:expr, imp: $imp:expr, $($args:tt)*) => { {
882        {
883            #[deprecated = "Using old-style imp format. Use `imp = ` instead of `imp: ` for better tooling support"]
884            macro_rules! log(
885                () => {}
886            );
887            log!();
888        }
889        $crate::log_with_level!($cat, $crate::DebugLevel::Log, imp = $imp, $($args)*)
890    }};
891    ($cat:expr, id: $id:expr, $($args:tt)*) => { {
892        {
893            #[deprecated = "Using old-style id format. Use `id = ` instead of `id: ` for better tooling support"]
894            macro_rules! log(
895                () => {}
896            );
897            log!();
898        }
899        $crate::log_with_level!($cat, $crate::DebugLevel::Log, id = $id, $($args)*)
900    }};
901    ($cat:expr, $($args:tt)*) => { {
902        $crate::log_with_level!($cat, $crate::DebugLevel::Log, $($args)*)
903    }};
904);
905
906#[macro_export]
907macro_rules! trace(
908    ($cat:expr, obj = $obj:expr, $($args:tt)*) => { {
909        $crate::log_with_level!($cat, $crate::DebugLevel::Trace, obj = $obj, $($args)*)
910    }};
911    ($cat:expr, imp = $imp:expr, $($args:tt)*) => { {
912        $crate::log_with_level!($cat, $crate::DebugLevel::Trace, imp = $imp, $($args)*)
913    }};
914    ($cat:expr, id = $id:expr, $($args:tt)*) => { {
915        $crate::log_with_level!($cat, $crate::DebugLevel::Trace, id = $id, $($args)*)
916    }};
917
918    ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
919        {
920            #[deprecated = "Using old-style obj format. Use `obj = ` instead of `obj: ` for better tooling support"]
921            macro_rules! trace(
922                () => {}
923            );
924            trace!();
925        }
926        $crate::log_with_level!($cat, $crate::DebugLevel::Trace, obj = $obj, $($args)*)
927    }};
928    ($cat:expr, imp: $imp:expr, $($args:tt)*) => { {
929        {
930            #[deprecated = "Using old-style imp format. Use `imp = ` instead of `imp: ` for better tooling support"]
931            macro_rules! trace(
932                () => {}
933            );
934            trace!();
935        }
936        $crate::log_with_level!($cat, $crate::DebugLevel::Trace, imp = $imp, $($args)*)
937    }};
938    ($cat:expr, id: $id:expr, $($args:tt)*) => { {
939        {
940            #[deprecated = "Using old-style id format. Use `id = ` instead of `id: `"]
941            macro_rules! trace(
942                () => {}
943            );
944            trace!();
945        }
946        $crate::log_with_level!($cat, $crate::DebugLevel::Trace, id = $id, $($args)*)
947    }};
948    ($cat:expr, $($args:tt)*) => { {
949        $crate::log_with_level!($cat, $crate::DebugLevel::Trace, $($args)*)
950    }};
951);
952
953#[macro_export]
954macro_rules! memdump(
955    ($cat:expr, obj = $obj:expr, $($args:tt)*) => { {
956        $crate::log_with_level!($cat, $crate::DebugLevel::Memdump, obj = $obj, $($args)*)
957    }};
958    ($cat:expr, imp = $imp:expr, $($args:tt)*) => { {
959        $crate::log_with_level!($cat, $crate::DebugLevel::Memdump, imp = $imp, $($args)*)
960    }};
961    ($cat:expr, id = $id:expr, $($args:tt)*) => { {
962        $crate::log_with_level!($cat, $crate::DebugLevel::Memdump, id = $id, $($args)*)
963    }};
964
965    ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
966        {
967            #[deprecated = "Using old-style obj format. Use `obj = ` instead of `obj: ` for better tooling support"]
968            macro_rules! memdump(
969                () => {}
970            );
971            memdump!();
972        }
973        $crate::log_with_level!($cat, $crate::DebugLevel::Memdump, obj = $obj, $($args)*)
974    }};
975    ($cat:expr, imp: $imp:expr, $($args:tt)*) => { {
976        {
977            #[deprecated = "Using old-style imp format. Use `imp = ` instead of `imp: ` for better tooling support"]
978            macro_rules! memdump(
979                () => {}
980            );
981            memdump!();
982        }
983        $crate::log_with_level!($cat, $crate::DebugLevel::Memdump, imp = $imp, $($args)*)
984    }};
985    ($cat:expr, id: $id:expr, $($args:tt)*) => { {
986        {
987            #[deprecated = "Using old-style id format. Use `id = ` instead of `id: ` for better tooling support"]
988            macro_rules! memdump(
989                () => {}
990            );
991            memdump!();
992        }
993        $crate::log_with_level!($cat, $crate::DebugLevel::Memdump, id = $id, $($args)*)
994    }};
995    ($cat:expr, $($args:tt)*) => { {
996        $crate::log_with_level!($cat, $crate::DebugLevel::Memdump, $($args)*)
997    }};
998);
999
1000#[macro_export]
1001macro_rules! log_with_level(
1002    ($cat:expr, $level:expr, obj = $obj:expr, $msg:literal) => { {
1003        let cat = $cat.clone();
1004
1005        // Check the log level before using `format_args!` otherwise
1006        // formatted arguments are evaluated even if we end up not logging.
1007        #[allow(unused_unsafe)]
1008        #[allow(clippy::redundant_closure_call)]
1009        if cat.above_threshold($level) {
1010            use $crate::glib::prelude::Cast;
1011
1012            // FIXME: Once there's a function_name! macro that returns a string literal we can
1013            // directly pass it as `&GStr` forward
1014
1015            let obj = &$obj;
1016            let obj = unsafe { obj.unsafe_cast_ref::<$crate::glib::Object>() };
1017            let function_name = $crate::glib::function_name!();
1018
1019            // Check if formatting is necessary or not
1020            // FIXME: This needs to be a closure because the return value of format_args!() can't
1021            // be assigned to a variable
1022            (|args: std::fmt::Arguments| {
1023                if args.as_str().is_some() {
1024                    $crate::DebugCategory::log_literal_unfiltered(
1025                        cat,
1026                        Some(obj),
1027                        $level,
1028                        unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1029                        function_name,
1030                        line!(),
1031                        $crate::glib::gstr!($msg),
1032                    )
1033                } else {
1034                    $crate::DebugCategory::log_unfiltered(
1035                        cat,
1036                        Some(obj),
1037                        $level,
1038                        unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1039                        function_name,
1040                        line!(),
1041                        args,
1042                    )
1043                }
1044            })(format_args!($msg))
1045        }
1046    }};
1047    ($cat:expr, $level:expr, obj = $obj:expr, $($args:tt)*) => { {
1048        let cat = $cat.clone();
1049
1050        // Check the log level before using `format_args!` otherwise
1051        // formatted arguments are evaluated even if we end up not logging.
1052        #[allow(unused_unsafe)]
1053        if cat.above_threshold($level) {
1054            use $crate::glib::prelude::Cast;
1055
1056            // FIXME: Once there's a function_name! macro that returns a string literal we can
1057            // directly pass it as `&GStr` forward
1058
1059            let obj = &$obj;
1060            let obj = unsafe { obj.unsafe_cast_ref::<$crate::glib::Object>() };
1061            $crate::DebugCategory::log_unfiltered(
1062                cat,
1063                Some(obj),
1064                $level,
1065                unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1066                $crate::glib::function_name!(),
1067                line!(),
1068                format_args!($($args)*),
1069            )
1070        }
1071    }};
1072    ($cat:expr, $level:expr, imp = $imp:expr, $msg:literal) => { {
1073        let cat = $cat.clone();
1074
1075        // Check the log level before using `format_args!` otherwise
1076        // formatted arguments are evaluated even if we end up not logging.
1077        #[allow(unused_unsafe)]
1078        #[allow(clippy::redundant_closure_call)]
1079        if cat.above_threshold($level) {
1080            use $crate::glib::prelude::Cast;
1081
1082            // FIXME: Once there's a function_name! macro that returns a string literal we can
1083            // directly pass it as `&GStr` forward
1084
1085            let obj = $imp.obj();
1086            let obj = unsafe { obj.unsafe_cast_ref::<$crate::glib::Object>() };
1087            let function_name = $crate::glib::function_name!();
1088
1089            // Check if formatting is necessary or not
1090            // FIXME: This needs to be a closure because the return value of format_args!() can't
1091            // be assigned to a variable
1092            (|args: std::fmt::Arguments| {
1093                if args.as_str().is_some() {
1094                    $crate::DebugCategory::log_literal_unfiltered(
1095                        cat,
1096                        Some(obj),
1097                        $level,
1098                        unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1099                        function_name,
1100                        line!(),
1101                        $crate::glib::gstr!($msg),
1102                    )
1103                } else {
1104                    $crate::DebugCategory::log_unfiltered(
1105                        cat,
1106                        Some(obj),
1107                        $level,
1108                        unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1109                        function_name,
1110                        line!(),
1111                        args,
1112                    )
1113                }
1114            })(format_args!($msg))
1115        }
1116    }};
1117    ($cat:expr, $level:expr, imp = $imp:expr, $($args:tt)*) => { {
1118        let cat = $cat.clone();
1119
1120        // Check the log level before using `format_args!` otherwise
1121        // formatted arguments are evaluated even if we end up not logging.
1122        #[allow(unused_unsafe)]
1123        if cat.above_threshold($level) {
1124            use $crate::glib::prelude::Cast;
1125
1126            // FIXME: Once there's a function_name! macro that returns a string literal we can
1127            // directly pass it as `&GStr` forward
1128
1129            let obj = $imp.obj();
1130            let obj = unsafe { obj.unsafe_cast_ref::<$crate::glib::Object>() };
1131            $crate::DebugCategory::log_unfiltered(
1132                cat,
1133                Some(obj),
1134                $level,
1135                unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1136                $crate::glib::function_name!(),
1137                line!(),
1138                format_args!($($args)*),
1139            )
1140        }
1141    }};
1142    ($cat:expr, $level:expr, id = $id:literal, $msg:literal) => { {
1143        let cat = $cat.clone();
1144
1145        // Check the log level before using `format_args!` otherwise
1146        // formatted arguments are evaluated even if we end up not logging.
1147        #[allow(unused_unsafe)]
1148        #[allow(clippy::redundant_closure_call)]
1149        if cat.above_threshold($level) {
1150            // FIXME: Once there's a function_name! macro that returns a string literal we can
1151            // directly pass it as `&GStr` forward
1152
1153            let function_name = $crate::glib::function_name!();
1154
1155            // Check if formatting is necessary or not
1156            // FIXME: This needs to be a closure because the return value of format_args!() can't
1157            // be assigned to a variable
1158            (|args: std::fmt::Arguments| {
1159                if args.as_str().is_some() {
1160                    $crate::DebugCategory::log_id_literal_unfiltered(
1161                        cat,
1162                        $crate::glib::gstr!($id),
1163                        $level,
1164                        unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1165                        function_name,
1166                        line!(),
1167                        $crate::glib::gstr!($msg),
1168                    )
1169                } else {
1170                    $crate::DebugCategory::log_id_unfiltered(
1171                        cat,
1172                        $crate::glib::gstr!($id),
1173                        $level,
1174                        unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1175                        function_name,
1176                        line!(),
1177                        args,
1178                    )
1179                }
1180            })(format_args!($msg))
1181        }
1182    }};
1183    ($cat:expr, $level:expr, id = $id:literal, $($args:tt)*) => { {
1184        let cat = $cat.clone();
1185
1186        // Check the log level before using `format_args!` otherwise
1187        // formatted arguments are evaluated even if we end up not logging.
1188        #[allow(unused_unsafe)]
1189        if cat.above_threshold($level) {
1190            // FIXME: Once there's a function_name! macro that returns a string literal we can
1191            // directly pass it as `&GStr` forward
1192
1193            $crate::DebugCategory::log_id_unfiltered(
1194                cat,
1195                $crate::glib::gstr!($id),
1196                $level,
1197                unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1198                $crate::glib::function_name!(),
1199                line!(),
1200                format_args!($($args)*),
1201            )
1202        }
1203    }};
1204    ($cat:expr, $level:expr, id = $id:expr, $msg:literal) => { {
1205        let cat = $cat.clone();
1206
1207        // Check the log level before using `format_args!` otherwise
1208        // formatted arguments are evaluated even if we end up not logging.
1209        #[allow(unused_unsafe)]
1210        #[allow(clippy::redundant_closure_call)]
1211        if cat.above_threshold($level) {
1212            // FIXME: Once there's a function_name! macro that returns a string literal we can
1213            // directly pass it as `&GStr` forward
1214
1215            let function_name = $crate::glib::function_name!();
1216
1217            // Check if formatting is necessary or not
1218            // FIXME: This needs to be a closure because the return value of format_args!() can't
1219            // be assigned to a variable
1220            (|args: std::fmt::Arguments| {
1221                if args.as_str().is_some() {
1222                    $crate::DebugCategory::log_id_literal_unfiltered(
1223                        cat,
1224                        $id,
1225                        $level,
1226                        unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1227                        function_name,
1228                        line!(),
1229                        $crate::glib::gstr!($msg),
1230                    )
1231                } else {
1232                    $crate::DebugCategory::log_id_unfiltered(
1233                        cat,
1234                        $id,
1235                        $level,
1236                        unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1237                        function_name,
1238                        line!(),
1239                        args,
1240                    )
1241                }
1242            })(format_args!($msg))
1243        }
1244    }};
1245    ($cat:expr, $level:expr, id = $id:expr, $($args:tt)*) => { {
1246        let cat = $cat.clone();
1247
1248        // Check the log level before using `format_args!` otherwise
1249        // formatted arguments are evaluated even if we end up not logging.
1250        #[allow(unused_unsafe)]
1251        if cat.above_threshold($level) {
1252            // FIXME: Once there's a function_name! macro that returns a string literal we can
1253            // directly pass it as `&GStr` forward
1254
1255            $crate::DebugCategory::log_id_unfiltered(
1256                cat,
1257                $id,
1258                $level,
1259                unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1260                $crate::glib::function_name!(),
1261                line!(),
1262                format_args!($($args)*),
1263            )
1264        }
1265    }};
1266    ($cat:expr, $level:expr, $msg:literal) => { {
1267        let cat = $cat.clone();
1268
1269        // Check the log level before using `format_args!` otherwise
1270        // formatted arguments are evaluated even if we end up not logging.
1271        #[allow(unused_unsafe)]
1272        #[allow(clippy::redundant_closure_call)]
1273        if cat.above_threshold($level) {
1274            // FIXME: Once there's a function_name! macro that returns a string literal we can
1275            // directly pass it as `&GStr` forward
1276
1277            let function_name = $crate::glib::function_name!();
1278
1279            // Check if formatting is necessary or not
1280            // FIXME: This needs to be a closure because the return value of format_args!() can't
1281            // be assigned to a variable
1282            (|args: std::fmt::Arguments| {
1283                if args.as_str().is_some() {
1284                    $crate::DebugCategory::log_literal_unfiltered(
1285                        cat,
1286                        None as Option<&$crate::glib::Object>,
1287                        $level,
1288                        unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1289                        function_name,
1290                        line!(),
1291                        $crate::glib::gstr!($msg),
1292                    )
1293                } else {
1294                    $crate::DebugCategory::log_unfiltered(
1295                        cat,
1296                        None as Option<&$crate::glib::Object>,
1297                        $level,
1298                        unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1299                        function_name,
1300                        line!(),
1301                        args,
1302                    )
1303                }
1304            })(format_args!($msg))
1305        }
1306    }};
1307    ($cat:expr, $level:expr, $($args:tt)*) => { {
1308        let cat = $cat.clone();
1309
1310        // Check the log level before using `format_args!` otherwise
1311        // formatted arguments are evaluated even if we end up not logging.
1312        #[allow(unused_unsafe)]
1313        if cat.above_threshold($level) {
1314            // FIXME: Once there's a function_name! macro that returns a string literal we can
1315            // directly pass it as `&GStr` forward
1316
1317            $crate::DebugCategory::log_unfiltered(
1318                cat,
1319                None as Option<&$crate::glib::Object>,
1320                $level,
1321                unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1322                $crate::glib::function_name!(),
1323                line!(),
1324                format_args!($($args)*),
1325            )
1326        }
1327    }};
1328);
1329
1330#[cfg(feature = "log")]
1331#[cfg_attr(docsrs, doc(cfg(feature = "log")))]
1332#[derive(Debug)]
1333pub struct DebugCategoryLogger(DebugCategory);
1334
1335#[cfg(feature = "log")]
1336#[cfg_attr(docsrs, doc(cfg(feature = "log")))]
1337impl DebugCategoryLogger {
1338    pub fn new(cat: DebugCategory) -> Self {
1339        skip_assert_initialized!();
1340        Self(cat)
1341    }
1342
1343    fn to_level(level: log::Level) -> crate::DebugLevel {
1344        skip_assert_initialized!();
1345        match level {
1346            log::Level::Error => DebugLevel::Error,
1347            log::Level::Warn => DebugLevel::Warning,
1348            log::Level::Info => DebugLevel::Info,
1349            log::Level::Debug => DebugLevel::Debug,
1350            log::Level::Trace => DebugLevel::Trace,
1351        }
1352    }
1353}
1354
1355#[cfg(feature = "log")]
1356#[cfg_attr(docsrs, doc(cfg(feature = "log")))]
1357impl log::Log for DebugCategoryLogger {
1358    fn enabled(&self, metadata: &log::Metadata) -> bool {
1359        self.0.above_threshold(Self::to_level(metadata.level()))
1360    }
1361
1362    fn log(&self, record: &log::Record) {
1363        if !self.enabled(record.metadata()) {
1364            return;
1365        }
1366        record.file().unwrap_or("").run_with_gstr(|file| {
1367            self.0.log(
1368                None::<&glib::Object>,
1369                Self::to_level(record.level()),
1370                file,
1371                record.module_path().unwrap_or(""),
1372                record.line().unwrap_or(0),
1373                *record.args(),
1374            );
1375        });
1376    }
1377
1378    fn flush(&self) {}
1379}
1380
1381unsafe extern "C" fn log_handler<T>(
1382    category: *mut ffi::GstDebugCategory,
1383    level: ffi::GstDebugLevel,
1384    file: *const c_char,
1385    function: *const c_char,
1386    line: i32,
1387    object: *mut glib::gobject_ffi::GObject,
1388    message: *mut ffi::GstDebugMessage,
1389    user_data: gpointer,
1390) where
1391    T: Fn(
1392            DebugCategory,
1393            DebugLevel,
1394            &glib::GStr,
1395            &glib::GStr,
1396            u32,
1397            Option<&LoggedObject>,
1398            &DebugMessage,
1399        ) + Send
1400        + Sync
1401        + 'static,
1402{
1403    if category.is_null() {
1404        return;
1405    }
1406    let category = DebugCategory(Some(ptr::NonNull::new_unchecked(category)));
1407    let level = from_glib(level);
1408    let file = glib::GStr::from_ptr(file);
1409    let function = glib::GStr::from_ptr(function);
1410    let line = line as u32;
1411    let object = ptr::NonNull::new(object).map(LoggedObject);
1412    let message = DebugMessage(ptr::NonNull::new_unchecked(message));
1413    let handler = &*(user_data as *mut T);
1414    (handler)(
1415        category,
1416        level,
1417        file,
1418        function,
1419        line,
1420        object.as_ref(),
1421        &message,
1422    );
1423}
1424
1425unsafe extern "C" fn log_handler_data_free<T>(data: gpointer) {
1426    let data = Box::from_raw(data as *mut T);
1427    drop(data);
1428}
1429
1430#[derive(Debug)]
1431pub struct DebugLogFunction(ptr::NonNull<std::os::raw::c_void>);
1432
1433// The contained pointer is never dereferenced and has no thread affinity.
1434// It may be convenient to send it or share it between threads to allow cleaning
1435// up log functions from other threads than the one that created it.
1436unsafe impl Send for DebugLogFunction {}
1437unsafe impl Sync for DebugLogFunction {}
1438
1439#[derive(Debug)]
1440#[doc(alias = "GObject")]
1441pub struct LoggedObject(ptr::NonNull<glib::gobject_ffi::GObject>);
1442
1443impl LoggedObject {
1444    #[inline]
1445    pub fn as_ptr(&self) -> *mut glib::gobject_ffi::GObject {
1446        self.0.as_ptr()
1447    }
1448}
1449
1450impl fmt::Display for LoggedObject {
1451    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1452        unsafe {
1453            let ptr = self.0.as_ptr();
1454            let g_type_instance = &mut (*ptr).g_type_instance;
1455            if glib::gobject_ffi::g_type_check_instance_is_fundamentally_a(
1456                g_type_instance,
1457                glib::gobject_ffi::g_object_get_type(),
1458            ) != glib::ffi::GFALSE
1459            {
1460                let type_ = (*g_type_instance.g_class).g_type;
1461
1462                if glib::gobject_ffi::g_type_is_a(type_, ffi::gst_pad_get_type())
1463                    != glib::ffi::GFALSE
1464                {
1465                    let name_ptr = (*(ptr as *mut ffi::GstObject)).name;
1466                    let name = if name_ptr.is_null() {
1467                        "<null>"
1468                    } else {
1469                        CStr::from_ptr(name_ptr)
1470                            .to_str()
1471                            .unwrap_or("<invalid name>")
1472                    };
1473
1474                    let parent_ptr = (*(ptr as *mut ffi::GstObject)).parent;
1475                    let parent_name = if parent_ptr.is_null() {
1476                        "<null>"
1477                    } else {
1478                        let name_ptr = (*(parent_ptr)).name;
1479                        if name_ptr.is_null() {
1480                            "<null>"
1481                        } else {
1482                            CStr::from_ptr(name_ptr)
1483                                .to_str()
1484                                .unwrap_or("<invalid name>")
1485                        }
1486                    };
1487
1488                    write!(f, "{parent_name}:{name}")
1489                } else if glib::gobject_ffi::g_type_is_a(type_, ffi::gst_object_get_type())
1490                    != glib::ffi::GFALSE
1491                {
1492                    let name_ptr = (*(ptr as *mut ffi::GstObject)).name;
1493                    let name = if name_ptr.is_null() {
1494                        "<null>"
1495                    } else {
1496                        CStr::from_ptr(name_ptr)
1497                            .to_str()
1498                            .unwrap_or("<invalid name>")
1499                    };
1500                    write!(f, "{name}")
1501                } else {
1502                    let type_name = CStr::from_ptr(glib::gobject_ffi::g_type_name(type_));
1503                    write!(
1504                        f,
1505                        "{}:{:?}",
1506                        type_name.to_str().unwrap_or("<invalid type>"),
1507                        ptr
1508                    )
1509                }
1510            } else {
1511                write!(f, "{ptr:?}")
1512            }
1513        }
1514    }
1515}
1516
1517#[doc(alias = "gst_debug_add_log_function")]
1518pub fn add_log_function<T>(function: T) -> DebugLogFunction
1519where
1520    T: Fn(
1521            DebugCategory,
1522            DebugLevel,
1523            &glib::GStr,
1524            &glib::GStr,
1525            u32,
1526            Option<&LoggedObject>,
1527            &DebugMessage,
1528        ) + Send
1529        + Sync
1530        + 'static,
1531{
1532    skip_assert_initialized!();
1533    unsafe {
1534        let user_data = Box::new(function);
1535        let user_data_ptr = Box::into_raw(user_data) as gpointer;
1536        ffi::gst_debug_add_log_function(
1537            Some(log_handler::<T>),
1538            user_data_ptr,
1539            Some(log_handler_data_free::<T>),
1540        );
1541        DebugLogFunction(ptr::NonNull::new_unchecked(user_data_ptr))
1542    }
1543}
1544
1545pub fn remove_default_log_function() {
1546    skip_assert_initialized!();
1547    unsafe {
1548        ffi::gst_debug_remove_log_function(None);
1549    }
1550}
1551
1552#[doc(alias = "gst_debug_remove_log_function_by_data")]
1553pub fn remove_log_function(log_fn: DebugLogFunction) {
1554    skip_assert_initialized!();
1555    unsafe {
1556        ffi::gst_debug_remove_log_function_by_data(log_fn.0.as_ptr());
1557    }
1558}
1559
1560#[cfg(test)]
1561mod tests {
1562    use std::sync::{mpsc, Arc, Mutex};
1563
1564    use super::*;
1565
1566    #[test]
1567    #[doc(alias = "get_existing")]
1568    fn existing() {
1569        crate::init().unwrap();
1570
1571        let perf_cat = DebugCategory::get("GST_PERFORMANCE")
1572            .expect("Unable to find `DebugCategory` with name \"GST_PERFORMANCE\"");
1573        assert_eq!(perf_cat.name(), CAT_PERFORMANCE.name());
1574    }
1575
1576    #[test]
1577    fn all() {
1578        crate::init().unwrap();
1579
1580        assert!(DebugCategory::all_categories()
1581            .iter()
1582            .any(|c| c.name() == "GST_PERFORMANCE"));
1583    }
1584
1585    #[test]
1586    fn new_and_log() {
1587        crate::init().unwrap();
1588
1589        let cat = DebugCategory::new(
1590            "test-cat",
1591            crate::DebugColorFlags::empty(),
1592            Some("some debug category"),
1593        );
1594
1595        error!(cat, "meh");
1596        warning!(cat, "meh");
1597        fixme!(cat, "meh");
1598        info!(cat, "meh");
1599        debug!(cat, "meh");
1600        log!(cat, "meh");
1601        trace!(cat, "meh");
1602        memdump!(cat, "meh");
1603
1604        let obj = crate::Bin::with_name("meh");
1605
1606        error!(cat, obj = &obj, "meh");
1607        warning!(cat, obj = &obj, "meh");
1608        fixme!(cat, obj = &obj, "meh");
1609        info!(cat, obj = &obj, "meh");
1610        debug!(cat, obj = &obj, "meh");
1611        log!(cat, obj = &obj, "meh");
1612        trace!(cat, obj = &obj, "meh");
1613        memdump!(cat, obj = &obj, "meh");
1614
1615        error!(cat, obj = obj, "meh");
1616        warning!(cat, obj = obj, "meh");
1617        fixme!(cat, obj = obj, "meh");
1618        info!(cat, obj = obj, "meh");
1619        debug!(cat, obj = obj, "meh");
1620        log!(cat, obj = obj, "meh");
1621        trace!(cat, obj = obj, "meh");
1622        memdump!(cat, obj = obj, "meh");
1623    }
1624
1625    #[cfg(feature = "log")]
1626    static LOGGER: Lazy<DebugCategoryLogger> = Lazy::new(|| {
1627        DebugCategoryLogger::new(DebugCategory::new(
1628            "Log_trait",
1629            crate::DebugColorFlags::empty(),
1630            Some("Using the Log trait"),
1631        ))
1632    });
1633
1634    #[test]
1635    #[cfg(feature = "log")]
1636    fn log_trait() {
1637        crate::init().unwrap();
1638
1639        log::set_logger(&(*LOGGER)).expect("Failed to set logger");
1640        log::set_max_level(log::LevelFilter::Trace);
1641        log::error!("meh");
1642        log::warn!("fish");
1643
1644        let (sender, receiver) = mpsc::channel();
1645        let sender = Arc::new(Mutex::new(sender));
1646        let handler = move |category: DebugCategory,
1647                            level: DebugLevel,
1648                            _file: &glib::GStr,
1649                            _function: &glib::GStr,
1650                            _line: u32,
1651                            _object: Option<&LoggedObject>,
1652                            message: &DebugMessage| {
1653            let cat = DebugCategory::get("Log_trait").unwrap();
1654
1655            if category != cat {
1656                // This test can run in parallel with other tests, including new_and_log above.
1657                // We cannot be certain we only see our own messages.
1658                return;
1659            }
1660
1661            assert_eq!(level, DebugLevel::Error);
1662            assert_eq!(message.get().unwrap().as_ref(), "meh");
1663            let _ = sender.lock().unwrap().send(());
1664        };
1665
1666        remove_default_log_function();
1667        add_log_function(handler);
1668
1669        let cat = LOGGER.0;
1670
1671        cat.set_threshold(crate::DebugLevel::Warning);
1672        log::error!("meh");
1673        receiver.recv().unwrap();
1674
1675        cat.set_threshold(crate::DebugLevel::Error);
1676        log::error!("meh");
1677        receiver.recv().unwrap();
1678
1679        cat.set_threshold(crate::DebugLevel::None);
1680        log::error!("fish");
1681        log::warn!("meh");
1682    }
1683
1684    #[test]
1685    fn log_handler() {
1686        crate::init().unwrap();
1687
1688        let cat = DebugCategory::new(
1689            "test-cat-log",
1690            crate::DebugColorFlags::empty(),
1691            Some("some debug category"),
1692        );
1693        cat.set_threshold(DebugLevel::Info);
1694        let obj = crate::Bin::with_name("meh");
1695
1696        let (sender, receiver) = mpsc::channel();
1697
1698        let sender = Arc::new(Mutex::new(sender));
1699
1700        let handler = move |category: DebugCategory,
1701                            level: DebugLevel,
1702                            _file: &glib::GStr,
1703                            _function: &glib::GStr,
1704                            _line: u32,
1705                            _object: Option<&LoggedObject>,
1706                            message: &DebugMessage| {
1707            let cat = DebugCategory::get("test-cat-log").unwrap();
1708
1709            if category != cat {
1710                // This test can run in parallel with other tests, including new_and_log above.
1711                // We cannot be certain we only see our own messages.
1712                return;
1713            }
1714
1715            assert_eq!(level, DebugLevel::Info);
1716            assert_eq!(message.get().unwrap().as_ref(), "meh");
1717            let _ = sender.lock().unwrap().send(());
1718        };
1719
1720        remove_default_log_function();
1721        let log_fn = add_log_function(handler);
1722        info!(cat, obj = &obj, "meh");
1723
1724        receiver.recv().unwrap();
1725
1726        remove_log_function(log_fn);
1727
1728        info!(cat, obj = &obj, "meh2");
1729        assert!(receiver.recv().is_err());
1730    }
1731
1732    #[test]
1733    fn no_argument_evaluation() {
1734        crate::init().unwrap();
1735
1736        let cat = DebugCategory::new(
1737            "no_argument_evaluation",
1738            crate::DebugColorFlags::empty(),
1739            Some("No Argument Evaluation debug category"),
1740        );
1741
1742        let mut arg_evaluated = false;
1743        trace!(cat, "{}", {
1744            arg_evaluated = true;
1745            "trace log"
1746        });
1747
1748        assert!(!arg_evaluated);
1749    }
1750
1751    #[cfg(feature = "v1_22")]
1752    #[test]
1753    fn id_logging() {
1754        crate::init().unwrap();
1755
1756        let cat = DebugCategory::new(
1757            "log_with_id_test_category",
1758            crate::DebugColorFlags::empty(),
1759            Some("Blablabla"),
1760        );
1761
1762        cat.set_threshold(crate::DebugLevel::Trace);
1763
1764        trace!(cat, id = "123", "test");
1765        trace!(cat, id = glib::GString::from("123"), "test");
1766        trace!(cat, id = &glib::GString::from("123"), "test");
1767
1768        // Try with a formatted string too (which is a different code path in the bindings)
1769        let log_id = glib::GString::from("456");
1770        trace!(cat, id = &log_id, "{log_id:?}");
1771    }
1772}