Skip to main content

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