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