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