memflow/plugins/
logger.rs1use crate::cglue::{
4 ext::{DisplayBaseRef, DisplayRef},
5 COption, CSliceRef, Opaquable,
6};
7
8use log::{Level, LevelFilter, SetLoggerError};
9
10use core::ffi::c_void;
11
12use std::sync::atomic::{AtomicPtr, Ordering};
13
14#[repr(C)]
16pub struct Metadata<'a> {
17 level: Level,
18 target: CSliceRef<'a, u8>,
19}
20
21#[repr(C)]
23pub struct Record<'a> {
24 metadata: Metadata<'a>,
25 message: DisplayRef<'a>,
26 module_path: COption<CSliceRef<'a, u8>>,
27 file: COption<CSliceRef<'a, u8>>,
28 line: COption<u32>,
29 }
32
33#[repr(C)]
36pub struct PluginLogger {
37 max_level: LevelFilter,
38 enabled: extern "C" fn(metadata: &Metadata) -> bool,
39 log: extern "C" fn(record: &Record) -> (),
40 flush: extern "C" fn() -> (),
41 on_level_change: AtomicPtr<c_void>,
42}
43
44impl PluginLogger {
45 pub fn new() -> Self {
52 Self {
53 max_level: log::max_level(),
54 enabled: mf_log_enabled,
55 log: mf_log_log,
56 flush: mf_log_flush,
57 on_level_change: AtomicPtr::new(std::ptr::null_mut()),
58 }
59 }
60
61 pub fn init(&'static self) -> Result<(), SetLoggerError> {
68 let val: SetMaxLevelFn = mf_log_set_max_level;
70 self.on_level_change
71 .store(val as *const c_void as *mut c_void, Ordering::SeqCst);
72 log::set_max_level(self.max_level);
73 log::set_logger(self)?;
74 Ok(())
75 }
76
77 pub fn on_level_change(&self, new_level: LevelFilter) {
79 let val = self.on_level_change.load(Ordering::Relaxed);
80 if let Some(on_change) =
81 unsafe { std::mem::transmute::<*mut c_void, Option<SetMaxLevelFn>>(val) }
82 {
83 on_change(new_level);
84 }
85 }
86}
87
88impl Default for PluginLogger {
89 fn default() -> Self {
90 PluginLogger::new()
91 }
92}
93
94fn display_obj<'a, T: 'a + core::fmt::Display>(obj: &'a T) -> DisplayRef<'a> {
95 let obj: DisplayBaseRef<T> = From::from(obj);
96 obj.into_opaque()
97}
98
99impl log::Log for PluginLogger {
100 fn enabled(&self, metadata: &log::Metadata) -> bool {
101 let m = Metadata {
102 level: metadata.level(),
103 target: metadata.target().into(),
104 };
105 (self.enabled)(&m)
106 }
107
108 fn log(&self, record: &log::Record) {
109 let message = display_obj(record.args());
110 let r = Record {
111 metadata: Metadata {
112 level: record.metadata().level(),
113 target: record.metadata().target().into(),
114 },
115 message,
116 module_path: record.module_path().map(|s| s.into()).into(),
117 file: record.file().map(|s| s.into()).into(),
118 line: record.line().into(),
119 };
120 (self.log)(&r)
121 }
122
123 fn flush(&self) {
124 (self.flush)()
125 }
126}
127
128type SetMaxLevelFn = extern "C" fn(LevelFilter);
129
130extern "C" fn mf_log_set_max_level(level: LevelFilter) {
132 log::set_max_level(level);
133}
134
135extern "C" fn mf_log_enabled(metadata: &Metadata) -> bool {
137 log::logger().enabled(
138 &log::Metadata::builder()
139 .level(metadata.level)
140 .target(unsafe { metadata.target.into_str() })
141 .build(),
142 )
143}
144
145extern "C" fn mf_log_log(record: &Record) {
147 log::logger().log(
148 &log::Record::builder()
149 .metadata(
150 log::Metadata::builder()
151 .level(record.metadata.level)
152 .target(unsafe { record.metadata.target.into_str() })
153 .build(),
154 )
155 .args(format_args!("{}", record.message))
156 .module_path(match &record.module_path {
157 COption::Some(s) => Some(unsafe { s.into_str() }),
158 COption::None => None,
159 })
160 .file(match &record.file {
161 COption::Some(s) => Some(unsafe { s.into_str() }),
162 COption::None => None,
163 })
164 .line(match &record.line {
165 COption::Some(l) => Some(*l),
166 COption::None => None,
167 })
168 .build(),
169 )
170}
171
172extern "C" fn mf_log_flush() {
174 log::logger().flush()
175}