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::{DebugLevel, ffi};
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 unsafe 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 unsafe 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 unsafe {
613 debug_assert!(!ptr.is_null());
614 DebugCategory(Some(ptr::NonNull::new_unchecked(ptr)))
615 }
616 }
617}
618
619impl FromGlibPtrFull<*mut ffi::GstDebugCategory> for DebugCategory {
620 #[inline]
621 unsafe fn from_glib_full(ptr: *mut ffi::GstDebugCategory) -> Self {
622 unsafe {
623 debug_assert!(!ptr.is_null());
624 DebugCategory(Some(ptr::NonNull::new_unchecked(ptr)))
625 }
626 }
627}
628
629pub static CAT_RUST: LazyLock<DebugCategory> = LazyLock::new(|| {
630 DebugCategory::new(
631 "GST_RUST",
632 crate::DebugColorFlags::UNDERLINE,
633 Some("GStreamer's Rust binding core"),
634 )
635});
636
637macro_rules! declare_debug_category_from_name(
638 ($cat:ident, $cat_name:expr) => (
639 pub static $cat: LazyLock<DebugCategory> = LazyLock::new(|| DebugCategory::get($cat_name)
640 .expect(&format!("Unable to find `DebugCategory` with name {}", $cat_name)));
641 );
642);
643
644declare_debug_category_from_name!(CAT_DEFAULT, "default");
645declare_debug_category_from_name!(CAT_GST_INIT, "GST_INIT");
646declare_debug_category_from_name!(CAT_MEMORY, "GST_MEMORY");
647declare_debug_category_from_name!(CAT_PARENTAGE, "GST_PARENTAGE");
648declare_debug_category_from_name!(CAT_STATES, "GST_STATES");
649declare_debug_category_from_name!(CAT_SCHEDULING, "GST_SCHEDULING");
650declare_debug_category_from_name!(CAT_BUFFER, "GST_BUFFER");
651declare_debug_category_from_name!(CAT_BUFFER_LIST, "GST_BUFFER_LIST");
652declare_debug_category_from_name!(CAT_BUS, "GST_BUS");
653declare_debug_category_from_name!(CAT_CAPS, "GST_CAPS");
654declare_debug_category_from_name!(CAT_CLOCK, "GST_CLOCK");
655declare_debug_category_from_name!(CAT_ELEMENT_PADS, "GST_ELEMENT_PADS");
656declare_debug_category_from_name!(CAT_PADS, "GST_PADS");
657declare_debug_category_from_name!(CAT_PERFORMANCE, "GST_PERFORMANCE");
658declare_debug_category_from_name!(CAT_PIPELINE, "GST_PIPELINE");
659declare_debug_category_from_name!(CAT_PLUGIN_LOADING, "GST_PLUGIN_LOADING");
660declare_debug_category_from_name!(CAT_PLUGIN_INFO, "GST_PLUGIN_INFO");
661declare_debug_category_from_name!(CAT_PROPERTIES, "GST_PROPERTIES");
662declare_debug_category_from_name!(CAT_NEGOTIATION, "GST_NEGOTIATION");
663declare_debug_category_from_name!(CAT_REFCOUNTING, "GST_REFCOUNTING");
664declare_debug_category_from_name!(CAT_ERROR_SYSTEM, "GST_ERROR_SYSTEM");
665declare_debug_category_from_name!(CAT_EVENT, "GST_EVENT");
666declare_debug_category_from_name!(CAT_MESSAGE, "GST_MESSAGE");
667declare_debug_category_from_name!(CAT_PARAMS, "GST_PARAMS");
668declare_debug_category_from_name!(CAT_CALL_TRACE, "GST_CALL_TRACE");
669declare_debug_category_from_name!(CAT_SIGNAL, "GST_SIGNAL");
670declare_debug_category_from_name!(CAT_PROBE, "GST_PROBE");
671declare_debug_category_from_name!(CAT_REGISTRY, "GST_REGISTRY");
672declare_debug_category_from_name!(CAT_QOS, "GST_QOS");
673declare_debug_category_from_name!(CAT_META, "GST_META");
674declare_debug_category_from_name!(CAT_LOCKING, "GST_LOCKING");
675declare_debug_category_from_name!(CAT_CONTEXT, "GST_CONTEXT");
676
677pub trait DebugLogger {
678 fn above_threshold(&self, level: DebugLevel) -> bool;
679
680 fn log_unfiltered(
681 &self,
682 obj: Option<&impl IsA<glib::Object>>,
683 level: DebugLevel,
684 file: &glib::GStr,
685 function: &str,
686 line: u32,
687 args: fmt::Arguments,
688 );
689
690 fn log_literal_unfiltered(
691 &self,
692 obj: Option<&impl IsA<glib::Object>>,
693 level: DebugLevel,
694 file: &glib::GStr,
695 function: &str,
696 line: u32,
697 msg: &glib::GStr,
698 );
699
700 #[cfg(feature = "v1_22")]
701 fn log_id_unfiltered(
702 &self,
703 id: impl AsRef<glib::GStr>,
704 level: DebugLevel,
705 file: &glib::GStr,
706 function: &str,
707 line: u32,
708 args: fmt::Arguments,
709 );
710
711 #[cfg(feature = "v1_22")]
712 fn log_id_literal_unfiltered(
713 &self,
714 id: impl AsRef<glib::GStr>,
715 level: DebugLevel,
716 file: &glib::GStr,
717 function: &str,
718 line: u32,
719 msg: &glib::GStr,
720 );
721}
722
723#[macro_export]
724macro_rules! error(
725 ($logger:expr, obj = $obj:expr, $($args:tt)*) => { {
726 $crate::log_with_level!($logger, $crate::DebugLevel::Error, obj = $obj, $($args)*)
727 }};
728 ($logger:expr, imp = $imp:expr, $($args:tt)*) => { {
729 $crate::log_with_level!($logger, $crate::DebugLevel::Error, imp = $imp, $($args)*)
730 }};
731 ($logger:expr, id = $id:expr, $($args:tt)*) => { {
732 $crate::log_with_level!($logger, $crate::DebugLevel::Error, id = $id, $($args)*)
733 }};
734 ($logger:expr, $($args:tt)*) => { {
735 $crate::log_with_level!($logger, $crate::DebugLevel::Error, $($args)*)
736 }};
737);
738
739#[macro_export]
740macro_rules! warning(
741 ($logger:expr, obj = $obj:expr, $($args:tt)*) => { {
742 $crate::log_with_level!($logger, $crate::DebugLevel::Warning, obj = $obj, $($args)*)
743 }};
744 ($logger:expr, imp = $imp:expr, $($args:tt)*) => { {
745 $crate::log_with_level!($logger, $crate::DebugLevel::Warning, imp = $imp, $($args)*)
746 }};
747 ($logger:expr, id = $id:expr, $($args:tt)*) => { {
748 $crate::log_with_level!($logger, $crate::DebugLevel::Warning, id = $id, $($args)*)
749 }};
750 ($logger:expr, $($args:tt)*) => { {
751 $crate::log_with_level!($logger, $crate::DebugLevel::Warning, $($args)*)
752 }};
753);
754
755#[macro_export]
756macro_rules! fixme(
757 ($logger:expr, obj = $obj:expr, $($args:tt)*) => { {
758 $crate::log_with_level!($logger, $crate::DebugLevel::Fixme, obj = $obj, $($args)*)
759 }};
760 ($logger:expr, imp = $imp:expr, $($args:tt)*) => { {
761 $crate::log_with_level!($logger, $crate::DebugLevel::Fixme, imp = $imp, $($args)*)
762 }};
763 ($logger:expr, id = $id:expr, $($args:tt)*) => { {
764 $crate::log_with_level!($logger, $crate::DebugLevel::Fixme, id = $id, $($args)*)
765 }};
766 ($logger:expr, $($args:tt)*) => { {
767 $crate::log_with_level!($logger, $crate::DebugLevel::Fixme, $($args)*)
768 }};
769);
770
771#[macro_export]
772macro_rules! info(
773 ($logger:expr, obj = $obj:expr, $($args:tt)*) => { {
774 $crate::log_with_level!($logger, $crate::DebugLevel::Info, obj = $obj, $($args)*)
775 }};
776 ($logger:expr, imp = $imp:expr, $($args:tt)*) => { {
777 $crate::log_with_level!($logger, $crate::DebugLevel::Info, imp = $imp, $($args)*)
778 }};
779 ($logger:expr, id = $id:expr, $($args:tt)*) => { {
780 $crate::log_with_level!($logger, $crate::DebugLevel::Info, id = $id, $($args)*)
781 }};
782 ($logger:expr, $($args:tt)*) => { {
783 $crate::log_with_level!($logger, $crate::DebugLevel::Info, $($args)*)
784 }};
785);
786
787#[macro_export]
788macro_rules! debug(
789 ($logger:expr, obj = $obj:expr, $($args:tt)*) => { {
790 $crate::log_with_level!($logger, $crate::DebugLevel::Debug, obj = $obj, $($args)*)
791 }};
792 ($logger:expr, imp = $imp:expr, $($args:tt)*) => { {
793 $crate::log_with_level!($logger, $crate::DebugLevel::Debug, imp = $imp, $($args)*)
794 }};
795 ($logger:expr, id = $id:expr, $($args:tt)*) => { {
796 $crate::log_with_level!($logger, $crate::DebugLevel::Debug, id = $id, $($args)*)
797 }};
798 ($logger:expr, $($args:tt)*) => { {
799 $crate::log_with_level!($logger, $crate::DebugLevel::Debug, $($args)*)
800 }};
801);
802
803#[macro_export]
804macro_rules! log(
805 ($logger:expr, obj = $obj:expr, $($args:tt)*) => { {
806 $crate::log_with_level!($logger, $crate::DebugLevel::Log, obj = $obj, $($args)*)
807 }};
808 ($logger:expr, imp = $imp:expr, $($args:tt)*) => { {
809 $crate::log_with_level!($logger, $crate::DebugLevel::Log, imp = $imp, $($args)*)
810 }};
811 ($logger:expr, id = $id:expr, $($args:tt)*) => { {
812 $crate::log_with_level!($logger, $crate::DebugLevel::Log, id = $id, $($args)*)
813 }};
814 ($logger:expr, $($args:tt)*) => { {
815 $crate::log_with_level!($logger, $crate::DebugLevel::Log, $($args)*)
816 }};
817);
818
819#[macro_export]
820macro_rules! trace(
821 ($logger:expr, obj = $obj:expr, $($args:tt)*) => { {
822 $crate::log_with_level!($logger, $crate::DebugLevel::Trace, obj = $obj, $($args)*)
823 }};
824 ($logger:expr, imp = $imp:expr, $($args:tt)*) => { {
825 $crate::log_with_level!($logger, $crate::DebugLevel::Trace, imp = $imp, $($args)*)
826 }};
827 ($logger:expr, id = $id:expr, $($args:tt)*) => { {
828 $crate::log_with_level!($logger, $crate::DebugLevel::Trace, id = $id, $($args)*)
829 }};
830 ($logger:expr, $($args:tt)*) => { {
831 $crate::log_with_level!($logger, $crate::DebugLevel::Trace, $($args)*)
832 }};
833);
834
835#[macro_export]
836macro_rules! memdump(
837 ($logger:expr, obj = $obj:expr, $($args:tt)*) => { {
838 $crate::log_with_level!($logger, $crate::DebugLevel::Memdump, obj = $obj, $($args)*)
839 }};
840 ($logger:expr, imp = $imp:expr, $($args:tt)*) => { {
841 $crate::log_with_level!($logger, $crate::DebugLevel::Memdump, imp = $imp, $($args)*)
842 }};
843 ($logger:expr, id = $id:expr, $($args:tt)*) => { {
844 $crate::log_with_level!($logger, $crate::DebugLevel::Memdump, id = $id, $($args)*)
845 }};
846 ($logger:expr, $($args:tt)*) => { {
847 $crate::log_with_level!($logger, $crate::DebugLevel::Memdump, $($args)*)
848 }};
849);
850
851#[macro_export]
852macro_rules! log_with_level(
853 ($logger:expr, $level:expr, obj = $obj:expr, $msg:literal) => { {
854 #[allow(unused_imports)]
855 use $crate::log::DebugLogger;
856 let logger = &$logger;
857
858 #[allow(unused_unsafe)]
861 #[allow(clippy::redundant_closure_call)]
862 if logger.above_threshold($level) {
863 use $crate::glib::prelude::Cast;
864
865 let obj = &$obj;
869 let obj = unsafe { obj.unsafe_cast_ref::<$crate::glib::Object>() };
870 let function_name = $crate::glib::function_name!();
871
872 (|args: std::fmt::Arguments| {
876 if args.as_str().is_some() {
877 logger.log_literal_unfiltered(
878 Some(obj),
879 $level,
880 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
881 function_name,
882 line!(),
883 $crate::glib::gstr!($msg),
884 )
885 } else {
886 logger.log_unfiltered(
887 Some(obj),
888 $level,
889 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
890 function_name,
891 line!(),
892 args,
893 )
894 }
895 })(format_args!($msg))
896 }
897 }};
898 ($logger:expr, $level:expr, obj = $obj:expr, $($args:tt)*) => { {
899 #[allow(unused_imports)]
900 use $crate::log::DebugLogger;
901 let logger = &$logger;
902
903 #[allow(unused_unsafe)]
906 if logger.above_threshold($level) {
907 use $crate::glib::prelude::Cast;
908
909 let obj = &$obj;
913 let obj = unsafe { obj.unsafe_cast_ref::<$crate::glib::Object>() };
914 logger.log_unfiltered(
915 Some(obj),
916 $level,
917 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
918 $crate::glib::function_name!(),
919 line!(),
920 format_args!($($args)*),
921 )
922 }
923 }};
924 ($logger:expr, $level:expr, imp = $imp:expr, $msg:literal) => { {
925 #[allow(unused_imports)]
926 use $crate::log::DebugLogger;
927 let logger = &$logger;
928
929 #[allow(unused_unsafe)]
932 #[allow(clippy::redundant_closure_call)]
933 if logger.above_threshold($level) {
934 use $crate::glib::prelude::Cast;
935
936 let obj = $imp.obj();
940 let obj = unsafe { obj.unsafe_cast_ref::<$crate::glib::Object>() };
941 let function_name = $crate::glib::function_name!();
942
943 (|args: std::fmt::Arguments| {
947 if args.as_str().is_some() {
948 logger.log_literal_unfiltered(
949 Some(obj),
950 $level,
951 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
952 function_name,
953 line!(),
954 $crate::glib::gstr!($msg),
955 )
956 } else {
957 logger.log_unfiltered(
958 Some(obj),
959 $level,
960 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
961 function_name,
962 line!(),
963 args,
964 )
965 }
966 })(format_args!($msg))
967 }
968 }};
969 ($logger:expr, $level:expr, imp = $imp:expr, $($args:tt)*) => { {
970 #[allow(unused_imports)]
971 use $crate::log::DebugLogger;
972 let logger = &$logger;
973
974 #[allow(unused_unsafe)]
977 if logger.above_threshold($level) {
978 use $crate::glib::prelude::Cast;
979
980 let obj = $imp.obj();
984 let obj = unsafe { obj.unsafe_cast_ref::<$crate::glib::Object>() };
985 logger.log_unfiltered(
986 Some(obj),
987 $level,
988 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
989 $crate::glib::function_name!(),
990 line!(),
991 format_args!($($args)*),
992 )
993 }
994 }};
995 ($logger:expr, $level:expr, id = $id:literal, $msg:literal) => { {
996 #[allow(unused_imports)]
997 use $crate::log::DebugLogger;
998 let logger = &$logger;
999
1000 #[allow(unused_unsafe)]
1003 #[allow(clippy::redundant_closure_call)]
1004 if logger.above_threshold($level) {
1005 let function_name = $crate::glib::function_name!();
1009
1010 (|args: std::fmt::Arguments| {
1014 if args.as_str().is_some() {
1015 logger.log_id_literal_unfiltered(
1016 $crate::glib::gstr!($id),
1017 $level,
1018 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1019 function_name,
1020 line!(),
1021 $crate::glib::gstr!($msg),
1022 )
1023 } else {
1024 logger.log_id_unfiltered(
1025 $crate::glib::gstr!($id),
1026 $level,
1027 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1028 function_name,
1029 line!(),
1030 args,
1031 )
1032 }
1033 })(format_args!($msg))
1034 }
1035 }};
1036 ($logger:expr, $level:expr, id = $id:literal, $($args:tt)*) => { {
1037 #[allow(unused_imports)]
1038 use $crate::log::DebugLogger;
1039 let logger = &$logger;
1040
1041 #[allow(unused_unsafe)]
1044 if logger.above_threshold($level) {
1045 logger.log_id_unfiltered(
1049 $crate::glib::gstr!($id),
1050 $level,
1051 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1052 $crate::glib::function_name!(),
1053 line!(),
1054 format_args!($($args)*),
1055 )
1056 }
1057 }};
1058 ($logger:expr, $level:expr, id = $id:expr, $msg:literal) => { {
1059 #[allow(unused_imports)]
1060 use $crate::log::DebugLogger;
1061 let logger = &$logger;
1062
1063 #[allow(unused_unsafe)]
1066 #[allow(clippy::redundant_closure_call)]
1067 if logger.above_threshold($level) {
1068 let function_name = $crate::glib::function_name!();
1072
1073 (|args: std::fmt::Arguments| {
1077 if args.as_str().is_some() {
1078 logger.log_id_literal_unfiltered(
1079 $id,
1080 $level,
1081 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1082 function_name,
1083 line!(),
1084 $crate::glib::gstr!($msg),
1085 )
1086 } else {
1087 logger.log_id_unfiltered(
1088 $id,
1089 $level,
1090 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1091 function_name,
1092 line!(),
1093 args,
1094 )
1095 }
1096 })(format_args!($msg))
1097 }
1098 }};
1099 ($logger:expr, $level:expr, id = $id:expr, $($args:tt)*) => { {
1100 #[allow(unused_imports)]
1101 use $crate::log::DebugLogger;
1102 let logger = &$logger;
1103
1104 #[allow(unused_unsafe)]
1107 if logger.above_threshold($level) {
1108 logger.log_id_unfiltered(
1112 $id,
1113 $level,
1114 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1115 $crate::glib::function_name!(),
1116 line!(),
1117 format_args!($($args)*),
1118 )
1119 }
1120 }};
1121 ($logger:expr, $level:expr, $msg:literal) => { {
1122 #[allow(unused_imports)]
1123 use $crate::log::DebugLogger;
1124 let logger = &$logger;
1125
1126 #[allow(unused_unsafe)]
1129 #[allow(clippy::redundant_closure_call)]
1130 if logger.above_threshold($level) {
1131 let function_name = $crate::glib::function_name!();
1135
1136 (|args: std::fmt::Arguments| {
1140 if args.as_str().is_some() {
1141 logger.log_literal_unfiltered(
1142 None as Option<&$crate::glib::Object>,
1143 $level,
1144 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1145 function_name,
1146 line!(),
1147 $crate::glib::gstr!($msg),
1148 )
1149 } else {
1150 logger.log_unfiltered(
1151 None as Option<&$crate::glib::Object>,
1152 $level,
1153 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1154 function_name,
1155 line!(),
1156 args,
1157 )
1158 }
1159 })(format_args!($msg))
1160 }
1161 }};
1162 ($logger:expr, $level:expr, $($args:tt)*) => { {
1163 #[allow(unused_imports)]
1164 use $crate::log::DebugLogger;
1165 let logger = &$logger;
1166
1167 #[allow(unused_unsafe)]
1170 if logger.above_threshold($level) {
1171 logger.log_unfiltered(
1175 None as Option<&$crate::glib::Object>,
1176 $level,
1177 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1178 $crate::glib::function_name!(),
1179 line!(),
1180 format_args!($($args)*),
1181 )
1182 }
1183 }};
1184);
1185
1186#[cfg(feature = "log")]
1187#[cfg_attr(docsrs, doc(cfg(feature = "log")))]
1188#[derive(Debug)]
1189pub struct DebugCategoryLogger(DebugCategory);
1190
1191#[cfg(feature = "log")]
1192#[cfg_attr(docsrs, doc(cfg(feature = "log")))]
1193impl DebugCategoryLogger {
1194 pub fn new(cat: DebugCategory) -> Self {
1195 skip_assert_initialized!();
1196 Self(cat)
1197 }
1198
1199 fn to_level(level: log::Level) -> crate::DebugLevel {
1200 skip_assert_initialized!();
1201 match level {
1202 log::Level::Error => DebugLevel::Error,
1203 log::Level::Warn => DebugLevel::Warning,
1204 log::Level::Info => DebugLevel::Info,
1205 log::Level::Debug => DebugLevel::Debug,
1206 log::Level::Trace => DebugLevel::Trace,
1207 }
1208 }
1209}
1210
1211#[cfg(feature = "log")]
1212#[cfg_attr(docsrs, doc(cfg(feature = "log")))]
1213impl log::Log for DebugCategoryLogger {
1214 fn enabled(&self, metadata: &log::Metadata) -> bool {
1215 self.0.above_threshold(Self::to_level(metadata.level()))
1216 }
1217
1218 fn log(&self, record: &log::Record) {
1219 if !self.enabled(record.metadata()) {
1220 return;
1221 }
1222 record.file().unwrap_or("").run_with_gstr(|file| {
1223 self.0.log(
1224 None::<&glib::Object>,
1225 Self::to_level(record.level()),
1226 file,
1227 record.module_path().unwrap_or(""),
1228 record.line().unwrap_or(0),
1229 *record.args(),
1230 );
1231 });
1232 }
1233
1234 fn flush(&self) {}
1235}
1236
1237unsafe extern "C" fn log_handler<T>(
1238 category: *mut ffi::GstDebugCategory,
1239 level: ffi::GstDebugLevel,
1240 file: *const c_char,
1241 function: *const c_char,
1242 line: i32,
1243 object: *mut glib::gobject_ffi::GObject,
1244 message: *mut ffi::GstDebugMessage,
1245 user_data: gpointer,
1246) where
1247 T: Fn(
1248 DebugCategory,
1249 DebugLevel,
1250 &glib::GStr,
1251 &glib::GStr,
1252 u32,
1253 Option<&LoggedObject>,
1254 &DebugMessage,
1255 ) + Send
1256 + Sync
1257 + 'static,
1258{
1259 unsafe {
1260 if category.is_null() {
1261 return;
1262 }
1263 let category = DebugCategory(Some(ptr::NonNull::new_unchecked(category)));
1264 let level = from_glib(level);
1265 let file = glib::GStr::from_ptr(file);
1266 let function = glib::GStr::from_ptr(function);
1267 let line = line as u32;
1268 let object = ptr::NonNull::new(object).map(LoggedObject);
1269 let message = DebugMessage(ptr::NonNull::new_unchecked(message));
1270 let handler = &*(user_data as *mut T);
1271 (handler)(
1272 category,
1273 level,
1274 file,
1275 function,
1276 line,
1277 object.as_ref(),
1278 &message,
1279 );
1280 }
1281}
1282
1283unsafe extern "C" fn log_handler_data_free<T>(data: gpointer) {
1284 unsafe {
1285 let data = Box::from_raw(data as *mut T);
1286 drop(data);
1287 }
1288}
1289
1290#[derive(Debug)]
1291pub struct DebugLogFunction(ptr::NonNull<std::os::raw::c_void>);
1292
1293unsafe impl Send for DebugLogFunction {}
1297unsafe impl Sync for DebugLogFunction {}
1298
1299#[derive(Debug)]
1300#[doc(alias = "GObject")]
1301pub struct LoggedObject(ptr::NonNull<glib::gobject_ffi::GObject>);
1302
1303impl LoggedObject {
1304 #[inline]
1305 pub fn as_ptr(&self) -> *mut glib::gobject_ffi::GObject {
1306 self.0.as_ptr()
1307 }
1308}
1309
1310impl fmt::Display for LoggedObject {
1311 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1312 unsafe {
1313 let ptr = self.0.as_ptr();
1314 let g_type_instance = &mut (*ptr).g_type_instance;
1315 if glib::gobject_ffi::g_type_check_instance_is_fundamentally_a(
1316 g_type_instance,
1317 glib::gobject_ffi::g_object_get_type(),
1318 ) != glib::ffi::GFALSE
1319 {
1320 let type_ = (*g_type_instance.g_class).g_type;
1321
1322 if glib::gobject_ffi::g_type_is_a(type_, ffi::gst_pad_get_type())
1323 != glib::ffi::GFALSE
1324 {
1325 let name_ptr = (*(ptr as *mut ffi::GstObject)).name;
1326 let name = if name_ptr.is_null() {
1327 "<null>"
1328 } else {
1329 CStr::from_ptr(name_ptr)
1330 .to_str()
1331 .unwrap_or("<invalid name>")
1332 };
1333
1334 let parent_ptr = (*(ptr as *mut ffi::GstObject)).parent;
1335 let parent_name = if parent_ptr.is_null() {
1336 "<null>"
1337 } else {
1338 let name_ptr = (*(parent_ptr)).name;
1339 if name_ptr.is_null() {
1340 "<null>"
1341 } else {
1342 CStr::from_ptr(name_ptr)
1343 .to_str()
1344 .unwrap_or("<invalid name>")
1345 }
1346 };
1347
1348 write!(f, "{parent_name}:{name}")
1349 } else if glib::gobject_ffi::g_type_is_a(type_, ffi::gst_object_get_type())
1350 != glib::ffi::GFALSE
1351 {
1352 let name_ptr = (*(ptr as *mut ffi::GstObject)).name;
1353 let name = if name_ptr.is_null() {
1354 "<null>"
1355 } else {
1356 CStr::from_ptr(name_ptr)
1357 .to_str()
1358 .unwrap_or("<invalid name>")
1359 };
1360 write!(f, "{name}")
1361 } else {
1362 let type_name = CStr::from_ptr(glib::gobject_ffi::g_type_name(type_));
1363 write!(
1364 f,
1365 "{}:{:?}",
1366 type_name.to_str().unwrap_or("<invalid type>"),
1367 ptr
1368 )
1369 }
1370 } else {
1371 write!(f, "{ptr:?}")
1372 }
1373 }
1374 }
1375}
1376
1377#[doc(alias = "gst_debug_add_log_function")]
1378pub fn add_log_function<T>(function: T) -> DebugLogFunction
1379where
1380 T: Fn(
1381 DebugCategory,
1382 DebugLevel,
1383 &glib::GStr,
1384 &glib::GStr,
1385 u32,
1386 Option<&LoggedObject>,
1387 &DebugMessage,
1388 ) + Send
1389 + Sync
1390 + 'static,
1391{
1392 skip_assert_initialized!();
1393 unsafe {
1394 let user_data = Box::new(function);
1395 let user_data_ptr = Box::into_raw(user_data) as gpointer;
1396 ffi::gst_debug_add_log_function(
1397 Some(log_handler::<T>),
1398 user_data_ptr,
1399 Some(log_handler_data_free::<T>),
1400 );
1401 DebugLogFunction(ptr::NonNull::new_unchecked(user_data_ptr))
1402 }
1403}
1404
1405pub fn remove_default_log_function() {
1406 skip_assert_initialized!();
1407 unsafe {
1408 ffi::gst_debug_remove_log_function(None);
1409 }
1410}
1411
1412#[doc(alias = "gst_debug_remove_log_function_by_data")]
1413pub fn remove_log_function(log_fn: DebugLogFunction) {
1414 skip_assert_initialized!();
1415 unsafe {
1416 ffi::gst_debug_remove_log_function_by_data(log_fn.0.as_ptr());
1417 }
1418}
1419
1420#[cfg(test)]
1421mod tests {
1422 use std::sync::{Arc, Mutex, mpsc};
1423
1424 use super::*;
1425
1426 #[test]
1427 #[doc(alias = "get_existing")]
1428 fn existing() {
1429 crate::init().unwrap();
1430
1431 let perf_cat = DebugCategory::get("GST_PERFORMANCE")
1432 .expect("Unable to find `DebugCategory` with name \"GST_PERFORMANCE\"");
1433 assert_eq!(perf_cat.name(), CAT_PERFORMANCE.name());
1434 }
1435
1436 #[test]
1437 fn all() {
1438 crate::init().unwrap();
1439
1440 assert!(
1441 DebugCategory::all_categories()
1442 .iter()
1443 .any(|c| c.name() == "GST_PERFORMANCE")
1444 );
1445 }
1446
1447 #[test]
1448 fn new_and_log() {
1449 crate::init().unwrap();
1450
1451 let cat = DebugCategory::new(
1452 "test-cat",
1453 crate::DebugColorFlags::empty(),
1454 Some("some debug category"),
1455 );
1456
1457 error!(cat, "meh");
1458 warning!(cat, "meh");
1459 fixme!(cat, "meh");
1460 info!(cat, "meh");
1461 debug!(cat, "meh");
1462 log!(cat, "meh");
1463 trace!(cat, "meh");
1464 memdump!(cat, "meh");
1465
1466 let obj = crate::Bin::with_name("meh");
1467
1468 error!(cat, obj = &obj, "meh");
1469 warning!(cat, obj = &obj, "meh");
1470 fixme!(cat, obj = &obj, "meh");
1471 info!(cat, obj = &obj, "meh");
1472 debug!(cat, obj = &obj, "meh");
1473 log!(cat, obj = &obj, "meh");
1474 trace!(cat, obj = &obj, "meh");
1475 memdump!(cat, obj = &obj, "meh");
1476
1477 error!(cat, obj = obj, "meh");
1478 warning!(cat, obj = obj, "meh");
1479 fixme!(cat, obj = obj, "meh");
1480 info!(cat, obj = obj, "meh");
1481 debug!(cat, obj = obj, "meh");
1482 log!(cat, obj = obj, "meh");
1483 trace!(cat, obj = obj, "meh");
1484 memdump!(cat, obj = obj, "meh");
1485 }
1486
1487 #[cfg(feature = "log")]
1488 static LOGGER: LazyLock<DebugCategoryLogger> = LazyLock::new(|| {
1489 DebugCategoryLogger::new(DebugCategory::new(
1490 "Log_trait",
1491 crate::DebugColorFlags::empty(),
1492 Some("Using the Log trait"),
1493 ))
1494 });
1495
1496 #[test]
1497 #[cfg(feature = "log")]
1498 fn log_trait() {
1499 crate::init().unwrap();
1500
1501 log::set_logger(&(*LOGGER)).expect("Failed to set logger");
1502 log::set_max_level(log::LevelFilter::Trace);
1503 log::error!("meh");
1504 log::warn!("fish");
1505
1506 let (sender, receiver) = mpsc::channel();
1507 let sender = Arc::new(Mutex::new(sender));
1508 let handler = move |category: DebugCategory,
1509 level: DebugLevel,
1510 _file: &glib::GStr,
1511 _function: &glib::GStr,
1512 _line: u32,
1513 _object: Option<&LoggedObject>,
1514 message: &DebugMessage| {
1515 let cat = DebugCategory::get("Log_trait").unwrap();
1516
1517 if category != cat {
1518 return;
1521 }
1522
1523 assert_eq!(level, DebugLevel::Error);
1524 assert_eq!(message.get().unwrap().as_ref(), "meh");
1525 let _ = sender.lock().unwrap().send(());
1526 };
1527
1528 remove_default_log_function();
1529 add_log_function(handler);
1530
1531 let cat = LOGGER.0;
1532
1533 cat.set_threshold(crate::DebugLevel::Warning);
1534 log::error!("meh");
1535 receiver.recv().unwrap();
1536
1537 cat.set_threshold(crate::DebugLevel::Error);
1538 log::error!("meh");
1539 receiver.recv().unwrap();
1540
1541 cat.set_threshold(crate::DebugLevel::None);
1542 log::error!("fish");
1543 log::warn!("meh");
1544 }
1545
1546 #[test]
1547 fn log_handler() {
1548 crate::init().unwrap();
1549
1550 let cat = DebugCategory::new(
1551 "test-cat-log",
1552 crate::DebugColorFlags::empty(),
1553 Some("some debug category"),
1554 );
1555 cat.set_threshold(DebugLevel::Info);
1556 let obj = crate::Bin::with_name("meh");
1557
1558 let (sender, receiver) = mpsc::channel();
1559
1560 let sender = Arc::new(Mutex::new(sender));
1561
1562 let handler = move |category: DebugCategory,
1563 level: DebugLevel,
1564 _file: &glib::GStr,
1565 _function: &glib::GStr,
1566 _line: u32,
1567 _object: Option<&LoggedObject>,
1568 message: &DebugMessage| {
1569 let cat = DebugCategory::get("test-cat-log").unwrap();
1570
1571 if category != cat {
1572 return;
1575 }
1576
1577 assert_eq!(level, DebugLevel::Info);
1578 assert_eq!(message.get().unwrap().as_ref(), "meh");
1579 let _ = sender.lock().unwrap().send(());
1580 };
1581
1582 remove_default_log_function();
1583 let log_fn = add_log_function(handler);
1584 info!(cat, obj = &obj, "meh");
1585
1586 receiver.recv().unwrap();
1587
1588 remove_log_function(log_fn);
1589
1590 info!(cat, obj = &obj, "meh2");
1591 assert!(receiver.recv().is_err());
1592 }
1593
1594 #[test]
1595 fn no_argument_evaluation() {
1596 crate::init().unwrap();
1597
1598 let cat = DebugCategory::new(
1599 "no_argument_evaluation",
1600 crate::DebugColorFlags::empty(),
1601 Some("No Argument Evaluation debug category"),
1602 );
1603
1604 let mut arg_evaluated = false;
1605 trace!(cat, "{}", {
1606 arg_evaluated = true;
1607 "trace log"
1608 });
1609
1610 assert!(!arg_evaluated);
1611 }
1612
1613 #[cfg(feature = "v1_22")]
1614 #[test]
1615 fn id_logging() {
1616 crate::init().unwrap();
1617
1618 let cat = DebugCategory::new(
1619 "log_with_id_test_category",
1620 crate::DebugColorFlags::empty(),
1621 Some("Blablabla"),
1622 );
1623
1624 cat.set_threshold(crate::DebugLevel::Trace);
1625
1626 trace!(cat, id = "123", "test");
1627 trace!(cat, id = glib::GString::from("123"), "test");
1628 trace!(cat, id = &glib::GString::from("123"), "test");
1629
1630 let log_id = glib::GString::from("456");
1632 trace!(cat, id = &log_id, "{log_id:?}");
1633 }
1634}