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