console_log/lib.rs
1#![deny(missing_docs)]
2#![doc(html_root_url = "https://docs.rs/console_log/1.0.0")]
3
4//! A logger that logs to the browser's console.
5//!
6//! # Example
7//!
8//! ```rust,no_run
9//! use log::Level;
10//! use log::info;
11//! fn main() {
12//! console_log::init_with_level(Level::Debug);
13//!
14//! info!("It works!");
15//!
16//! // ...
17//! }
18//! ```
19//!
20//! # Log Levels
21//!
22//! Rust's log levels map to the browser's console log in the following way.
23//!
24//! | Rust | Web Console |
25//! |------------|-------------------|
26//! | `trace!()` | `console.debug()` |
27//! | `debug!()` | `console.log()` |
28//! | `info!()` | `console.info()` |
29//! | `warn!()` | `console.warn()` |
30//! | `error!()` | `console.error()` |
31//!
32//! # Getting Fancy
33//!
34//! The feature set provided by this crate is intentionally very basic. If you need more flexible
35//! formatting of log messages (timestamps, file and line info, etc.) this crate can be used with
36//! the [`fern`] logger via the [`console_log::log`] function.
37//!
38//! ## Colors
39//!
40//! The `"color"` feature adds styling to the log messages.
41//!
42//! `Cargo.toml`
43//! ```toml
44//! console_log = { version = "0.2", features = ["color"] }
45//! ```
46//!
47//! The styled log messages will be rendered as follows:
48//!
49//! 
50//!
51//! # Code Size
52//!
53//! [Twiggy] reports this library adding about 180Kb to the size of a minimal wasm binary in a
54//! debug build. If you want to avoid this, mark the library as optional and conditionally
55//! initialize it in your code for non-release builds.
56//!
57//! `Cargo.toml`
58//! ```toml
59//! [dependencies]
60//! cfg-if = "0.1"
61//! log = "0.4"
62//! console_log = { version = "0.2", optional = true }
63//!
64//! [features]
65//! default = ["console_log"]
66//! ```
67//!
68//! `lib.rs`
69//! ```rust,ignore
70//! use wasm_bindgen::prelude::*;
71//! use cfg_if::cfg_if;
72//!
73//! cfg_if! {
74//! if #[cfg(feature = "console_log")] {
75//! fn init_log() {
76//! use log::Level;
77//! console_log::init_with_level(Level::Trace).expect("error initializing log");
78//! }
79//! } else {
80//! fn init_log() {}
81//! }
82//! }
83//!
84//! #[wasm_bindgen]
85//! pub fn main() {
86//! init_log();
87//! // ...
88//! }
89//! ```
90//!
91//! # Limitations
92//!
93//! The file and line number information associated with the log messages reports locations from
94//! the shims generated by `wasm-bindgen`, not the location of the logger call.
95//!
96//! [Twiggy]: https://github.com/rustwasm/twiggy
97//! [`console_log::log`]: fn.log.html
98//! [`fern`]: https://docs.rs/fern
99
100use log::{Level, Log, Metadata, Record, SetLoggerError};
101use web_sys::console;
102
103#[cfg(feature = "color")]
104use wasm_bindgen::JsValue;
105
106#[cfg(feature = "color")]
107const STYLE: style::Style<'static> = style::Style::default();
108
109#[cfg(feature = "color")]
110#[doc(hidden)]
111mod style;
112
113static LOGGER: WebConsoleLogger = WebConsoleLogger {};
114
115struct WebConsoleLogger {}
116
117impl Log for WebConsoleLogger {
118 #[inline]
119 fn enabled(&self, metadata: &Metadata) -> bool {
120 metadata.level() <= log::max_level()
121 }
122
123 fn log(&self, record: &Record) {
124 if !self.enabled(record.metadata()) {
125 return;
126 }
127
128 log(record);
129 }
130
131 fn flush(&self) {}
132}
133
134/// Print a `log::Record` to the browser's console at the appropriate level.
135///
136/// This function is useful for integrating with the [`fern`](https://crates.io/crates/fern) logger
137/// crate.
138///
139/// ## Example
140/// ```rust,ignore
141/// fern::Dispatch::new()
142/// .chain(fern::Output::call(console_log::log))
143/// .apply()?;
144/// ```
145#[cfg_attr(not(feature = "color"), inline)]
146pub fn log(record: &Record) {
147 #[cfg(not(feature = "color"))]
148 {
149 // pick the console.log() variant for the appropriate logging level
150 let console_log = match record.level() {
151 Level::Error => console::error_1,
152 Level::Warn => console::warn_1,
153 Level::Info => console::info_1,
154 Level::Debug => console::log_1,
155 Level::Trace => console::debug_1,
156 };
157
158 console_log(&format!("{}", record.args()).into());
159 }
160
161 #[cfg(feature = "color")]
162 {
163 // pick the console.log() variant for the appropriate logging level
164 let console_log = match record.level() {
165 Level::Error => console::error_4,
166 Level::Warn => console::warn_4,
167 Level::Info => console::info_4,
168 Level::Debug => console::log_4,
169 Level::Trace => console::debug_4,
170 };
171
172 let message = {
173 let message = format!(
174 "%c{level}%c {file}:{line} %c\n{text}",
175 level = record.level(),
176 file = record.file().unwrap_or_else(|| record.target()),
177 line = record
178 .line()
179 .map_or_else(|| "[Unknown]".to_string(), |line| line.to_string()),
180 text = record.args(),
181 );
182 JsValue::from(&message)
183 };
184
185 let level_style = {
186 let style_str = match record.level() {
187 Level::Trace => STYLE.trace,
188 Level::Debug => STYLE.debug,
189 Level::Info => STYLE.info,
190 Level::Warn => STYLE.warn,
191 Level::Error => STYLE.error,
192 };
193
194 JsValue::from(style_str)
195 };
196
197 let file_line_style = JsValue::from_str(STYLE.file_line);
198 let text_style = JsValue::from_str(STYLE.text);
199 console_log(&message, &level_style, &file_line_style, &text_style);
200 }
201}
202
203/// Initializes the global logger setting `max_log_level` to the given value.
204///
205/// ## Example
206///
207/// ```
208/// use log::Level;
209/// fn main() {
210/// console_log::init_with_level(Level::Debug).expect("error initializing logger");
211/// }
212/// ```
213#[inline]
214pub fn init_with_level(level: Level) -> Result<(), SetLoggerError> {
215 log::set_logger(&LOGGER)?;
216 log::set_max_level(level.to_level_filter());
217 Ok(())
218}
219
220/// Initializes the global logger with `max_log_level` set to `Level::Info` (a sensible default).
221///
222/// ## Example
223///
224/// ```
225/// fn main() {
226/// console_log::init().expect("error initializing logger");
227/// }
228/// ```
229#[inline]
230pub fn init() -> Result<(), SetLoggerError> {
231 init_with_level(Level::Info)
232}