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