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}