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