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