rustbridge_logging/
layer.rs1use crate::callback::LogCallbackManager;
4use rustbridge_core::LogLevel;
5use tracing::field::{Field, Visit};
6use tracing::{Event, Level, Subscriber};
7use tracing_subscriber::Layer;
8use tracing_subscriber::layer::Context;
9use tracing_subscriber::registry::LookupSpan;
10
11pub struct FfiLoggingLayer {
13 manager: &'static LogCallbackManager,
14}
15
16impl FfiLoggingLayer {
17 pub fn new() -> Self {
19 Self {
20 manager: LogCallbackManager::global(),
21 }
22 }
23
24 pub fn with_manager(manager: &'static LogCallbackManager) -> Self {
26 Self { manager }
27 }
28
29 fn convert_level(level: &Level) -> LogLevel {
31 match *level {
32 Level::TRACE => LogLevel::Trace,
33 Level::DEBUG => LogLevel::Debug,
34 Level::INFO => LogLevel::Info,
35 Level::WARN => LogLevel::Warn,
36 Level::ERROR => LogLevel::Error,
37 }
38 }
39}
40
41impl Default for FfiLoggingLayer {
42 fn default() -> Self {
43 Self::new()
44 }
45}
46
47impl<S> Layer<S> for FfiLoggingLayer
48where
49 S: Subscriber + for<'a> LookupSpan<'a>,
50{
51 fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
52 let metadata = event.metadata();
53 let level = Self::convert_level(metadata.level());
54
55 if !self.manager.is_enabled(level) {
57 return;
58 }
59
60 let mut visitor = MessageVisitor::default();
62 event.record(&mut visitor);
63
64 let message = visitor.into_message();
65 let target = metadata.target();
66
67 self.manager.log(level, target, &message);
69 }
70
71 fn enabled(&self, metadata: &tracing::Metadata<'_>, _ctx: Context<'_, S>) -> bool {
72 let level = Self::convert_level(metadata.level());
73 self.manager.is_enabled(level)
74 }
75}
76
77#[derive(Default)]
79struct MessageVisitor {
80 message: Option<String>,
81 fields: Vec<(String, String)>,
82}
83
84impl Visit for MessageVisitor {
85 fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
86 let name = field.name();
87 if name == "message" {
88 self.message = Some(format!("{:?}", value));
89 } else {
90 self.fields.push((name.to_string(), format!("{:?}", value)));
92 }
93 }
94
95 fn record_str(&mut self, field: &Field, value: &str) {
96 let name = field.name();
97 if name == "message" {
98 self.message = Some(value.to_string());
99 } else {
100 self.fields.push((name.to_string(), value.to_string()));
102 }
103 }
104
105 fn record_i64(&mut self, field: &Field, value: i64) {
106 let name = field.name();
107 if name != "message" {
108 self.fields.push((name.to_string(), value.to_string()));
109 }
110 }
111
112 fn record_u64(&mut self, field: &Field, value: u64) {
113 let name = field.name();
114 if name != "message" {
115 self.fields.push((name.to_string(), value.to_string()));
116 }
117 }
118
119 fn record_bool(&mut self, field: &Field, value: bool) {
120 let name = field.name();
121 if name != "message" {
122 self.fields.push((name.to_string(), value.to_string()));
123 }
124 }
125}
126
127impl MessageVisitor {
128 fn into_message(self) -> String {
130 let mut result = self.message.unwrap_or_default();
131
132 if !self.fields.is_empty() {
134 if !result.is_empty() {
135 result.push(' ');
136 }
137 for (i, (key, value)) in self.fields.iter().enumerate() {
138 if i > 0 {
139 result.push(' ');
140 }
141 result.push_str(&format!("{}={}", key, value));
142 }
143 }
144
145 result
146 }
147}
148
149pub fn init_logging() {
155 use once_cell::sync::OnceCell;
156 use tracing_subscriber::filter::LevelFilter;
157 use tracing_subscriber::prelude::*;
158 use tracing_subscriber::reload;
159
160 static INITIALIZED: OnceCell<()> = OnceCell::new();
162
163 INITIALIZED.get_or_init(|| {
164 let layer = FfiLoggingLayer::new();
165
166 let initial_level = LogCallbackManager::global().level();
168 let initial_filter = match initial_level {
169 LogLevel::Trace => LevelFilter::TRACE,
170 LogLevel::Debug => LevelFilter::DEBUG,
171 LogLevel::Info => LevelFilter::INFO,
172 LogLevel::Warn => LevelFilter::WARN,
173 LogLevel::Error => LevelFilter::ERROR,
174 LogLevel::Off => LevelFilter::OFF,
175 };
176
177 let (filter, reload_handle) = reload::Layer::new(initial_filter);
178
179 crate::reload::ReloadHandle::global().set_handle(reload_handle);
181
182 let subscriber = tracing_subscriber::registry().with(filter).with(layer);
184
185 let _ = tracing::subscriber::set_global_default(subscriber);
187 });
188}
189
190#[allow(dead_code)] pub fn init_logging_with_level(level: LogLevel) {
193 LogCallbackManager::global().set_level(level);
194 init_logging();
195}
196
197#[cfg(test)]
198#[path = "layer/layer_tests.rs"]
199mod layer_tests;