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.message.unwrap_or_default();
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}
82
83impl Visit for MessageVisitor {
84 fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
85 if field.name() == "message" {
86 self.message = Some(format!("{:?}", value));
87 } else if self.message.is_none() {
88 self.message = Some(format!("{:?}", value));
90 }
91 }
92
93 fn record_str(&mut self, field: &Field, value: &str) {
94 if field.name() == "message" || self.message.is_none() {
96 self.message = Some(value.to_string());
97 }
98 }
99}
100
101pub fn init_logging() {
107 use once_cell::sync::OnceCell;
108 use tracing_subscriber::filter::LevelFilter;
109 use tracing_subscriber::prelude::*;
110 use tracing_subscriber::reload;
111
112 static INITIALIZED: OnceCell<()> = OnceCell::new();
114
115 INITIALIZED.get_or_init(|| {
116 let layer = FfiLoggingLayer::new();
117
118 let initial_level = LogCallbackManager::global().level();
120 let initial_filter = match initial_level {
121 LogLevel::Trace => LevelFilter::TRACE,
122 LogLevel::Debug => LevelFilter::DEBUG,
123 LogLevel::Info => LevelFilter::INFO,
124 LogLevel::Warn => LevelFilter::WARN,
125 LogLevel::Error => LevelFilter::ERROR,
126 LogLevel::Off => LevelFilter::OFF,
127 };
128
129 let (filter, reload_handle) = reload::Layer::new(initial_filter);
130
131 crate::reload::ReloadHandle::global().set_handle(reload_handle);
133
134 let subscriber = tracing_subscriber::registry().with(filter).with(layer);
136
137 let _ = tracing::subscriber::set_global_default(subscriber);
139 });
140}
141
142#[allow(dead_code)] pub fn init_logging_with_level(level: LogLevel) {
145 LogCallbackManager::global().set_level(level);
146 init_logging();
147}
148
149#[cfg(test)]
150#[path = "layer/layer_tests.rs"]
151mod layer_tests;