1use std::cell::RefCell;
2use std::ffi::{c_char, CString};
3use std::fmt::{Debug, Write};
4
5use tracing::field::{Field, Visit};
6use tracing::{subscriber::DefaultGuard, Event, Level, Subscriber};
7use tracing_subscriber::filter::{EnvFilter, LevelFilter};
8use tracing_subscriber::{prelude::*, registry::Registry, Layer};
9
10use crate::macros::*;
11
12#[derive(Debug, Clone)]
13#[repr(C)]
14pub enum LogLevel {
15 Off,
16 Trace,
17 Debug,
18 Info,
19 Warn,
20 Error,
21}
22
23impl<'a> From<&'a Level> for LogLevel {
24 fn from(level: &'a Level) -> Self {
25 match *level {
26 Level::TRACE => Self::Trace,
27 Level::DEBUG => Self::Debug,
28 Level::INFO => Self::Info,
29 Level::WARN => Self::Warn,
30 Level::ERROR => Self::Error,
31 }
32 }
33}
34
35impl From<LogLevel> for LevelFilter {
36 fn from(level: LogLevel) -> Self {
37 match level {
38 LogLevel::Off => LevelFilter::OFF,
39 LogLevel::Trace => LevelFilter::TRACE,
40 LogLevel::Debug => LevelFilter::DEBUG,
41 LogLevel::Info => LevelFilter::INFO,
42 LogLevel::Warn => LevelFilter::WARN,
43 LogLevel::Error => LevelFilter::ERROR,
44 }
45 }
46}
47
48#[repr(C)]
49pub struct PkgcraftLog {
50 message: *mut c_char,
51 level: LogLevel,
52}
53
54impl<'a> From<&'a Event<'_>> for PkgcraftLog {
55 fn from(event: &'a Event<'_>) -> Self {
56 let mut message = String::new();
57 let mut visitor = MessageVisitor { message: &mut message };
58 event.record(&mut visitor);
59
60 Self {
61 message: try_ptr_from_str!(message),
62 level: event.metadata().level().into(),
63 }
64 }
65}
66
67impl Drop for PkgcraftLog {
68 fn drop(&mut self) {
69 unsafe {
70 drop(CString::from_raw(self.message));
71 }
72 }
73}
74
75#[no_mangle]
80pub unsafe extern "C" fn pkgcraft_log_free(l: *mut PkgcraftLog) {
81 if !l.is_null() {
82 unsafe { drop(Box::from_raw(l)) };
83 }
84}
85
86type LogCallback = extern "C" fn(*mut PkgcraftLog);
87
88pub struct PkgcraftLayer {
89 log_cb: LogCallback,
90}
91
92impl PkgcraftLayer {
93 fn new(log_cb: LogCallback) -> Self {
94 Self { log_cb }
95 }
96}
97
98struct MessageVisitor<'a> {
99 message: &'a mut String,
100}
101
102impl<'a> Visit for MessageVisitor<'a> {
103 fn record_debug(&mut self, field: &Field, value: &dyn Debug) {
104 if field.name() == "message" {
105 write!(self.message, "{value:?}").unwrap();
106 }
107 }
108}
109
110impl<S> Layer<S> for PkgcraftLayer
111where
112 S: Subscriber,
113{
114 fn on_event(&self, event: &Event<'_>, _ctx: tracing_subscriber::layer::Context<'_, S>) {
115 (self.log_cb)(Box::into_raw(Box::new(event.into())));
116 }
117}
118
119thread_local! {
120 static SUBSCRIBER: RefCell<Option<DefaultGuard>> = const { RefCell::new(None) };
121}
122
123#[no_mangle]
125pub extern "C" fn pkgcraft_logging_enable(cb: LogCallback, level: LogLevel) {
126 let level_filter: LevelFilter = level.into();
127 let filter = EnvFilter::builder()
128 .with_default_directive(level_filter.into())
129 .from_env_lossy();
130
131 let subscriber = Registry::default()
132 .with(filter)
133 .with(PkgcraftLayer::new(cb));
134
135 SUBSCRIBER.with(|prev| *prev.borrow_mut() = None);
137 let guard = tracing::subscriber::set_default(subscriber);
138 SUBSCRIBER.with(|prev| *prev.borrow_mut() = Some(guard));
139}
140
141#[no_mangle]
146pub unsafe extern "C" fn pkgcraft_log_test(msg: *const c_char, level: LogLevel) {
147 let message = try_str_from_ptr!(msg);
148 use LogLevel::*;
149 match level {
150 Off => (),
151 Trace => tracing::trace!("{message}"),
152 Debug => tracing::debug!("{message}"),
153 Info => tracing::info!("{message}"),
154 Warn => tracing::warn!("{message}"),
155 Error => tracing::error!("{message}"),
156 }
157}