Skip to main content

apple_log/
logger.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, sanitized_c_string};
7use crate::error::LogError;
8use crate::ffi;
9use crate::os_log::{Level, OSLog};
10use crate::os_signpost_id::OSSignpostId;
11
12/// Controls whether logged string payloads are persisted in clear text.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub enum Privacy {
15    Public,
16    Private,
17}
18
19impl Privacy {
20    const fn raw(self) -> i32 {
21        match self {
22            Self::Public => 0,
23            Self::Private => 1,
24        }
25    }
26}
27
28const fn severity_for_level(level: Level) -> i32 {
29    match level {
30        Level::Default => 0,
31        Level::Debug => 2,
32        Level::Info => 3,
33        Level::Error => 6,
34        Level::Fault => 8,
35    }
36}
37
38/// Swift `Logger` backed by an `OSLog` handle.
39pub struct Logger {
40    ptr: NonNull<c_void>,
41}
42
43impl Logger {
44    fn bridge_default() -> Self {
45        Self {
46            ptr: NonNull::new(unsafe { ffi::apple_log_logger_default() })
47                .expect("Swift bridge never returns NULL for Logger.default"),
48        }
49    }
50
51    /// Creates a logger for a subsystem/category pair.
52    ///
53    /// # Errors
54    ///
55    /// Returns an error if either argument contains a NUL byte or the bridge fails.
56    pub fn new(subsystem: &str, category: &str) -> Result<Self, LogError> {
57        let subsystem = c_string_arg("subsystem", subsystem)?;
58        let category = c_string_arg("category", category)?;
59        let ptr = bridge_ptr_result("Logger::new", |error_out| unsafe {
60            ffi::apple_log_logger_create(subsystem.as_ptr(), category.as_ptr(), error_out)
61        })?;
62        Ok(Self { ptr })
63    }
64
65    /// Creates a logger from an existing `OSLog` handle.
66    ///
67    /// # Errors
68    ///
69    /// Returns an error if the bridge fails to create the logger wrapper.
70    pub fn from_os_log(log: &OSLog) -> Result<Self, LogError> {
71        let ptr = bridge_ptr_result("Logger::from_os_log", |error_out| unsafe {
72            ffi::apple_log_logger_from_os_log(log.as_ptr(), error_out)
73        })?;
74        Ok(Self { ptr })
75    }
76
77    /// Returns `Logger()`.
78    #[must_use]
79    pub fn default() -> Self {
80        Self::bridge_default()
81    }
82
83    /// Returns a disabled logger.
84    #[must_use]
85    pub fn disabled() -> Self {
86        Self {
87            ptr: NonNull::new(unsafe { ffi::apple_log_logger_disabled() })
88                .expect("Swift bridge never returns NULL for Logger.disabled"),
89        }
90    }
91
92    fn write(&self, severity: i32, privacy: Privacy, message: &str) {
93        let message = sanitized_c_string(message);
94        unsafe {
95            ffi::apple_log_logger_log(self.ptr.as_ptr(), severity, privacy.raw(), message.as_ptr());
96        }
97    }
98
99    /// Emits a message at one of the classic `OSLogType` levels.
100    pub fn log(&self, level: Level, message: &str) {
101        self.write(severity_for_level(level), Privacy::Public, message);
102    }
103
104    /// Emits a message with an explicit privacy level.
105    pub fn log_with_privacy(&self, level: Level, message: &str, privacy: Privacy) {
106        self.write(severity_for_level(level), privacy, message);
107    }
108
109    pub fn trace(&self, message: &str) {
110        self.write(1, Privacy::Public, message);
111    }
112
113    pub fn debug(&self, message: &str) {
114        self.write(2, Privacy::Public, message);
115    }
116
117    pub fn info(&self, message: &str) {
118        self.write(3, Privacy::Public, message);
119    }
120
121    pub fn notice(&self, message: &str) {
122        self.write(4, Privacy::Public, message);
123    }
124
125    pub fn warning(&self, message: &str) {
126        self.write(5, Privacy::Public, message);
127    }
128
129    pub fn error(&self, message: &str) {
130        self.write(6, Privacy::Public, message);
131    }
132
133    pub fn critical(&self, message: &str) {
134        self.write(7, Privacy::Public, message);
135    }
136
137    pub fn fault(&self, message: &str) {
138        self.write(8, Privacy::Public, message);
139    }
140
141    /// Returns whether the underlying `OSLog` handle enables this classic level.
142    #[must_use]
143    pub fn is_enabled(&self, level: Level) -> bool {
144        unsafe { ffi::apple_log_logger_is_enabled(self.ptr.as_ptr(), level as u8) }
145    }
146
147    /// Generates a signpost identifier associated with this logger's log handle.
148    #[must_use]
149    pub fn signpost_id(&self) -> OSSignpostId {
150        OSSignpostId::from_u64(unsafe { ffi::apple_log_logger_signpost_id(self.ptr.as_ptr()) })
151    }
152
153    /// Generates a signpost identifier derived from a pointer.
154    #[must_use]
155    pub fn signpost_id_from_pointer<T>(&self, pointer: *const T) -> OSSignpostId {
156        OSSignpostId::from_u64(unsafe {
157            ffi::apple_log_logger_signpost_id_from_pointer(self.ptr.as_ptr(), pointer.cast())
158        })
159    }
160
161    /// Returns whether signposts are enabled for this logger's log handle.
162    #[must_use]
163    pub fn signposts_enabled(&self) -> bool {
164        unsafe { ffi::apple_log_logger_signposts_enabled(self.ptr.as_ptr()) }
165    }
166
167    pub fn signpost_event(&self, id: OSSignpostId, name: &str, message: &str) {
168        let name = sanitized_c_string(name);
169        let message = sanitized_c_string(message);
170        unsafe {
171            ffi::apple_log_logger_signpost_event(
172                self.ptr.as_ptr(),
173                id.as_u64(),
174                name.as_ptr(),
175                message.as_ptr(),
176            );
177        }
178    }
179
180    pub fn signpost_interval_begin(&self, id: OSSignpostId, name: &str) {
181        let name = sanitized_c_string(name);
182        unsafe {
183            ffi::apple_log_logger_signpost_interval_begin(
184                self.ptr.as_ptr(),
185                id.as_u64(),
186                name.as_ptr(),
187            );
188        }
189    }
190
191    pub fn signpost_animation_interval_begin(&self, id: OSSignpostId, name: &str) {
192        let name = sanitized_c_string(name);
193        unsafe {
194            ffi::apple_log_logger_signpost_animation_interval_begin(
195                self.ptr.as_ptr(),
196                id.as_u64(),
197                name.as_ptr(),
198            );
199        }
200    }
201
202    pub fn signpost_interval_end(&self, id: OSSignpostId, name: &str) {
203        let name = sanitized_c_string(name);
204        unsafe {
205            ffi::apple_log_logger_signpost_interval_end(self.ptr.as_ptr(), id.as_u64(), name.as_ptr());
206        }
207    }
208
209    pub(crate) const fn as_ptr(&self) -> *mut c_void {
210        self.ptr.as_ptr()
211    }
212}
213
214impl Default for Logger {
215    fn default() -> Self {
216        Self::bridge_default()
217    }
218}
219
220impl Drop for Logger {
221    fn drop(&mut self) {
222        unsafe { ffi::apple_log_logger_release(self.ptr.as_ptr()) };
223    }
224}