tracing_subscriber_wasm/lib.rs
1#![deny(missing_docs)]
2
3//! A [`MakeWriter`] implementation to allow directly using
4//! [`tracing_subscriber`] in the browser or with NodeJS.
5//!
6//! The [`MakeConsoleWriter`] allows mapping arbitrary trace events to
7//! any other console verbosity level. Check out the
8//! [`MakeConsoleWriter::map_trace_level_to`] and similar methods when
9//! building the writer.
10//!
11//! ### Important Note
12//! In my testing, if you don't call `.without_time` on the
13//! subscriber builder, a runtime exception will be raised.
14//!
15//! # Example
16//! ```rust
17//! use tracing_subscriber::fmt;
18//! use tracing_subscriber_wasm::MakeConsoleWriter;
19//!
20//! fmt()
21//! .with_writer(
22//! // To avoide trace events in the browser from showing their
23//! // JS backtrace, which is very annoying, in my opinion
24//! MakeConsoleWriter::default().map_trace_level_to(tracing::Level::DEBUG),
25//! )
26//! // For some reason, if we don't do this in the browser, we get
27//! // a runtime error.
28//! .without_time()
29//! .init();
30//! ```
31
32use std::io::{
33 self,
34 Write,
35};
36use tracing::instrument::WithSubscriber;
37use tracing_subscriber::fmt::MakeWriter;
38
39/// This is the main type that is passed to `with_writer`.
40///
41/// Note that you can map the tracing levels to different console
42/// verbosity levels. This is especially useful for mapping
43/// [`tracing::Level::TRACE`] to a different console level,
44/// such as `debug`, to avoid all `trace` tracing events
45/// showing up in the browser's console with their backtrace
46/// expanded.
47#[derive(Clone, Copy, Debug, Default)]
48pub struct MakeConsoleWriter(MappedLevels);
49
50impl From<MappedLevels> for MakeConsoleWriter {
51 fn from(mapped_levels: MappedLevels) -> Self {
52 Self::from_mapped_levels(mapped_levels)
53 }
54}
55
56impl MakeConsoleWriter {
57 /// Creates a new [`MakeConsoleWriter`] with the default [`MappedLevels`].
58 pub fn new() -> Self {
59 Self::default()
60 }
61
62 /// Creates a new [`MakeConsoleWriter`] with a custom [`MappedLevels`].
63 pub fn from_mapped_levels(mapped_levels: MappedLevels) -> Self {
64 Self(mapped_levels)
65 }
66
67 /// Maps the [`tracing::Level::TRACE`] to another console level.
68 pub fn map_trace_level_to(mut self, level: tracing::Level) -> Self {
69 self.0.trace = level;
70
71 self
72 }
73
74 /// Maps the [`tracing::Level::DEBUG`] to another console level.
75 pub fn map_debug_level_to(mut self, level: tracing::Level) -> Self {
76 self.0.debug = level;
77
78 self
79 }
80
81 /// Maps the [`tracing::Level::INFO`] to another console level.
82 pub fn map_info_level_to(mut self, level: tracing::Level) -> Self {
83 self.0.info = level;
84
85 self
86 }
87
88 /// Maps the [`tracing::Level::WARN`] to another console level.
89 pub fn map_warn_level_to(mut self, level: tracing::Level) -> Self {
90 self.0.warn = level;
91
92 self
93 }
94
95 /// Maps the [`tracing::Level::ERROR`] to another console level.
96 pub fn map_error_level_to(mut self, level: tracing::Level) -> Self {
97 self.0.error = level;
98
99 self
100 }
101}
102
103impl<'a> MakeWriter<'a> for MakeConsoleWriter {
104 type Writer = ConsoleWriter;
105
106 fn make_writer(&'a self) -> Self::Writer {
107 unimplemented!("use make_writer_for instead");
108 }
109
110 fn make_writer_for(&'a self, meta: &tracing::Metadata<'_>) -> Self::Writer {
111 ConsoleWriter(*meta.level(), Vec::with_capacity(256))
112 }
113}
114
115/// Allows mapping [`tracing::Level`] events to a different
116/// console level.
117#[derive(Clone, Copy, Debug)]
118pub struct MappedLevels {
119 /// The verbosity level [`tracing::Level::TRACE`] events should be mapped to
120 /// in the console.
121 pub trace: tracing::Level,
122 /// The verbosity level [`tracing::Level::DEBUG`] events should be mapped to
123 /// in the console.
124 pub debug: tracing::Level,
125 /// The verbosity level [`tracing::Level::INFO`] events should be mapped to
126 /// in the console.
127 pub info: tracing::Level,
128 /// The verbosity level [`tracing::Level::WARN`] events should be mapped to
129 /// in the console.
130 pub warn: tracing::Level,
131 /// The verbosity level [`tracing::Level::ERROR`] events should be mapped to
132 /// in the console.
133 pub error: tracing::Level,
134}
135
136impl Default for MappedLevels {
137 fn default() -> Self {
138 Self {
139 trace: tracing::Level::TRACE,
140 debug: tracing::Level::DEBUG,
141 info: tracing::Level::INFO,
142 warn: tracing::Level::WARN,
143 error: tracing::Level::ERROR,
144 }
145 }
146}
147
148/// The type which is responsible for actually writing the tracing
149/// event out to the console.
150pub struct ConsoleWriter(tracing::Level, Vec<u8>);
151
152impl io::Write for ConsoleWriter {
153 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
154 self.1.write(buf)
155 }
156
157 fn flush(&mut self) -> io::Result<()> {
158 use gloo::console;
159 use tracing::Level;
160
161 let data = std::str::from_utf8(&self.1).map_err(|_| {
162 io::Error::new(io::ErrorKind::InvalidData, "data not UTF-8")
163 })?;
164
165 match self.0 {
166 Level::TRACE => console::debug!(data),
167 Level::DEBUG => console::debug!(data),
168 Level::INFO => console::log!(data),
169 Level::WARN => console::warn!(data),
170 Level::ERROR => console::error!(data),
171 }
172
173 Ok(())
174 }
175}
176
177impl Drop for ConsoleWriter {
178 fn drop(&mut self) {
179 let _ = self.flush();
180 }
181}