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