#[cfg(unix)]
use std::os::unix::io::AsRawFd;
use std::{
boxed::Box as Box_,
sync::{Arc, Mutex},
};
use once_cell::sync::Lazy;
use crate::{translate::*, GStr, GString, LogWriterOutput};
#[derive(Debug)]
pub struct LogHandlerId(u32);
#[doc(hidden)]
impl FromGlib<u32> for LogHandlerId {
#[inline]
unsafe fn from_glib(value: u32) -> Self {
Self(value)
}
}
#[doc(hidden)]
impl IntoGlib for LogHandlerId {
type GlibType = u32;
#[inline]
fn into_glib(self) -> u32 {
self.0
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum LogLevel {
#[doc(alias = "G_LOG_LEVEL_ERROR")]
Error,
#[doc(alias = "G_LOG_LEVEL_CRITICAL")]
Critical,
#[doc(alias = "G_LOG_LEVEL_WARNING")]
Warning,
#[doc(alias = "G_LOG_LEVEL_MESSAGE")]
Message,
#[doc(alias = "G_LOG_LEVEL_INFO")]
Info,
#[doc(alias = "G_LOG_LEVEL_DEBUG")]
Debug,
}
#[doc(hidden)]
impl IntoGlib for LogLevel {
type GlibType = u32;
#[inline]
fn into_glib(self) -> u32 {
match self {
Self::Error => ffi::G_LOG_LEVEL_ERROR,
Self::Critical => ffi::G_LOG_LEVEL_CRITICAL,
Self::Warning => ffi::G_LOG_LEVEL_WARNING,
Self::Message => ffi::G_LOG_LEVEL_MESSAGE,
Self::Info => ffi::G_LOG_LEVEL_INFO,
Self::Debug => ffi::G_LOG_LEVEL_DEBUG,
}
}
}
#[doc(hidden)]
impl FromGlib<u32> for LogLevel {
#[inline]
unsafe fn from_glib(value: u32) -> Self {
if value & ffi::G_LOG_LEVEL_ERROR != 0 {
Self::Error
} else if value & ffi::G_LOG_LEVEL_CRITICAL != 0 {
Self::Critical
} else if value & ffi::G_LOG_LEVEL_WARNING != 0 {
Self::Warning
} else if value & ffi::G_LOG_LEVEL_MESSAGE != 0 {
Self::Message
} else if value & ffi::G_LOG_LEVEL_INFO != 0 {
Self::Info
} else if value & ffi::G_LOG_LEVEL_DEBUG != 0 {
Self::Debug
} else {
panic!("Unknown log level: {value}")
}
}
}
impl LogLevel {
#[doc(hidden)]
pub fn priority(&self) -> &'static str {
match self {
Self::Error => "3",
Self::Critical => "4",
Self::Warning => "4",
Self::Message => "5",
Self::Info => "6",
Self::Debug => "7",
}
}
}
bitflags::bitflags! {
#[doc(alias = "GLogLevelFlags")]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct LogLevels: u32 {
#[doc(alias = "G_LOG_LEVEL_ERROR")]
const LEVEL_ERROR = ffi::G_LOG_LEVEL_ERROR;
#[doc(alias = "G_LOG_LEVEL_CRITICAL")]
const LEVEL_CRITICAL = ffi::G_LOG_LEVEL_CRITICAL;
#[doc(alias = "G_LOG_LEVEL_WARNING")]
const LEVEL_WARNING = ffi::G_LOG_LEVEL_WARNING;
#[doc(alias = "G_LOG_LEVEL_MESSAGE")]
const LEVEL_MESSAGE = ffi::G_LOG_LEVEL_MESSAGE;
#[doc(alias = "G_LOG_LEVEL_INFO")]
const LEVEL_INFO = ffi::G_LOG_LEVEL_INFO;
#[doc(alias = "G_LOG_LEVEL_DEBUG")]
const LEVEL_DEBUG = ffi::G_LOG_LEVEL_DEBUG;
}
}
#[doc(hidden)]
impl IntoGlib for LogLevels {
type GlibType = ffi::GLogLevelFlags;
#[inline]
fn into_glib(self) -> ffi::GLogLevelFlags {
self.bits()
}
}
#[doc(hidden)]
impl FromGlib<ffi::GLogLevelFlags> for LogLevels {
#[inline]
unsafe fn from_glib(value: ffi::GLogLevelFlags) -> Self {
Self::from_bits_truncate(value)
}
}
fn to_log_flags(fatal: bool, recursion: bool) -> u32 {
(if fatal { ffi::G_LOG_FLAG_FATAL } else { 0 })
| if recursion {
ffi::G_LOG_FLAG_RECURSION
} else {
0
}
}
#[doc(alias = "g_log_set_handler_full")]
pub fn log_set_handler<P: Fn(Option<&str>, LogLevel, &str) + Send + Sync + 'static>(
log_domain: Option<&str>,
log_levels: LogLevels,
fatal: bool,
recursion: bool,
log_func: P,
) -> LogHandlerId {
let log_func_data: Box_<P> = Box_::new(log_func);
unsafe extern "C" fn log_func_func<
P: Fn(Option<&str>, LogLevel, &str) + Send + Sync + 'static,
>(
log_domain: *const libc::c_char,
log_level: ffi::GLogLevelFlags,
message: *const libc::c_char,
user_data: ffi::gpointer,
) {
let log_domain: Borrowed<Option<GString>> = from_glib_borrow(log_domain);
let message: Borrowed<GString> = from_glib_borrow(message);
let callback: &P = &*(user_data as *mut _);
(*callback)(
(*log_domain).as_ref().map(|s| s.as_str()),
from_glib(log_level),
message.as_str(),
);
}
let log_func = Some(log_func_func::<P> as _);
unsafe extern "C" fn destroy_func<
P: Fn(Option<&str>, LogLevel, &str) + Send + Sync + 'static,
>(
data: ffi::gpointer,
) {
let _callback: Box_<P> = Box_::from_raw(data as *mut _);
}
let destroy_call4 = Some(destroy_func::<P> as _);
let super_callback0: Box_<P> = log_func_data;
unsafe {
from_glib(ffi::g_log_set_handler_full(
log_domain.to_glib_none().0,
log_levels.into_glib() | to_log_flags(fatal, recursion),
log_func,
Box_::into_raw(super_callback0) as *mut _,
destroy_call4,
))
}
}
#[doc(alias = "g_log_remove_handler")]
pub fn log_remove_handler(log_domain: Option<&str>, handler_id: LogHandlerId) {
unsafe {
ffi::g_log_remove_handler(log_domain.to_glib_none().0, handler_id.into_glib());
}
}
#[doc(alias = "g_log_set_always_fatal")]
pub fn log_set_always_fatal(fatal_levels: LogLevels) -> LogLevels {
unsafe { from_glib(ffi::g_log_set_always_fatal(fatal_levels.into_glib())) }
}
#[doc(alias = "g_log_set_fatal_mask")]
pub fn log_set_fatal_mask(log_domain: Option<&str>, fatal_levels: LogLevels) -> LogLevels {
unsafe {
from_glib(ffi::g_log_set_fatal_mask(
log_domain.to_glib_none().0,
fatal_levels.into_glib(),
))
}
}
type PrintCallback = dyn Fn(&str) + Send + Sync + 'static;
static PRINT_HANDLER: Lazy<Mutex<Option<Arc<PrintCallback>>>> = Lazy::new(|| Mutex::new(None));
#[doc(alias = "g_set_print_handler")]
pub fn set_print_handler<P: Fn(&str) + Send + Sync + 'static>(func: P) {
unsafe extern "C" fn func_func(string: *const libc::c_char) {
if let Some(callback) = PRINT_HANDLER
.lock()
.expect("Failed to lock PRINT_HANDLER")
.as_ref()
.map(Arc::clone)
{
let string: Borrowed<GString> = from_glib_borrow(string);
(*callback)(string.as_str())
}
}
*PRINT_HANDLER
.lock()
.expect("Failed to lock PRINT_HANDLER to change callback") = Some(Arc::new(func));
unsafe { ffi::g_set_print_handler(Some(func_func as _)) };
}
pub fn unset_print_handler() {
*PRINT_HANDLER
.lock()
.expect("Failed to lock PRINT_HANDLER to remove callback") = None;
unsafe { ffi::g_set_print_handler(None) };
}
static PRINTERR_HANDLER: Lazy<Mutex<Option<Arc<PrintCallback>>>> = Lazy::new(|| Mutex::new(None));
#[doc(alias = "g_set_printerr_handler")]
pub fn set_printerr_handler<P: Fn(&str) + Send + Sync + 'static>(func: P) {
unsafe extern "C" fn func_func(string: *const libc::c_char) {
if let Some(callback) = PRINTERR_HANDLER
.lock()
.expect("Failed to lock PRINTERR_HANDLER")
.as_ref()
.map(Arc::clone)
{
let string: Borrowed<GString> = from_glib_borrow(string);
(*callback)(string.as_str())
}
}
*PRINTERR_HANDLER
.lock()
.expect("Failed to lock PRINTERR_HANDLER to change callback") = Some(Arc::new(func));
unsafe { ffi::g_set_printerr_handler(Some(func_func as _)) };
}
pub fn unset_printerr_handler() {
*PRINTERR_HANDLER
.lock()
.expect("Failed to lock PRINTERR_HANDLER to remove callback") = None;
unsafe { ffi::g_set_printerr_handler(None) };
}
type LogCallback = dyn Fn(Option<&str>, LogLevel, &str) + Send + Sync + 'static;
static DEFAULT_HANDLER: Lazy<Mutex<Option<Arc<LogCallback>>>> = Lazy::new(|| Mutex::new(None));
#[doc(alias = "g_log_set_default_handler")]
pub fn log_set_default_handler<P: Fn(Option<&str>, LogLevel, &str) + Send + Sync + 'static>(
log_func: P,
) {
unsafe extern "C" fn func_func(
log_domain: *const libc::c_char,
log_levels: ffi::GLogLevelFlags,
message: *const libc::c_char,
_user_data: ffi::gpointer,
) {
if let Some(callback) = DEFAULT_HANDLER
.lock()
.expect("Failed to lock DEFAULT_HANDLER")
.as_ref()
.map(Arc::clone)
{
let log_domain: Borrowed<Option<GString>> = from_glib_borrow(log_domain);
let message: Borrowed<GString> = from_glib_borrow(message);
(*callback)(
(*log_domain).as_ref().map(|s| s.as_str()),
from_glib(log_levels),
message.as_str(),
);
}
}
*DEFAULT_HANDLER
.lock()
.expect("Failed to lock DEFAULT_HANDLER to change callback") = Some(Arc::new(log_func));
unsafe { ffi::g_log_set_default_handler(Some(func_func as _), std::ptr::null_mut()) };
}
#[doc(alias = "g_log_set_default_handler")]
pub fn log_unset_default_handler() {
*DEFAULT_HANDLER
.lock()
.expect("Failed to lock DEFAULT_HANDLER to remove callback") = None;
unsafe {
ffi::g_log_set_default_handler(Some(ffi::g_log_default_handler), std::ptr::null_mut())
};
}
#[doc(alias = "g_log_default_handler")]
pub fn log_default_handler(log_domain: Option<&str>, log_level: LogLevel, message: Option<&str>) {
unsafe {
ffi::g_log_default_handler(
log_domain.to_glib_none().0,
log_level.into_glib(),
message.to_glib_none().0,
std::ptr::null_mut(),
)
}
}
#[repr(transparent)]
#[derive(Debug)]
#[doc(alias = "GLogField")]
pub struct LogField<'a>(ffi::GLogField, std::marker::PhantomData<&'a GStr>);
impl<'a> LogField<'a> {
pub fn new(key: &'a GStr, value: &[u8]) -> Self {
let (value, length) = if value.is_empty() {
(&[0u8] as &[u8], -1isize)
} else {
(value, value.len().try_into().unwrap())
};
Self(
ffi::GLogField {
key: key.as_ptr(),
value: value.as_ptr() as *const _,
length,
},
Default::default(),
)
}
pub fn new_user_data(key: &'a GStr, data: usize) -> Self {
Self(
ffi::GLogField {
key: key.as_ptr(),
value: data as *const _,
length: 0,
},
Default::default(),
)
}
pub fn key(&self) -> &str {
unsafe { std::ffi::CStr::from_ptr(self.0.key as *const _) }
.to_str()
.unwrap()
}
pub fn value_bytes(&self) -> Option<&[u8]> {
match self.0.length {
0 => None,
n if n < 0 => {
Some(unsafe { std::ffi::CStr::from_ptr(self.0.value as *const _) }.to_bytes())
}
_ => Some(unsafe {
std::slice::from_raw_parts(self.0.value as *const u8, self.0.length as usize)
}),
}
}
pub fn value_str(&self) -> Option<&str> {
std::str::from_utf8(self.value_bytes()?).ok()
}
pub fn user_data(&self) -> Option<usize> {
(self.0.length == 0).then_some(self.0.value as usize)
}
}
type WriterCallback = dyn Fn(LogLevel, &[LogField<'_>]) -> LogWriterOutput + Send + Sync + 'static;
static WRITER_FUNC: once_cell::sync::OnceCell<Box<WriterCallback>> =
once_cell::sync::OnceCell::new();
#[doc(alias = "g_log_set_writer_func")]
pub fn log_set_writer_func<
P: Fn(LogLevel, &[LogField<'_>]) -> LogWriterOutput + Send + Sync + 'static,
>(
writer_func: P,
) {
if WRITER_FUNC.set(Box::new(writer_func)).is_err() {
panic!("Writer func can only be set once");
}
unsafe extern "C" fn writer_trampoline(
log_level: ffi::GLogLevelFlags,
fields: *const ffi::GLogField,
n_fields: libc::size_t,
_user_data: ffi::gpointer,
) -> ffi::GLogWriterOutput {
let writer_func = WRITER_FUNC.get().unwrap();
let fields = std::slice::from_raw_parts(fields as *const LogField<'_>, n_fields);
writer_func(from_glib(log_level), fields).into_glib()
}
unsafe {
ffi::g_log_set_writer_func(Some(writer_trampoline), std::ptr::null_mut(), None);
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! g_log_inner {
($log_domain:expr, $log_level:expr, $format:literal $(,$arg:expr)* $(,)?) => {{
let mut w = $crate::GStringBuilder::default();
if !std::fmt::Write::write_fmt(&mut w, std::format_args!($format, $($arg),*)).is_err() {
unsafe {
$crate::ffi::g_log(
$crate::translate::ToGlibPtr::to_glib_none(&$log_domain).0,
<$crate::LogLevel as $crate::translate::IntoGlib>::into_glib(
$log_level
),
b"%s\0".as_ptr() as *const _,
$crate::translate::ToGlibPtr::<*const std::os::raw::c_char>::to_glib_none(
&w.into_string()
).0,
);
}
}
}};
}
#[macro_export]
macro_rules! g_log {
($log_level:expr, $format:literal $(,$arg:expr)* $(,)?) => {{
$crate::g_log_inner!(None::<&str>, $log_level, $format, $($arg),*);
}};
($log_domain:expr, $log_level:expr, $format:literal $(,$arg:expr)* $(,)?) => {{
let log_domain = <Option<&str> as std::convert::From<_>>::from($log_domain);
$crate::g_log_inner!(log_domain, $log_level, $format, $($arg),*);
}};
}
#[macro_export]
macro_rules! g_error {
($log_domain:expr, $format:literal, $($arg:expr),* $(,)?) => {{
$crate::g_log!($log_domain, $crate::LogLevel::Error, $format, $($arg),*);
}};
($log_domain:expr, $format:literal $(,)?) => {{
$crate::g_log!($log_domain, $crate::LogLevel::Error, $format);
}};
}
#[macro_export]
macro_rules! g_critical {
($log_domain:expr, $format:literal, $($arg:expr),* $(,)?) => {{
$crate::g_log!($log_domain, $crate::LogLevel::Critical, $format, $($arg),*);
}};
($log_domain:expr, $format:literal $(,)?) => {{
$crate::g_log!($log_domain, $crate::LogLevel::Critical, $format);
}};
}
#[macro_export]
macro_rules! g_warning {
($log_domain:expr, $format:literal, $($arg:expr),* $(,)?) => {{
$crate::g_log!($log_domain, $crate::LogLevel::Warning, $format, $($arg),*);
}};
($log_domain:expr, $format:literal $(,)?) => {{
$crate::g_log!($log_domain, $crate::LogLevel::Warning, $format);
}};
}
#[macro_export]
macro_rules! g_message {
($log_domain:expr, $format:literal, $($arg:expr),* $(,)?) => {{
$crate::g_log!($log_domain, $crate::LogLevel::Message, $format, $($arg),*);
}};
($log_domain:expr, $format:literal $(,)?) => {{
$crate::g_log!($log_domain, $crate::LogLevel::Message, $format);
}};
}
#[macro_export]
macro_rules! g_info {
($log_domain:expr, $format:literal, $($arg:expr),* $(,)?) => {{
$crate::g_log!($log_domain, $crate::LogLevel::Info, $format, $($arg),*);
}};
($log_domain:expr, $format:literal $(,)?) => {{
$crate::g_log!($log_domain, $crate::LogLevel::Info, $format);
}};
}
#[macro_export]
macro_rules! g_debug {
($log_domain:expr, $format:literal, $($arg:expr),* $(,)?) => {{
$crate::g_log!($log_domain, $crate::LogLevel::Debug, $format, $($arg),*);
}};
($log_domain:expr, $format:literal $(,)?) => {{
$crate::g_log!($log_domain, $crate::LogLevel::Debug, $format);
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! g_print_inner {
($func:ident, $format:expr $(, $arg:expr)* $(,)?) => {{
let mut w = $crate::GStringBuilder::default();
if !std::fmt::Write::write_fmt(&mut w, std::format_args!($format, $($arg),*)).is_err() {
unsafe {
$crate::ffi::$func(
b"%s\0".as_ptr() as *const _,
$crate::translate::ToGlibPtr::<*const std::os::raw::c_char>::to_glib_none(
&w.into_string()
).0,
);
}
}
}};
}
#[macro_export]
macro_rules! g_print {
($format:expr $(,$arg:expr)* $(,)?) => {{
$crate::g_print_inner!(g_print, $format, $($arg),*);
}};
}
#[macro_export]
macro_rules! g_printerr {
($format:expr $(, $arg:expr)* $(,)?) => {{
$crate::g_print_inner!(g_printerr, $format, $($arg),*);
}};
}
#[macro_export]
macro_rules! log_structured {
($log_domain:expr, $log_level:expr, {$($key:expr => $format:expr $(,$arg:expr)* $(,)?);+ $(;)?} $(,)?) => {{
let log_domain = <Option<&str> as std::convert::From<_>>::from($log_domain);
let log_domain_str = log_domain.unwrap_or_default();
let level: $crate::LogLevel = $log_level;
let field_count =
<[()]>::len(&[$($crate::log_structured_inner!(@clear $key)),+])
+ log_domain.map(|_| 2usize).unwrap_or(1usize)
+ 3;
let mut line = [0u8; 32]; let line = {
use std::io::Write;
let mut cursor = std::io::Cursor::new(&mut line[..]);
std::write!(&mut cursor, "{}", line!()).unwrap();
let pos = cursor.position() as usize;
&line[..pos]
};
$crate::log_structured_array(
level,
&[
$crate::LogField::new(
$crate::gstr!("PRIORITY"),
level.priority().as_bytes(),
),
$crate::LogField::new(
$crate::gstr!("CODE_FILE"),
file!().as_bytes(),
),
$crate::LogField::new(
$crate::gstr!("CODE_LINE"),
line,
),
$crate::LogField::new(
$crate::gstr!("CODE_FUNC"),
$crate::function_name!().as_bytes(),
),
$(
$crate::LogField::new(
$crate::log_structured_inner!(@key $key),
$crate::log_structured_inner!(@value $format $(,$arg)*),
),
)+
$crate::LogField::new(
$crate::gstr!("GLIB_DOMAIN"),
log_domain_str.as_bytes(),
),
][0..field_count],
)
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! log_structured_inner {
(@clear $($_:tt)*) => { () };
(@key $key:literal) => { $crate::gstr!($key) };
(@key $key:expr) => { std::convert::AsRef::<$crate::GStr>::as_ref(&$key) };
(@value $value:expr) => { std::convert::AsRef::<[u8]>::as_ref(&$value) };
(@value $format:expr $(,$arg:expr)+) => {
{
let mut builder = $crate::GStringBuilder::default();
if std::fmt::Write::write_fmt(&mut builder, format_args!($format, $($arg),+)).is_err() {
return;
}
builder.into_string()
}.as_str().as_bytes()
};
}
#[doc(alias = "g_log_structured_array")]
#[inline]
pub fn log_structured_array(log_level: LogLevel, fields: &[LogField<'_>]) {
unsafe {
ffi::g_log_structured_array(
log_level.into_glib(),
fields.as_ptr() as *const ffi::GLogField,
fields.len(),
)
}
}
#[doc(alias = "g_log_variant")]
#[inline]
pub fn log_variant(log_domain: Option<&str>, log_level: LogLevel, fields: &crate::Variant) {
unsafe {
ffi::g_log_variant(
log_domain.to_glib_none().0,
log_level.into_glib(),
fields.to_glib_none().0,
);
}
}
#[cfg(unix)]
#[cfg_attr(docsrs, doc(cfg(unix)))]
#[doc(alias = "g_log_writer_supports_color")]
#[inline]
pub fn log_writer_supports_color<T: AsRawFd>(output_fd: T) -> bool {
unsafe { from_glib(ffi::g_log_writer_supports_color(output_fd.as_raw_fd())) }
}
#[cfg(unix)]
#[cfg_attr(docsrs, doc(cfg(unix)))]
#[doc(alias = "g_log_writer_is_journald")]
#[inline]
pub fn log_writer_is_journald<T: AsRawFd>(output_fd: T) -> bool {
unsafe { from_glib(ffi::g_log_writer_is_journald(output_fd.as_raw_fd())) }
}
#[doc(alias = "g_log_writer_format_fields")]
#[inline]
pub fn log_writer_format_fields(
log_level: LogLevel,
fields: &[LogField<'_>],
use_color: bool,
) -> GString {
unsafe {
from_glib_full(ffi::g_log_writer_format_fields(
log_level.into_glib(),
fields.as_ptr() as *const ffi::GLogField,
fields.len(),
use_color.into_glib(),
))
}
}
#[doc(alias = "g_log_writer_journald")]
#[inline]
pub fn log_writer_journald(log_level: LogLevel, fields: &[LogField<'_>]) -> LogWriterOutput {
unsafe {
from_glib(ffi::g_log_writer_journald(
log_level.into_glib(),
fields.as_ptr() as *const ffi::GLogField,
fields.len(),
std::ptr::null_mut(),
))
}
}
#[doc(alias = "g_log_writer_standard_streams")]
#[inline]
pub fn log_writer_standard_streams(
log_level: LogLevel,
fields: &[LogField<'_>],
) -> LogWriterOutput {
unsafe {
from_glib(ffi::g_log_writer_standard_streams(
log_level.into_glib(),
fields.as_ptr() as *const ffi::GLogField,
fields.len(),
std::ptr::null_mut(),
))
}
}
#[doc(alias = "g_log_writer_default")]
#[inline]
pub fn log_writer_default(log_level: LogLevel, fields: &[LogField<'_>]) -> LogWriterOutput {
unsafe {
from_glib(ffi::g_log_writer_default(
log_level.into_glib(),
fields.as_ptr() as *const ffi::GLogField,
fields.len(),
std::ptr::null_mut(),
))
}
}
#[cfg(feature = "v2_68")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_68")))]
#[doc(alias = "g_log_writer_default_set_use_stderr")]
#[inline]
pub unsafe fn log_writer_default_set_use_stderr(use_stderr: bool) {
ffi::g_log_writer_default_set_use_stderr(use_stderr.into_glib());
}
#[cfg(feature = "v2_68")]
#[cfg_attr(docsrs, doc(cfg(feature = "v2_68")))]
#[doc(alias = "g_log_writer_default_would_drop")]
#[inline]
pub fn log_writer_default_would_drop(log_level: LogLevel, log_domain: Option<&str>) -> bool {
unsafe {
from_glib(ffi::g_log_writer_default_would_drop(
log_level.into_glib(),
log_domain.to_glib_none().0,
))
}
}