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