tracing_browser_subscriber/
lib.rs1use core::default::Default;
15use core::fmt::Write;
16
17use tracing;
18use tracing::span;
19use tracing_subscriber::layer::{Context, Layer};
20use tracing_subscriber::prelude::__tracing_subscriber_SubscriberExt;
21use tracing_subscriber::registry::{LookupSpan, Registry};
22
23mod console;
24use console::Recorder;
25use wasm_bindgen::UnwrapThrowExt;
26
27mod typings;
28
29pub struct BrowserLayer {
30 record_timings: bool,
31 max_level: tracing::Level,
32 }
34
35impl Default for BrowserLayer {
36 fn default() -> Self {
37 Self {
38 record_timings: true,
39 max_level: tracing::Level::INFO,
40 }
41 }
42}
43
44impl BrowserLayer {
45 pub fn new() -> Self {
46 Self::default()
47 }
48
49 pub fn with_max_level(mut self, level: tracing::Level) -> Self {
50 self.max_level = level;
51 self
52 }
53
54 pub fn with_record_timings(mut self, record_timings: bool) -> Self {
55 self.record_timings = record_timings;
56 self
57 }
58
59 pub fn write_for_level(
60 &self,
61 level: &tracing::Level,
62 origin: String,
63 context: Option<Vec<String>>,
64 recorder: Recorder,
65 ) {
66 let f: fn(String) = match *level {
67 tracing::Level::ERROR => typings::error,
68 tracing::Level::WARN => typings::warn,
69 tracing::Level::INFO => typings::info,
70 tracing::Level::DEBUG => typings::debug,
71 tracing::Level::TRACE => typings::trace,
72 };
73 let spans = context
74 .map(|v| {
75 let s: String = v.concat();
76 s
77 })
78 .unwrap_or_else(|| String::new());
79 f(format!("{} {} {} {}", level, origin, recorder, spans));
80 }
81}
82
83fn format_mark(name: Option<&str>, id: &tracing::Id) -> String {
84 let mut buffer = String::new();
85 if let Some(n) = name {
86 write!(buffer, "[{}]", n).unwrap_throw();
87 }
88 write!(buffer, "{:x}", id.into_u64()).unwrap_throw();
89 buffer
90}
91
92impl<S: tracing::Subscriber + for<'a> LookupSpan<'a>> Layer<S> for BrowserLayer {
93 fn enabled(&self, metadata: &tracing::Metadata<'_>, _ctx: Context<'_, S>) -> bool {
94 metadata.level() <= &self.max_level
95 }
96
97 fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
98 let mut recorder = Recorder::new();
99 attrs.record(&mut recorder);
100 if let Some(span_ref) = ctx.span(id) {
101 span_ref.extensions_mut().insert(recorder);
102 }
103 }
104
105 fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) {
106 if let Some(span_ref) = ctx.span(span) {
107 if let Some(recorder) = span_ref.extensions_mut().get_mut::<Recorder>() {
108 values.record(recorder)
109 }
110 }
111 }
112
113 fn on_event(&self, event: &tracing::Event<'_>, ctx: Context<'_, S>) {
114 let mut recorder = Recorder::new();
115 event.record(&mut recorder);
116 let metadata = event.metadata();
117 let recorders = ctx.current_span().id().and_then(|id| {
118 ctx.span_scope(id).map(|scope| {
119 let span_details: Vec<String> = scope
120 .from_root()
121 .map(|span_ref| {
122 format!(
123 "{} {}",
124 span_ref.name(),
125 span_ref
126 .extensions()
127 .get::<Recorder>()
128 .expect("Unregistered Span")
129 )
130 })
131 .collect();
132 span_details
133 })
134 });
135 self.write_for_level(
136 metadata.level(),
137 metadata
138 .file()
139 .and_then(|file| metadata.line().map(|ln| format!("{}:{}", file, ln)))
140 .unwrap_or_default(),
141 recorders,
142 recorder,
143 );
144 }
145
146 fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
147 if self.record_timings {
148 if let Some(span_ref) = ctx.span(id) {
149 typings::mark(&format_mark(Some(span_ref.metadata().name()), id));
150 } else {
151 typings::mark(&format_mark(None, id));
152 }
153 }
154 }
155
156 fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) {
157 if self.record_timings {
158 if let Some(span_ref) = ctx.span(id) {
159 let metadata = span_ref.metadata();
160 if let Some(recorder) = span_ref.extensions_mut().get_mut::<Recorder>() {
161 typings::measure(
162 format!(
163 "[{}] {} {}",
164 metadata.name(),
165 metadata.module_path().unwrap_or("..."),
166 recorder
167 ),
168 format_mark(Some(span_ref.metadata().name()), id),
169 )
170 .unwrap_throw();
171 } else {
172 typings::measure(
173 format!(
174 "[{}] {}",
175 metadata.name(),
176 metadata.module_path().unwrap_or("...")
177 ),
178 format_mark(None, id),
179 )
180 .unwrap_throw();
181 }
182 }
183 }
184 }
185}
186
187pub fn configure_as_global_default() {
188 tracing::subscriber::set_global_default(
189 Registry::default().with(BrowserLayer::new().with_max_level(tracing::Level::DEBUG)),
190 )
191 .expect("default global");
192}
193#[cfg(test)]
194mod tests;