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