1use abi_stable::{sabi_trait, std_types::RBox};
18use log::{Log, Metadata, Record};
19use log_types::{RLevelFilter, RMetadata, RRecord};
20
21pub type BoxDynLogTarget = LogTarget_TO<'static, RBox<()>>;
23
24#[sabi_trait]
26pub trait LogTarget: Send + Sync {
27 fn enabled(&self, metadata: RMetadata<'_>) -> bool;
28 fn log(&self, record: RRecord<'_>);
29 fn flush(&self);
30 fn max_level(&self) -> RLevelFilter;
31}
32
33pub struct StaticLogTarget;
36
37impl StaticLogTarget {
38 pub fn get() -> BoxDynLogTarget {
39 BoxDynLogTarget::from_value(Self, sabi_trait::TD_Opaque)
40 }
41}
42
43impl LogTarget for StaticLogTarget {
44 fn enabled(&self, metadata: RMetadata<'_>) -> bool {
45 log::logger().enabled(&metadata.into())
46 }
47
48 fn log(&self, record: RRecord<'_>) {
49 record.log(log::logger());
50 }
51
52 fn flush(&self) {
53 log::logger().flush();
54 }
55
56 fn max_level(&self) -> RLevelFilter {
57 log::max_level().into()
58 }
59}
60
61pub struct NoOpLogTarget;
63
64impl NoOpLogTarget {
65 pub fn get() -> BoxDynLogTarget {
66 BoxDynLogTarget::from_value(Self, sabi_trait::TD_Opaque)
67 }
68}
69
70impl LogTarget for NoOpLogTarget {
71 fn enabled(&self, _metadata: RMetadata<'_>) -> bool {
72 false
73 }
74
75 fn log(&self, _record: RRecord<'_>) {}
76
77 fn flush(&self) {}
78
79 fn max_level(&self) -> RLevelFilter {
80 RLevelFilter::Off
81 }
82}
83
84pub struct ForwardLogger {
86 target: BoxDynLogTarget,
87}
88
89impl ForwardLogger {
90 pub fn register(target: BoxDynLogTarget, crate_name: &'static str) {
93 log::set_max_level(target.max_level().into());
94 log::set_logger(Box::leak(Box::new(Self { target }))).expect("logger must only be registered once per plugin");
95
96 log::trace!("initialized log forwarding for dynamic library '{crate_name}'");
97 }
98}
99
100impl Log for ForwardLogger {
101 fn enabled(&self, metadata: &Metadata<'_>) -> bool {
102 self.target.enabled(metadata.into())
103 }
104
105 fn log(&self, record: &Record<'_>) {
106 self.target.log(record.into());
107 }
108
109 fn flush(&self) {
110 self.target.flush();
111 }
112}
113
114mod log_types {
116 use abi_stable::StableAbi;
117 use abi_stable::std_types::{ROption, RStr, RString};
118 use abi_stable::traits::{IntoReprC, IntoReprRust};
119 use log::{Level, LevelFilter, Log, Metadata, Record, RecordBuilder};
120 use std::fmt::Arguments;
121
122 #[repr(C)]
124 #[derive(StableAbi)]
125 pub struct RMetadata<'a> {
126 level: RLevel,
127 target: RStr<'a>,
128 }
129
130 impl<'a> From<&Metadata<'a>> for RMetadata<'a> {
131 fn from(value: &Metadata<'a>) -> Self {
132 Self {
133 level: value.level().into(),
134 target: value.target().into_c(),
135 }
136 }
137 }
138
139 impl<'a> From<RMetadata<'a>> for Metadata<'a> {
140 fn from(value: RMetadata<'a>) -> Self {
141 Metadata::builder()
142 .level(value.level.into())
143 .target(value.target.as_str())
144 .build()
145 }
146 }
147
148 #[repr(u8)]
150 #[derive(StableAbi, Clone, Copy)]
151 pub enum RLevelFilter {
152 Off,
153 Error,
154 Warn,
155 Info,
156 Debug,
157 Trace,
158 }
159
160 impl From<LevelFilter> for RLevelFilter {
161 fn from(value: LevelFilter) -> Self {
162 match value {
163 LevelFilter::Off => Self::Off,
164 LevelFilter::Error => Self::Error,
165 LevelFilter::Warn => Self::Warn,
166 LevelFilter::Info => Self::Info,
167 LevelFilter::Debug => Self::Debug,
168 LevelFilter::Trace => Self::Trace,
169 }
170 }
171 }
172
173 impl From<RLevelFilter> for LevelFilter {
174 fn from(value: RLevelFilter) -> Self {
175 match value {
176 RLevelFilter::Off => Self::Off,
177 RLevelFilter::Error => Self::Error,
178 RLevelFilter::Warn => Self::Warn,
179 RLevelFilter::Info => Self::Info,
180 RLevelFilter::Debug => Self::Debug,
181 RLevelFilter::Trace => Self::Trace,
182 }
183 }
184 }
185
186 #[repr(u8)]
188 #[derive(StableAbi, Clone, Copy)]
189 pub enum RLevel {
190 Error,
191 Warn,
192 Info,
193 Debug,
194 Trace,
195 }
196
197 impl From<Level> for RLevel {
198 fn from(value: Level) -> Self {
199 match value {
200 Level::Error => Self::Error,
201 Level::Warn => Self::Warn,
202 Level::Info => Self::Info,
203 Level::Debug => Self::Debug,
204 Level::Trace => Self::Trace,
205 }
206 }
207 }
208
209 impl From<RLevel> for Level {
210 fn from(value: RLevel) -> Self {
211 match value {
212 RLevel::Error => Self::Error,
213 RLevel::Warn => Self::Warn,
214 RLevel::Info => Self::Info,
215 RLevel::Debug => Self::Debug,
216 RLevel::Trace => Self::Trace,
217 }
218 }
219 }
220
221 #[repr(C)]
223 #[derive(StableAbi)]
224 pub struct RRecord<'a> {
225 args: RString,
228 metadata: RMetadata<'a>,
229 module_path: ROption<RStr<'a>>,
230 file: ROption<RStr<'a>>,
231 line: ROption<u32>,
232 }
233
234 impl<'a> From<&'a Record<'a>> for RRecord<'a> {
235 fn from(value: &'a Record<'a>) -> Self {
236 Self {
237 args: value.args().to_string().into_c(),
238 metadata: value.metadata().into(),
239 module_path: value.module_path().map(IntoReprC::into_c).into_c(),
240 file: value.file().map(IntoReprC::into_c).into_c(),
241 line: value.line().into_c(),
242 }
243 }
244 }
245
246 impl RRecord<'_> {
247 pub fn log(self, logger: &dyn Log) {
248 fn inner<'a>(logger: &dyn Log, args: Arguments<'a>, mut builder: RecordBuilder<'a>) {
249 logger.log(&builder.args(args).build());
250 }
251
252 let mut builder = Record::builder();
253
254 builder
255 .metadata(self.metadata.into())
256 .module_path(self.module_path.map(IntoReprRust::into_rust).into_rust())
257 .file(self.file.map(IntoReprRust::into_rust).into_rust())
258 .line(self.line.into_rust());
259
260 inner(logger, format_args!("{}", self.args), builder);
262 }
263 }
264}