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