206 lines
6.1 KiB
Rust
206 lines
6.1 KiB
Rust
use crate::{
|
||
ffi::{
|
||
__dso_handle, _os_activity_create, _os_activity_current,
|
||
os_activity_flag_t_OS_ACTIVITY_FLAG_DEFAULT, os_activity_scope_enter,
|
||
os_activity_scope_leave, os_activity_scope_state_s, os_activity_scope_state_t,
|
||
os_activity_t, os_log_create, os_log_t, os_log_type_t_OS_LOG_TYPE_DEBUG,
|
||
os_log_type_t_OS_LOG_TYPE_ERROR, os_log_type_t_OS_LOG_TYPE_INFO, os_release,
|
||
wrapped_os_log_default, wrapped_os_log_with_type,
|
||
},
|
||
visitor::{AttributeMap, FieldVisitor},
|
||
};
|
||
use once_cell::sync::Lazy;
|
||
use parking_lot::Mutex;
|
||
use std::{collections::HashMap, ffi::CString, ops::Deref, ptr::addr_of_mut};
|
||
use tracing_core::{
|
||
span::{Attributes, Id},
|
||
Event, Level, Subscriber,
|
||
};
|
||
use tracing_subscriber::{
|
||
layer::{Context, Layer},
|
||
registry::LookupSpan,
|
||
};
|
||
|
||
static NAMES: Lazy<Mutex<HashMap<String, CString>>> = Lazy::new(Mutex::default);
|
||
|
||
struct Activity(os_activity_t);
|
||
// lol
|
||
unsafe impl Send for Activity {}
|
||
unsafe impl Sync for Activity {}
|
||
impl Deref for Activity {
|
||
type Target = os_activity_t;
|
||
|
||
fn deref(&self) -> &Self::Target {
|
||
&self.0
|
||
}
|
||
}
|
||
impl Drop for Activity {
|
||
fn drop(&mut self) {
|
||
unsafe {
|
||
os_release(self.0 as *mut _);
|
||
}
|
||
}
|
||
}
|
||
|
||
pub struct OsLogger {
|
||
logger: os_log_t,
|
||
}
|
||
|
||
impl Default for OsLogger {
|
||
fn default() -> Self {
|
||
Self {
|
||
logger: unsafe { wrapped_os_log_default() },
|
||
}
|
||
}
|
||
}
|
||
|
||
impl OsLogger {
|
||
/// Initialize a new `OsLogger`, which will output [tracing] events to
|
||
/// os_log on Apple platforms.
|
||
///
|
||
/// # Arguments
|
||
///
|
||
/// * `subsystem` - An identifier string, in reverse DNS notation, that
|
||
/// represents the subsystem that’s performing logging, for example,
|
||
/// `com.your_company.your_subsystem_name`. The subsystem is used for
|
||
/// categorization and filtering of related log messages, as well as for
|
||
/// grouping related logging settings.
|
||
/// * `category` - A category within the specified subsystem. The system
|
||
/// uses the category to categorize and filter related log messages, as
|
||
/// well as to group related logging settings within the subsystem’s
|
||
/// settings. A category’s logging settings override those of the parent
|
||
/// subsystem.
|
||
pub fn new<S, C>(subsystem: S, category: C) -> Self
|
||
where
|
||
S: AsRef<str>,
|
||
C: AsRef<str>,
|
||
{
|
||
let subsystem = CString::new(subsystem.as_ref())
|
||
.expect("failed to construct C string from subsystem name");
|
||
let category = CString::new(category.as_ref())
|
||
.expect("failed to construct C string from category name");
|
||
let logger = unsafe { os_log_create(subsystem.as_ptr(), category.as_ptr()) };
|
||
Self { logger }
|
||
}
|
||
}
|
||
|
||
unsafe impl Sync for OsLogger {}
|
||
unsafe impl Send for OsLogger {}
|
||
|
||
impl<S> Layer<S> for OsLogger
|
||
where
|
||
S: Subscriber + for<'a> LookupSpan<'a>,
|
||
{
|
||
fn on_new_span(&self, attrs: &Attributes, id: &Id, ctx: Context<S>) {
|
||
let span = ctx.span(id).expect("invalid span, this shouldn't happen");
|
||
let mut extensions = span.extensions_mut();
|
||
if extensions.get_mut::<Activity>().is_none() {
|
||
let mut names = NAMES.lock();
|
||
let metadata = span.metadata();
|
||
let parent_activity = match span.parent() {
|
||
Some(parent) => **parent
|
||
.extensions()
|
||
.get::<Activity>()
|
||
.expect("parent span didn't contain activity wtf"),
|
||
None => unsafe { addr_of_mut!(_os_activity_current) },
|
||
};
|
||
let mut attributes = AttributeMap::default();
|
||
let mut attr_visitor = FieldVisitor::new(&mut attributes);
|
||
attrs.record(&mut attr_visitor);
|
||
let name = {
|
||
let function_name = [metadata.target(), metadata.name()].join("::");
|
||
let full_name = format!(
|
||
"{}({})",
|
||
function_name,
|
||
attributes
|
||
.into_iter()
|
||
.map(|(k, v)| format!("{}: {}", k, v))
|
||
.collect::<Vec<_>>()
|
||
.join(", ")
|
||
);
|
||
names.entry(full_name.clone()).or_insert_with(|| {
|
||
CString::new(full_name).expect("failed to construct C string from span name")
|
||
})
|
||
};
|
||
let activity = unsafe {
|
||
_os_activity_create(
|
||
addr_of_mut!(__dso_handle) as *mut _,
|
||
name.as_ptr(),
|
||
parent_activity,
|
||
os_activity_flag_t_OS_ACTIVITY_FLAG_DEFAULT,
|
||
)
|
||
};
|
||
extensions.insert(Activity(activity));
|
||
}
|
||
}
|
||
|
||
fn on_event(&self, event: &Event, ctx: Context<S>) {
|
||
let metadata = event.metadata();
|
||
let level = match *metadata.level() {
|
||
Level::TRACE => os_log_type_t_OS_LOG_TYPE_DEBUG,
|
||
Level::DEBUG => os_log_type_t_OS_LOG_TYPE_DEBUG,
|
||
Level::INFO => os_log_type_t_OS_LOG_TYPE_INFO,
|
||
Level::WARN => os_log_type_t_OS_LOG_TYPE_ERROR,
|
||
Level::ERROR => os_log_type_t_OS_LOG_TYPE_ERROR,
|
||
};
|
||
let mut attributes = AttributeMap::default();
|
||
let mut attr_visitor = FieldVisitor::new(&mut attributes);
|
||
event.record(&mut attr_visitor);
|
||
let mut message = String::new();
|
||
if let Some(value) = attributes.remove("message") {
|
||
message = value;
|
||
message.push_str(" ");
|
||
}
|
||
message.push_str(
|
||
&attributes
|
||
.into_iter()
|
||
.map(|(k, v)| format!("{}={}", k, v))
|
||
.collect::<Vec<_>>()
|
||
.join(" "),
|
||
);
|
||
message.retain(|c| c != '\0');
|
||
let message =
|
||
CString::new(message).expect("failed to convert formatted message to a C string");
|
||
if let Some(parent_id) = ctx.current_span().id() {
|
||
let span = ctx
|
||
.span(parent_id)
|
||
.expect("invalid span, this shouldn't happen");
|
||
let mut extensions = span.extensions_mut();
|
||
let activity = extensions
|
||
.get_mut::<Activity>()
|
||
.expect("span didn't contain activity wtf");
|
||
|
||
let raw_state = [0u64; 2usize];
|
||
unsafe {
|
||
let state: os_activity_scope_state_s = std::mem::transmute(raw_state);
|
||
let state: os_activity_scope_state_t = &state as *const _ as *mut _;
|
||
os_activity_scope_enter(**activity, state);
|
||
wrapped_os_log_with_type(self.logger, level, message.as_ptr());
|
||
os_activity_scope_leave(state);
|
||
}
|
||
} else {
|
||
unsafe { wrapped_os_log_with_type(self.logger, level, message.as_ptr()) };
|
||
}
|
||
}
|
||
|
||
fn on_enter(&self, _id: &Id, _ctx: Context<S>) {}
|
||
|
||
fn on_exit(&self, _id: &Id, _ctx: Context<S>) {}
|
||
|
||
fn on_close(&self, id: Id, ctx: Context<S>) {
|
||
let span = ctx.span(&id).expect("invalid span, this shouldn't happen");
|
||
let mut extensions = span.extensions_mut();
|
||
extensions
|
||
.remove::<Activity>()
|
||
.expect("span didn't contain activity wtf");
|
||
}
|
||
}
|
||
|
||
impl Drop for OsLogger {
|
||
fn drop(&mut self) {
|
||
unsafe {
|
||
os_release(self.logger as *mut _);
|
||
}
|
||
}
|
||
}
|