Skip to main content

apple_log/
os_log.rs

1#![allow(clippy::missing_panics_doc, clippy::should_implement_trait)]
2
3use core::ffi::c_void;
4use std::ptr::NonNull;
5
6use crate::bridge_support::{bridge_ptr_result, c_string_arg, take_optional_c_string};
7use crate::error::LogError;
8use crate::ffi;
9
10pub const CATEGORY_POINTS_OF_INTEREST: &str = ffi::category::POINTS_OF_INTEREST;
11pub const CATEGORY_DYNAMIC_TRACING: &str = ffi::category::DYNAMIC_TRACING;
12pub const CATEGORY_DYNAMIC_STACK_TRACING: &str = ffi::category::DYNAMIC_STACK_TRACING;
13
14/// Apple's log levels (`OSLogType`).
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16#[repr(u8)]
17pub enum Level {
18    Default = ffi::level::DEFAULT,
19    Info = ffi::level::INFO,
20    Debug = ffi::level::DEBUG,
21    Error = ffi::level::ERROR,
22    Fault = ffi::level::FAULT,
23}
24
25/// Safe wrapper around `OSLog` / `os_log_t` handles.
26pub struct OSLog {
27    ptr: NonNull<c_void>,
28}
29
30impl OSLog {
31    fn bridge_default() -> Self {
32        Self {
33            ptr: NonNull::new(unsafe { ffi::apple_log_os_log_default() })
34                .expect("Swift bridge never returns NULL for OSLog.default"),
35        }
36    }
37
38    /// Creates a log handle for a subsystem/category pair.
39    ///
40    /// # Errors
41    ///
42    /// Returns an error if either string contains a NUL byte or the bridge fails.
43    pub fn new(subsystem: &str, category: &str) -> Result<Self, LogError> {
44        let subsystem = c_string_arg("subsystem", subsystem)?;
45        let category = c_string_arg("category", category)?;
46        let ptr = bridge_ptr_result("OSLog::new", |error_out| unsafe {
47            ffi::apple_log_os_log_create(subsystem.as_ptr(), category.as_ptr(), error_out)
48        })?;
49        Ok(Self { ptr })
50    }
51
52    /// Returns `OSLog.default`.
53    #[must_use]
54    pub fn default() -> Self {
55        Self::bridge_default()
56    }
57
58    /// Returns `OSLog.disabled`.
59    #[must_use]
60    pub fn disabled() -> Self {
61        Self {
62            ptr: NonNull::new(unsafe { ffi::apple_log_os_log_disabled() })
63                .expect("Swift bridge never returns NULL for OSLog.disabled"),
64        }
65    }
66
67    /// Returns whether a log level is enabled.
68    #[must_use]
69    pub fn is_enabled(&self, level: Level) -> bool {
70        unsafe { ffi::apple_log_os_log_is_enabled(self.ptr.as_ptr(), level as u8) }
71    }
72
73    /// Returns whether signposts are enabled for this log handle.
74    #[must_use]
75    pub fn signposts_enabled(&self) -> bool {
76        unsafe { ffi::apple_log_os_log_signposts_enabled(self.ptr.as_ptr()) }
77    }
78
79    /// Returns the subsystem used to create this log handle, if the bridge knows it.
80    #[must_use]
81    pub fn subsystem(&self) -> Option<String> {
82        unsafe { take_optional_c_string(ffi::apple_log_os_log_copy_subsystem(self.ptr.as_ptr())) }
83    }
84
85    /// Returns the category used to create this log handle, if the bridge knows it.
86    #[must_use]
87    pub fn category(&self) -> Option<String> {
88        unsafe { take_optional_c_string(ffi::apple_log_os_log_copy_category(self.ptr.as_ptr())) }
89    }
90
91    pub(crate) const fn as_ptr(&self) -> *mut c_void {
92        self.ptr.as_ptr()
93    }
94}
95
96impl Default for OSLog {
97    fn default() -> Self {
98        Self::bridge_default()
99    }
100}
101
102impl Drop for OSLog {
103    fn drop(&mut self) {
104        unsafe { ffi::apple_log_os_log_release(self.ptr.as_ptr()) };
105    }
106}