glib/
log.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3#[cfg(unix)]
4use std::os::unix::io::{AsFd, AsRawFd};
5use std::{
6    boxed::Box as Box_,
7    sync::{Arc, Mutex, OnceLock},
8};
9
10use crate::{GStr, GString, LogWriterOutput, ffi, translate::*};
11
12#[derive(Debug)]
13pub struct LogHandlerId(u32);
14
15#[doc(hidden)]
16impl FromGlib<u32> for LogHandlerId {
17    #[inline]
18    unsafe fn from_glib(value: u32) -> Self {
19        Self(value)
20    }
21}
22
23#[doc(hidden)]
24impl IntoGlib for LogHandlerId {
25    type GlibType = u32;
26
27    #[inline]
28    fn into_glib(self) -> u32 {
29        self.0
30    }
31}
32
33#[derive(Copy, Clone, Debug, PartialEq, Eq)]
34pub enum LogLevel {
35    #[doc(alias = "G_LOG_LEVEL_ERROR")]
36    Error,
37    #[doc(alias = "G_LOG_LEVEL_CRITICAL")]
38    Critical,
39    #[doc(alias = "G_LOG_LEVEL_WARNING")]
40    Warning,
41    #[doc(alias = "G_LOG_LEVEL_MESSAGE")]
42    Message,
43    #[doc(alias = "G_LOG_LEVEL_INFO")]
44    Info,
45    #[doc(alias = "G_LOG_LEVEL_DEBUG")]
46    Debug,
47}
48
49#[doc(hidden)]
50impl IntoGlib for LogLevel {
51    type GlibType = u32;
52
53    #[inline]
54    fn into_glib(self) -> u32 {
55        match self {
56            Self::Error => ffi::G_LOG_LEVEL_ERROR,
57            Self::Critical => ffi::G_LOG_LEVEL_CRITICAL,
58            Self::Warning => ffi::G_LOG_LEVEL_WARNING,
59            Self::Message => ffi::G_LOG_LEVEL_MESSAGE,
60            Self::Info => ffi::G_LOG_LEVEL_INFO,
61            Self::Debug => ffi::G_LOG_LEVEL_DEBUG,
62        }
63    }
64}
65
66#[doc(hidden)]
67impl FromGlib<u32> for LogLevel {
68    #[inline]
69    unsafe fn from_glib(value: u32) -> Self {
70        if value & ffi::G_LOG_LEVEL_ERROR != 0 {
71            Self::Error
72        } else if value & ffi::G_LOG_LEVEL_CRITICAL != 0 {
73            Self::Critical
74        } else if value & ffi::G_LOG_LEVEL_WARNING != 0 {
75            Self::Warning
76        } else if value & ffi::G_LOG_LEVEL_MESSAGE != 0 {
77            Self::Message
78        } else if value & ffi::G_LOG_LEVEL_INFO != 0 {
79            Self::Info
80        } else if value & ffi::G_LOG_LEVEL_DEBUG != 0 {
81            Self::Debug
82        } else {
83            panic!("Unknown log level: {value}")
84        }
85    }
86}
87
88impl LogLevel {
89    #[doc(hidden)]
90    pub fn priority(&self) -> &'static str {
91        match self {
92            Self::Error => "3",
93            Self::Critical => "4",
94            Self::Warning => "4",
95            Self::Message => "5",
96            Self::Info => "6",
97            Self::Debug => "7",
98        }
99    }
100}
101
102bitflags::bitflags! {
103    #[doc(alias = "GLogLevelFlags")]
104    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
105    pub struct LogLevels: u32 {
106        #[doc(alias = "G_LOG_LEVEL_ERROR")]
107        const LEVEL_ERROR = ffi::G_LOG_LEVEL_ERROR;
108        #[doc(alias = "G_LOG_LEVEL_CRITICAL")]
109        const LEVEL_CRITICAL = ffi::G_LOG_LEVEL_CRITICAL;
110        #[doc(alias = "G_LOG_LEVEL_WARNING")]
111        const LEVEL_WARNING = ffi::G_LOG_LEVEL_WARNING;
112        #[doc(alias = "G_LOG_LEVEL_MESSAGE")]
113        const LEVEL_MESSAGE = ffi::G_LOG_LEVEL_MESSAGE;
114        #[doc(alias = "G_LOG_LEVEL_INFO")]
115        const LEVEL_INFO = ffi::G_LOG_LEVEL_INFO;
116        #[doc(alias = "G_LOG_LEVEL_DEBUG")]
117        const LEVEL_DEBUG = ffi::G_LOG_LEVEL_DEBUG;
118    }
119}
120
121#[doc(hidden)]
122impl IntoGlib for LogLevels {
123    type GlibType = ffi::GLogLevelFlags;
124
125    #[inline]
126    fn into_glib(self) -> ffi::GLogLevelFlags {
127        self.bits()
128    }
129}
130
131#[doc(hidden)]
132impl FromGlib<ffi::GLogLevelFlags> for LogLevels {
133    #[inline]
134    unsafe fn from_glib(value: ffi::GLogLevelFlags) -> Self {
135        Self::from_bits_truncate(value)
136    }
137}
138
139fn to_log_flags(fatal: bool, recursion: bool) -> u32 {
140    (if fatal { ffi::G_LOG_FLAG_FATAL } else { 0 })
141        | if recursion {
142            ffi::G_LOG_FLAG_RECURSION
143        } else {
144            0
145        }
146}
147
148#[doc(alias = "g_log_set_handler_full")]
149pub fn log_set_handler<P: Fn(Option<&str>, LogLevel, &str) + Send + Sync + 'static>(
150    log_domain: Option<&str>,
151    log_levels: LogLevels,
152    fatal: bool,
153    recursion: bool,
154    log_func: P,
155) -> LogHandlerId {
156    let log_func_data: Box_<P> = Box_::new(log_func);
157    unsafe extern "C" fn log_func_func<
158        P: Fn(Option<&str>, LogLevel, &str) + Send + Sync + 'static,
159    >(
160        log_domain: *const libc::c_char,
161        log_level: ffi::GLogLevelFlags,
162        message: *const libc::c_char,
163        user_data: ffi::gpointer,
164    ) {
165        unsafe {
166            let log_domain: Borrowed<Option<GString>> = from_glib_borrow(log_domain);
167            let message: Borrowed<GString> = from_glib_borrow(message);
168            let callback: &P = &*(user_data as *mut _);
169            (*callback)(
170                (*log_domain).as_ref().map(|s| s.as_str()),
171                from_glib(log_level),
172                message.as_str(),
173            );
174        }
175    }
176    let log_func = Some(log_func_func::<P> as _);
177    unsafe extern "C" fn destroy_func<
178        P: Fn(Option<&str>, LogLevel, &str) + Send + Sync + 'static,
179    >(
180        data: ffi::gpointer,
181    ) {
182        unsafe {
183            let _callback: Box_<P> = Box_::from_raw(data as *mut _);
184        }
185    }
186    let destroy_call4 = Some(destroy_func::<P> as _);
187    let super_callback0: Box_<P> = log_func_data;
188    unsafe {
189        from_glib(ffi::g_log_set_handler_full(
190            log_domain.to_glib_none().0,
191            log_levels.into_glib() | to_log_flags(fatal, recursion),
192            log_func,
193            Box_::into_raw(super_callback0) as *mut _,
194            destroy_call4,
195        ))
196    }
197}
198
199#[doc(alias = "g_log_remove_handler")]
200pub fn log_remove_handler(log_domain: Option<&str>, handler_id: LogHandlerId) {
201    unsafe {
202        ffi::g_log_remove_handler(log_domain.to_glib_none().0, handler_id.into_glib());
203    }
204}
205
206#[doc(alias = "g_log_set_always_fatal")]
207pub fn log_set_always_fatal(fatal_levels: LogLevels) -> LogLevels {
208    unsafe { from_glib(ffi::g_log_set_always_fatal(fatal_levels.into_glib())) }
209}
210
211#[doc(alias = "g_log_set_fatal_mask")]
212pub fn log_set_fatal_mask(log_domain: Option<&str>, fatal_levels: LogLevels) -> LogLevels {
213    unsafe {
214        from_glib(ffi::g_log_set_fatal_mask(
215            log_domain.to_glib_none().0,
216            fatal_levels.into_glib(),
217        ))
218    }
219}
220
221type PrintCallback = dyn Fn(&str) + Send + Sync + 'static;
222
223fn print_handler() -> &'static Mutex<Option<Arc<PrintCallback>>> {
224    static MUTEX: OnceLock<Mutex<Option<Arc<PrintCallback>>>> = OnceLock::new();
225    MUTEX.get_or_init(|| Mutex::new(None))
226}
227
228// rustdoc-stripper-ignore-next
229/// To set back the default print handler, use the [`unset_print_handler`] function.
230#[doc(alias = "g_set_print_handler")]
231pub fn set_print_handler<P: Fn(&str) + Send + Sync + 'static>(func: P) {
232    unsafe extern "C" fn func_func(string: *const libc::c_char) {
233        unsafe {
234            if let Some(callback) = print_handler()
235                .lock()
236                .expect("Failed to lock PRINT_HANDLER")
237                .as_ref()
238                .map(Arc::clone)
239            {
240                let string: Borrowed<GString> = from_glib_borrow(string);
241                (*callback)(string.as_str())
242            }
243        }
244    }
245    *print_handler()
246        .lock()
247        .expect("Failed to lock PRINT_HANDLER to change callback") = Some(Arc::new(func));
248    unsafe { ffi::g_set_print_handler(Some(func_func as _)) };
249}
250
251// rustdoc-stripper-ignore-next
252/// To set the default print handler, use the [`set_print_handler`] function.
253pub fn unset_print_handler() {
254    *print_handler()
255        .lock()
256        .expect("Failed to lock PRINT_HANDLER to remove callback") = None;
257    unsafe { ffi::g_set_print_handler(None) };
258}
259
260fn printerr_handler() -> &'static Mutex<Option<Arc<PrintCallback>>> {
261    static MUTEX: OnceLock<Mutex<Option<Arc<PrintCallback>>>> = OnceLock::new();
262    MUTEX.get_or_init(|| Mutex::new(None))
263}
264
265// rustdoc-stripper-ignore-next
266/// To set back the default print handler, use the [`unset_printerr_handler`] function.
267#[doc(alias = "g_set_printerr_handler")]
268pub fn set_printerr_handler<P: Fn(&str) + Send + Sync + 'static>(func: P) {
269    unsafe extern "C" fn func_func(string: *const libc::c_char) {
270        unsafe {
271            if let Some(callback) = printerr_handler()
272                .lock()
273                .expect("Failed to lock PRINTERR_HANDLER")
274                .as_ref()
275                .map(Arc::clone)
276            {
277                let string: Borrowed<GString> = from_glib_borrow(string);
278                (*callback)(string.as_str())
279            }
280        }
281    }
282    *printerr_handler()
283        .lock()
284        .expect("Failed to lock PRINTERR_HANDLER to change callback") = Some(Arc::new(func));
285    unsafe { ffi::g_set_printerr_handler(Some(func_func as _)) };
286}
287
288// rustdoc-stripper-ignore-next
289/// To set the default print handler, use the [`set_printerr_handler`] function.
290pub fn unset_printerr_handler() {
291    *printerr_handler()
292        .lock()
293        .expect("Failed to lock PRINTERR_HANDLER to remove callback") = None;
294    unsafe { ffi::g_set_printerr_handler(None) };
295}
296
297type LogCallback = dyn Fn(Option<&str>, LogLevel, &str) + Send + Sync + 'static;
298
299fn default_handler() -> &'static Mutex<Option<Arc<LogCallback>>> {
300    static MUTEX: OnceLock<Mutex<Option<Arc<LogCallback>>>> = OnceLock::new();
301    MUTEX.get_or_init(|| Mutex::new(None))
302}
303
304// rustdoc-stripper-ignore-next
305/// To set back the default print handler, use the [`log_unset_default_handler`] function.
306#[doc(alias = "g_log_set_default_handler")]
307pub fn log_set_default_handler<P: Fn(Option<&str>, LogLevel, &str) + Send + Sync + 'static>(
308    log_func: P,
309) {
310    unsafe extern "C" fn func_func(
311        log_domain: *const libc::c_char,
312        log_levels: ffi::GLogLevelFlags,
313        message: *const libc::c_char,
314        _user_data: ffi::gpointer,
315    ) {
316        unsafe {
317            if let Some(callback) = default_handler()
318                .lock()
319                .expect("Failed to lock DEFAULT_HANDLER")
320                .as_ref()
321                .map(Arc::clone)
322            {
323                let log_domain: Borrowed<Option<GString>> = from_glib_borrow(log_domain);
324                let message: Borrowed<GString> = from_glib_borrow(message);
325                (*callback)(
326                    (*log_domain).as_ref().map(|s| s.as_str()),
327                    from_glib(log_levels),
328                    message.as_str(),
329                );
330            }
331        }
332    }
333    *default_handler()
334        .lock()
335        .expect("Failed to lock DEFAULT_HANDLER to change callback") = Some(Arc::new(log_func));
336    unsafe { ffi::g_log_set_default_handler(Some(func_func as _), std::ptr::null_mut()) };
337}
338
339// rustdoc-stripper-ignore-next
340/// To set the default print handler, use the [`log_set_default_handler`] function.
341#[doc(alias = "g_log_set_default_handler")]
342pub fn log_unset_default_handler() {
343    *default_handler()
344        .lock()
345        .expect("Failed to lock DEFAULT_HANDLER to remove callback") = None;
346    unsafe {
347        ffi::g_log_set_default_handler(Some(ffi::g_log_default_handler), std::ptr::null_mut())
348    };
349}
350
351#[doc(alias = "g_log_default_handler")]
352pub fn log_default_handler(log_domain: Option<&str>, log_level: LogLevel, message: Option<&str>) {
353    unsafe {
354        ffi::g_log_default_handler(
355            log_domain.to_glib_none().0,
356            log_level.into_glib(),
357            message.to_glib_none().0,
358            std::ptr::null_mut(),
359        )
360    }
361}
362
363// rustdoc-stripper-ignore-next
364/// Structure representing a single field in a structured log entry.
365///
366/// See [`g_log_structured`][gls] for details. Log fields may contain UTF-8 strings, binary with
367/// embedded nul bytes, or arbitrary pointers.
368///
369/// [gls]: https://docs.gtk.org/glib/func.log_structured.html
370#[repr(transparent)]
371#[derive(Debug)]
372#[doc(alias = "GLogField")]
373pub struct LogField<'a>(ffi::GLogField, std::marker::PhantomData<&'a GStr>);
374
375impl<'a> LogField<'a> {
376    // rustdoc-stripper-ignore-next
377    /// Creates a field from a borrowed key and value.
378    pub fn new(key: &'a GStr, value: &[u8]) -> Self {
379        let (value, length) = if value.is_empty() {
380            // Use an empty C string to represent empty data, since length: 0 is reserved for user
381            // data fields.
382            (&[0u8] as &[u8], -1isize)
383        } else {
384            (value, value.len().try_into().unwrap())
385        };
386        Self(
387            ffi::GLogField {
388                key: key.as_ptr(),
389                value: value.as_ptr() as *const _,
390                length,
391            },
392            Default::default(),
393        )
394    }
395    // rustdoc-stripper-ignore-next
396    /// Creates a field with an empty value and `data` as a user data key. Fields created with this
397    /// function are ignored by the default log writer. These fields are used to pass custom data
398    /// into a writer function set with [`log_set_writer_func`], where it can be retrieved using
399    /// [`Self::user_data`].
400    ///
401    /// The passed `usize` can be used by the log writer as a key into a static data structure.
402    /// Thread locals are preferred since the log writer function will run in the same thread that
403    /// invoked [`log_structured_array`].
404    pub fn new_user_data(key: &'a GStr, data: usize) -> Self {
405        Self(
406            ffi::GLogField {
407                key: key.as_ptr(),
408                value: data as *const _,
409                length: 0,
410            },
411            Default::default(),
412        )
413    }
414    // rustdoc-stripper-ignore-next
415    /// Retrieves the field key.
416    pub fn key(&self) -> &str {
417        unsafe { std::ffi::CStr::from_ptr(self.0.key as *const _) }
418            .to_str()
419            .unwrap()
420    }
421    // rustdoc-stripper-ignore-next
422    /// Retrieves a byte array of the field value. Returns `None` if the field was created with
423    /// [`Self::new_user_data`].
424    pub fn value_bytes(&self) -> Option<&[u8]> {
425        match self.0.length {
426            0 => None,
427            n if n < 0 => {
428                Some(unsafe { std::ffi::CStr::from_ptr(self.0.value as *const _) }.to_bytes())
429            }
430            _ => Some(unsafe {
431                std::slice::from_raw_parts(self.0.value as *const u8, self.0.length as usize)
432            }),
433        }
434    }
435    // rustdoc-stripper-ignore-next
436    /// Retrieves a string of the field value, or `None` if the string is not valid UTF-8. Also
437    /// returns `None` if the field was created with [`Self::new_user_data`].
438    pub fn value_str(&self) -> Option<&str> {
439        std::str::from_utf8(self.value_bytes()?).ok()
440    }
441    // rustdoc-stripper-ignore-next
442    /// Retrieves the the user data value from a field created with [`Self::new_user_data`].
443    /// Returns `None` if the field was created with [`Self::new`].
444    pub fn user_data(&self) -> Option<usize> {
445        (self.0.length == 0).then_some(self.0.value as usize)
446    }
447}
448
449type WriterCallback = dyn Fn(LogLevel, &[LogField<'_>]) -> LogWriterOutput + Send + Sync + 'static;
450
451static WRITER_FUNC: OnceLock<Box<WriterCallback>> = OnceLock::new();
452
453#[doc(alias = "g_log_set_writer_func")]
454pub fn log_set_writer_func<
455    P: Fn(LogLevel, &[LogField<'_>]) -> LogWriterOutput + Send + Sync + 'static,
456>(
457    writer_func: P,
458) {
459    if WRITER_FUNC.set(Box::new(writer_func)).is_err() {
460        panic!("Writer func can only be set once");
461    }
462    unsafe extern "C" fn writer_trampoline(
463        log_level: ffi::GLogLevelFlags,
464        fields: *const ffi::GLogField,
465        n_fields: libc::size_t,
466        _user_data: ffi::gpointer,
467    ) -> ffi::GLogWriterOutput {
468        unsafe {
469            let writer_func = WRITER_FUNC.get().unwrap();
470            let fields = std::slice::from_raw_parts(fields as *const LogField<'_>, n_fields);
471            writer_func(from_glib(log_level), fields).into_glib()
472        }
473    }
474    unsafe {
475        ffi::g_log_set_writer_func(Some(writer_trampoline), std::ptr::null_mut(), None);
476    }
477}
478
479#[macro_export]
480#[doc(hidden)]
481macro_rules! g_log_inner {
482    ($log_domain:expr, $log_level:expr, $format:literal $(,$arg:expr)* $(,)?) => {{
483        let mut w = $crate::GStringBuilder::default();
484
485        // Can't really happen but better safe than sorry
486        if !std::fmt::Write::write_fmt(&mut w, std::format_args!($format, $($arg),*)).is_err() {
487            unsafe {
488                $crate::ffi::g_log(
489                    $crate::translate::ToGlibPtr::to_glib_none(&$log_domain).0,
490                    <$crate::LogLevel as $crate::translate::IntoGlib>::into_glib(
491                        $log_level
492                    ),
493                    b"%s\0".as_ptr() as *const _,
494                    $crate::translate::ToGlibPtr::<*const std::os::raw::c_char>::to_glib_none(
495                        &w.into_string()
496                    ).0,
497                );
498            }
499        }
500    }};
501}
502
503// rustdoc-stripper-ignore-next
504/// Macro used to log using GLib logging system. It uses [g_log].
505///
506/// [g_log]: https://docs.gtk.org/glib/func.log.html
507///
508/// Example:
509///
510/// ```no_run
511/// use glib::{LogLevel, g_log};
512///
513/// g_log!("test", LogLevel::Debug, "test");
514/// g_log!("test", LogLevel::Message, "test");
515/// // trailing commas work as well:
516/// g_log!("test", LogLevel::Message, "test",);
517///
518/// // You can also pass arguments like in format! or println!:
519/// let x = 12;
520/// g_log!("test", LogLevel::Error, "test: {}", x);
521/// g_log!("test", LogLevel::Critical, "test: {}", x);
522/// g_log!("test", LogLevel::Warning, "test: {} {}", x, "a");
523/// // trailing commas work as well:
524/// g_log!("test", LogLevel::Warning, "test: {} {}", x, "a",);
525/// ```
526///
527/// To be noted that the log domain is optional:
528///
529/// ```no_run
530/// use glib::{LogLevel, g_log};
531///
532/// // As you can see: no log domain:
533/// g_log!(LogLevel::Message, "test");
534/// // For the rest, it's just like when you have the log domain:
535/// // trailing commas:
536/// g_log!(LogLevel::Message, "test",);
537///
538/// // formatting:
539/// let x = 12;
540/// g_log!(LogLevel::Warning, "test: {} {}", x, "a");
541/// g_log!(LogLevel::Warning, "test: {} {}", x, "a",);
542/// ```
543#[macro_export]
544macro_rules! g_log {
545    ($log_level:expr, $format:literal $(,$arg:expr)* $(,)?) => {{
546        $crate::g_log_inner!(None::<&str>, $log_level, $format, $($arg),*);
547    }};
548    ($log_domain:expr, $log_level:expr, $format:literal $(,$arg:expr)* $(,)?) => {{
549        let log_domain = <Option<&str> as std::convert::From<_>>::from($log_domain);
550        $crate::g_log_inner!(log_domain, $log_level, $format, $($arg),*);
551    }};
552}
553
554// rustdoc-stripper-ignore-next
555/// Macro used to log using GLib logging system. It uses [g_log].
556///
557/// [g_log]: https://docs.gtk.org/glib/func.log.html
558///
559/// It is the same as calling the [`g_log!`](crate::g_log!) macro with [`LogLevel::Error`].
560///
561/// Example:
562///
563/// ```no_run
564/// use glib::g_error;
565///
566/// g_error!("test", "test");
567/// // Equivalent to:
568/// use glib::{g_log, LogLevel};
569/// g_log!("test", LogLevel::Error, "test");
570///
571/// // trailing commas work as well:
572/// g_error!("test", "test",);
573///
574/// // You can also pass arguments like in format! or println!:
575/// let x = 12;
576/// g_error!("test", "test: {}", x);
577/// g_error!("test", "test: {} {}", x, "a");
578/// // trailing commas work as well:
579/// g_error!("test", "test: {} {}", x, "a",);
580/// ```
581#[macro_export]
582macro_rules! g_error {
583    ($log_domain:expr, $format:literal, $($arg:expr),* $(,)?) => {{
584        $crate::g_log!($log_domain, $crate::LogLevel::Error, $format, $($arg),*);
585    }};
586    ($log_domain:expr, $format:literal $(,)?) => {{
587        $crate::g_log!($log_domain, $crate::LogLevel::Error, $format);
588    }};
589}
590
591// rustdoc-stripper-ignore-next
592/// Macro used to log using GLib logging system. It uses [g_log].
593///
594/// [g_log]: https://docs.gtk.org/glib/func.log.html
595///
596/// It is the same as calling the [`g_log!`](crate::g_log!) macro with [`LogLevel::Critical`].
597///
598/// Example:
599///
600/// ```no_run
601/// use glib::g_critical;
602///
603/// g_critical!("test", "test");
604/// // Equivalent to:
605/// use glib::{g_log, LogLevel};
606/// g_log!("test", LogLevel::Critical, "test");
607///
608/// // trailing commas work as well:
609/// g_critical!("test", "test",);
610///
611/// // You can also pass arguments like in format! or println!:
612/// let x = 12;
613/// g_critical!("test", "test: {}", x);
614/// g_critical!("test", "test: {} {}", x, "a");
615/// // trailing commas work as well:
616/// g_critical!("test", "test: {} {}", x, "a",);
617/// ```
618#[macro_export]
619macro_rules! g_critical {
620    ($log_domain:expr, $format:literal, $($arg:expr),* $(,)?) => {{
621        $crate::g_log!($log_domain, $crate::LogLevel::Critical, $format, $($arg),*);
622    }};
623    ($log_domain:expr, $format:literal $(,)?) => {{
624        $crate::g_log!($log_domain, $crate::LogLevel::Critical, $format);
625    }};
626}
627
628// rustdoc-stripper-ignore-next
629/// Macro used to log using GLib logging system. It uses [g_log].
630///
631/// [g_log]: https://docs.gtk.org/glib/func.log.html
632///
633/// It is the same as calling the [`g_log!`](crate::g_log!) macro with [`LogLevel::Warning`].
634///
635/// Example:
636///
637/// ```no_run
638/// use glib::g_warning;
639///
640/// g_warning!("test", "test");
641/// // Equivalent to:
642/// use glib::{g_log, LogLevel};
643/// g_log!("test", LogLevel::Warning, "test");
644///
645/// // trailing commas work as well:
646/// g_warning!("test", "test",);
647///
648/// // You can also pass arguments like in format! or println!:
649/// let x = 12;
650/// g_warning!("test", "test: {}", x);
651/// g_warning!("test", "test: {} {}", x, "a");
652/// // trailing commas work as well:
653/// g_warning!("test", "test: {} {}", x, "a",);
654/// ```
655#[macro_export]
656macro_rules! g_warning {
657    ($log_domain:expr, $format:literal, $($arg:expr),* $(,)?) => {{
658        $crate::g_log!($log_domain, $crate::LogLevel::Warning, $format, $($arg),*);
659    }};
660    ($log_domain:expr, $format:literal $(,)?) => {{
661        $crate::g_log!($log_domain, $crate::LogLevel::Warning, $format);
662    }};
663}
664
665// rustdoc-stripper-ignore-next
666/// Macro used to log using GLib logging system. It uses [g_log].
667///
668/// [g_log]: https://docs.gtk.org/glib/func.log.html
669///
670/// It is the same as calling the [`g_log!`](crate::g_log!) macro with [`LogLevel::Message`].
671///
672/// Example:
673///
674/// ```no_run
675/// use glib::g_message;
676///
677/// g_message!("test", "test");
678/// // Equivalent to:
679/// use glib::{g_log, LogLevel};
680/// g_log!("test", LogLevel::Message, "test");
681///
682/// // trailing commas work as well:
683/// g_message!("test", "test",);
684///
685/// // You can also pass arguments like in format! or println!:
686/// let x = 12;
687/// g_message!("test", "test: {}", x);
688/// g_message!("test", "test: {} {}", x, "a");
689/// // trailing commas work as well:
690/// g_message!("test", "test: {} {}", x, "a",);
691/// ```
692#[macro_export]
693macro_rules! g_message {
694    ($log_domain:expr, $format:literal, $($arg:expr),* $(,)?) => {{
695        $crate::g_log!($log_domain, $crate::LogLevel::Message, $format, $($arg),*);
696    }};
697    ($log_domain:expr, $format:literal $(,)?) => {{
698        $crate::g_log!($log_domain, $crate::LogLevel::Message, $format);
699    }};
700}
701
702// rustdoc-stripper-ignore-next
703/// Macro used to log using GLib logging system. It uses [g_log].
704///
705/// [g_log]: https://docs.gtk.org/glib/func.log.html
706///
707/// It is the same as calling the [`g_log!`](crate::g_log!) macro with [`LogLevel::Info`].
708///
709/// Example:
710///
711/// ```no_run
712/// use glib::g_info;
713///
714/// g_info!("test", "test");
715/// // Equivalent to:
716/// use glib::{g_log, LogLevel};
717/// g_log!("test", LogLevel::Info, "test");
718///
719/// // trailing commas work as well:
720/// g_info!("test", "test",);
721///
722/// // You can also pass arguments like in format! or println!:
723/// let x = 12;
724/// g_info!("test", "test: {}", x);
725/// g_info!("test", "test: {} {}", x, "a");
726/// // trailing commas work as well:
727/// g_info!("test", "test: {} {}", x, "a",);
728/// ```
729#[macro_export]
730macro_rules! g_info {
731    ($log_domain:expr, $format:literal, $($arg:expr),* $(,)?) => {{
732        $crate::g_log!($log_domain, $crate::LogLevel::Info, $format, $($arg),*);
733    }};
734    ($log_domain:expr, $format:literal $(,)?) => {{
735        $crate::g_log!($log_domain, $crate::LogLevel::Info, $format);
736    }};
737}
738
739// rustdoc-stripper-ignore-next
740/// Macro used to log using GLib logging system. It uses [g_log].
741///
742/// [g_log]: https://docs.gtk.org/glib/func.log.html
743///
744/// It is the same as calling the [`g_log!`](crate::g_log!) macro with [`LogLevel::Debug`].
745///
746/// Example:
747///
748/// ```no_run
749/// use glib::g_debug;
750///
751/// g_debug!("test", "test");
752/// // Equivalent to:
753/// use glib::{g_log, LogLevel};
754/// g_log!("test", LogLevel::Debug, "test");
755///
756/// // trailing commas work as well:
757/// g_debug!("test", "test",);
758///
759/// // You can also pass arguments like in format! or println!:
760/// let x = 12;
761/// g_debug!("test", "test: {}", x);
762/// g_debug!("test", "test: {} {}", x, "a");
763/// // trailing commas work as well:
764/// g_debug!("test", "test: {} {}", x, "a",);
765/// ```
766#[macro_export]
767macro_rules! g_debug {
768    ($log_domain:expr, $format:literal, $($arg:expr),* $(,)?) => {{
769        $crate::g_log!($log_domain, $crate::LogLevel::Debug, $format, $($arg),*);
770    }};
771    ($log_domain:expr, $format:literal $(,)?) => {{
772        $crate::g_log!($log_domain, $crate::LogLevel::Debug, $format);
773    }};
774}
775
776#[doc(hidden)]
777#[macro_export]
778macro_rules! g_print_inner {
779    ($func:ident, $format:expr $(, $arg:expr)* $(,)?) => {{
780        let mut w = $crate::GStringBuilder::default();
781
782        // Can't really happen but better safe than sorry
783        if !std::fmt::Write::write_fmt(&mut w, std::format_args!($format, $($arg),*)).is_err() {
784            unsafe {
785                $crate::ffi::$func(
786                    b"%s\0".as_ptr() as *const _,
787                    $crate::translate::ToGlibPtr::<*const std::os::raw::c_char>::to_glib_none(
788                        &w.into_string()
789                    ).0,
790                );
791            }
792        }
793    }};
794}
795
796// rustdoc-stripper-ignore-next
797/// Macro used to print messages. It uses [g_print].
798///
799/// [g_print]: https://docs.gtk.org/glib/func.print.html
800///
801/// Example:
802///
803/// ```no_run
804/// use glib::g_print;
805///
806/// g_print!("test");
807/// // trailing commas work as well:
808/// g_print!("test",);
809///
810/// let x = 12;
811/// g_print!("test: {}", x);
812/// g_print!("test: {} {}", x, "a");
813/// // trailing commas work as well:
814/// g_print!("test: {} {}", x, "a",);
815/// ```
816#[macro_export]
817macro_rules! g_print {
818    ($format:expr $(,$arg:expr)* $(,)?) => {{
819        $crate::g_print_inner!(g_print, $format, $($arg),*);
820    }};
821}
822
823// rustdoc-stripper-ignore-next
824/// Macro used to print error messages. It uses [g_printerr].
825///
826/// [g_printerr]: https://docs.gtk.org/glib/func.printerr.html
827///
828/// Example:
829///
830/// ```no_run
831/// use glib::g_printerr;
832///
833/// g_printerr!("test");
834/// // trailing commas work as well:
835/// g_printerr!("test",);
836///
837/// let x = 12;
838/// g_printerr!("test: {}", x);
839/// g_printerr!("test: {} {}", x, "a");
840/// // trailing commas work as well:
841/// g_printerr!("test: {} {}", x, "a",);
842/// ```
843#[macro_export]
844macro_rules! g_printerr {
845    ($format:expr $(, $arg:expr)* $(,)?) => {{
846        $crate::g_print_inner!(g_printerr, $format, $($arg),*);
847    }};
848}
849
850// rustdoc-stripper-ignore-next
851/// Macro used to log using GLib structured logging system.
852///
853/// The structured data is provided inside braces as key-value pairs using the `=>` token and
854/// separated by semicolons. The key can be a string literal or an expression that satisfies
855/// [`AsRef<GStr>`]. The value can be a format string with arguments, or a single expression that
856/// satisfies `AsRef<[u8]>`.
857///
858/// See [`g_log_structured`][gls] for more details.
859///
860/// [gls]: https://docs.gtk.org/glib/func.log_structured.html
861/// [`AsRef<GStr>`]: crate::GStr
862///
863/// Example:
864///
865/// ```no_run
866/// use glib::{GString, LogLevel, log_structured};
867/// use std::ffi::CString;
868///
869/// log_structured!(
870///     "test",
871///     LogLevel::Debug,
872///     {
873///         // a normal string field
874///         "MY_FIELD" => "123";
875///         // fields can also take format arguments
876///         "MY_FIELD2" => "abc {}", "def";
877///         // single argument can be a &str or a &[u8] or anything else satisfying AsRef<[u8]>
878///         "MY_FIELD3" => CString::new("my string").unwrap().to_bytes();
879///         // field names can also be dynamic
880///         GString::from("MY_FIELD4") => b"a binary string".to_owned();
881///         // the main log message goes in the MESSAGE field
882///         "MESSAGE" => "test: {} {}", 1, 2, ;
883///     }
884/// );
885/// ```
886#[macro_export]
887macro_rules! log_structured {
888    ($log_domain:expr, $log_level:expr, {$($key:expr => $format:expr $(,$arg:expr)* $(,)?);+ $(;)?} $(,)?) => {{
889        let log_domain = <Option<&str> as std::convert::From<_>>::from($log_domain);
890        let log_domain_str = log_domain.unwrap_or_default();
891        let level: $crate::LogLevel = $log_level;
892        let field_count =
893            <[()]>::len(&[$($crate::log_structured_inner!(@clear $key)),+])
894            + log_domain.map(|_| 2usize).unwrap_or(1usize)
895            + 3;
896
897        let mut line = [0u8; 32]; // 32 decimal digits of line numbers should be enough!
898        let line = {
899            use std::io::Write;
900
901            let mut cursor = std::io::Cursor::new(&mut line[..]);
902            std::write!(&mut cursor, "{}", line!()).unwrap();
903            let pos = cursor.position() as usize;
904            &line[..pos]
905        };
906
907        $crate::log_structured_array(
908            level,
909            &[
910                $crate::LogField::new(
911                    $crate::gstr!("PRIORITY"),
912                    level.priority().as_bytes(),
913                ),
914                $crate::LogField::new(
915                    $crate::gstr!("CODE_FILE"),
916                    file!().as_bytes(),
917                ),
918                $crate::LogField::new(
919                    $crate::gstr!("CODE_LINE"),
920                    line,
921                ),
922                $crate::LogField::new(
923                    $crate::gstr!("CODE_FUNC"),
924                    $crate::function_name!().as_bytes(),
925                ),
926                $(
927                    $crate::LogField::new(
928                        $crate::log_structured_inner!(@key $key),
929                        $crate::log_structured_inner!(@value $format $(,$arg)*),
930                    ),
931                )+
932                $crate::LogField::new(
933                    $crate::gstr!("GLIB_DOMAIN"),
934                    log_domain_str.as_bytes(),
935                ),
936            ][0..field_count],
937        )
938    }};
939}
940
941#[doc(hidden)]
942#[macro_export]
943macro_rules! log_structured_inner {
944    (@clear $($_:tt)*) => { () };
945    (@key $key:literal) => { $crate::gstr!($key) };
946    (@key $key:expr) => { std::convert::AsRef::<$crate::GStr>::as_ref(&$key) };
947    (@value $value:expr) => { std::convert::AsRef::<[u8]>::as_ref(&$value) };
948    (@value $format:expr $(,$arg:expr)+) => {
949        {
950            let mut builder = $crate::GStringBuilder::default();
951            if std::fmt::Write::write_fmt(&mut builder, format_args!($format, $($arg),+)).is_err() {
952                return;
953            }
954            builder.into_string()
955        }.as_str().as_bytes()
956    };
957}
958
959#[doc(alias = "g_log_structured_array")]
960#[inline]
961pub fn log_structured_array(log_level: LogLevel, fields: &[LogField<'_>]) {
962    unsafe {
963        ffi::g_log_structured_array(
964            log_level.into_glib(),
965            fields.as_ptr() as *const ffi::GLogField,
966            fields.len(),
967        )
968    }
969}
970
971#[doc(alias = "g_log_variant")]
972#[inline]
973pub fn log_variant(log_domain: Option<&str>, log_level: LogLevel, fields: &crate::Variant) {
974    unsafe {
975        ffi::g_log_variant(
976            log_domain.to_glib_none().0,
977            log_level.into_glib(),
978            fields.to_glib_none().0,
979        );
980    }
981}
982
983#[cfg(unix)]
984#[cfg_attr(docsrs, doc(cfg(unix)))]
985#[doc(alias = "g_log_writer_supports_color")]
986#[inline]
987pub fn log_writer_supports_color(output_fd: impl AsFd) -> bool {
988    unsafe {
989        from_glib(ffi::g_log_writer_supports_color(
990            output_fd.as_fd().as_raw_fd(),
991        ))
992    }
993}
994
995#[cfg(unix)]
996#[cfg_attr(docsrs, doc(cfg(unix)))]
997#[doc(alias = "g_log_writer_is_journald")]
998#[inline]
999pub fn log_writer_is_journald(output_fd: impl AsFd) -> bool {
1000    unsafe { from_glib(ffi::g_log_writer_is_journald(output_fd.as_fd().as_raw_fd())) }
1001}
1002
1003#[doc(alias = "g_log_writer_format_fields")]
1004#[inline]
1005pub fn log_writer_format_fields(
1006    log_level: LogLevel,
1007    fields: &[LogField<'_>],
1008    use_color: bool,
1009) -> GString {
1010    unsafe {
1011        from_glib_full(ffi::g_log_writer_format_fields(
1012            log_level.into_glib(),
1013            fields.as_ptr() as *const ffi::GLogField,
1014            fields.len(),
1015            use_color.into_glib(),
1016        ))
1017    }
1018}
1019
1020#[doc(alias = "g_log_writer_journald")]
1021#[inline]
1022pub fn log_writer_journald(log_level: LogLevel, fields: &[LogField<'_>]) -> LogWriterOutput {
1023    unsafe {
1024        from_glib(ffi::g_log_writer_journald(
1025            log_level.into_glib(),
1026            fields.as_ptr() as *const ffi::GLogField,
1027            fields.len(),
1028            std::ptr::null_mut(),
1029        ))
1030    }
1031}
1032
1033#[doc(alias = "g_log_writer_standard_streams")]
1034#[inline]
1035pub fn log_writer_standard_streams(
1036    log_level: LogLevel,
1037    fields: &[LogField<'_>],
1038) -> LogWriterOutput {
1039    unsafe {
1040        from_glib(ffi::g_log_writer_standard_streams(
1041            log_level.into_glib(),
1042            fields.as_ptr() as *const ffi::GLogField,
1043            fields.len(),
1044            std::ptr::null_mut(),
1045        ))
1046    }
1047}
1048
1049#[doc(alias = "g_log_writer_default")]
1050#[inline]
1051pub fn log_writer_default(log_level: LogLevel, fields: &[LogField<'_>]) -> LogWriterOutput {
1052    unsafe {
1053        from_glib(ffi::g_log_writer_default(
1054            log_level.into_glib(),
1055            fields.as_ptr() as *const ffi::GLogField,
1056            fields.len(),
1057            std::ptr::null_mut(),
1058        ))
1059    }
1060}
1061
1062// rustdoc-stripper-ignore-next
1063/// Sets whether GLib log functions output to stderr or stdout.
1064///
1065/// By default, log messages of level [`LogLevel::Info`] and [`LogLevel::Debug`] are sent to stdout,
1066/// and other log messages are sent to stderr. Passing `true` will send all messages to stderr.
1067///
1068/// # Safety
1069///
1070/// This function sets global state and is not thread-aware, as such it should be called before any
1071/// threads may try to use GLib logging.
1072#[cfg(feature = "v2_68")]
1073#[cfg_attr(docsrs, doc(cfg(feature = "v2_68")))]
1074#[doc(alias = "g_log_writer_default_set_use_stderr")]
1075#[inline]
1076pub unsafe fn log_writer_default_set_use_stderr(use_stderr: bool) {
1077    unsafe {
1078        ffi::g_log_writer_default_set_use_stderr(use_stderr.into_glib());
1079    }
1080}
1081
1082#[cfg(feature = "v2_68")]
1083#[cfg_attr(docsrs, doc(cfg(feature = "v2_68")))]
1084#[doc(alias = "g_log_writer_default_would_drop")]
1085#[inline]
1086pub fn log_writer_default_would_drop(log_level: LogLevel, log_domain: Option<&str>) -> bool {
1087    unsafe {
1088        from_glib(ffi::g_log_writer_default_would_drop(
1089            log_level.into_glib(),
1090            log_domain.to_glib_none().0,
1091        ))
1092    }
1093}