1#![deny(missing_docs)]
5use log::{Level, Log, Metadata, Record};
6use wasm_bindgen::prelude::*;
7use web_sys::console;
8
9pub struct Config {
11 level: Level,
12 module_prefix: Option<String>,
13 message_location: MessageLocation,
14}
15
16pub enum MessageLocation {
18 SameLine,
20 NewLine,
22}
23
24impl Default for Config {
25 fn default() -> Self {
26 Self {
27 level: Level::Debug,
28 module_prefix: None,
29 message_location: MessageLocation::SameLine,
30 }
31 }
32}
33
34impl Config {
35 pub fn new(level: Level) -> Self {
37 Self {
38 level,
39 module_prefix: None,
40 message_location: MessageLocation::SameLine,
41 }
42 }
43
44 pub fn module_prefix(mut self, module_prefix: &str) -> Self {
49 self.module_prefix = Some(module_prefix.to_string());
50 self
51 }
52
53 pub fn message_on_new_line(mut self) -> Self {
56 self.message_location = MessageLocation::NewLine;
57 self
58 }
59}
60
61struct Style {
63 lvl_trace: String,
64 lvl_debug: String,
65 lvl_info: String,
66 lvl_warn: String,
67 lvl_error: String,
68 tgt: String,
69 args: String,
70}
71
72impl Style {
73 fn new() -> Style {
74 let base = String::from("color: white; padding: 0 3px; background:");
75 Style {
76 lvl_trace: format!("{} gray;", base),
77 lvl_debug: format!("{} blue;", base),
78 lvl_info: format!("{} green;", base),
79 lvl_warn: format!("{} orange;", base),
80 lvl_error: format!("{} darkred;", base),
81 tgt: String::from("font-weight: bold; color: inherit"),
82 args: String::from("background: inherit; color: inherit"),
83 }
84 }
85}
86
87struct WasmLogger {
89 config: Config,
90 style: Style,
91}
92
93impl Log for WasmLogger {
94 fn enabled(&self, metadata: &Metadata<'_>) -> bool {
95 if let Some(ref prefix) = self.config.module_prefix {
96 metadata.target().starts_with(prefix)
97 } else {
98 true
99 }
100 }
101
102 fn log(&self, record: &Record<'_>) {
103 if self.enabled(record.metadata()) {
104 let style = &self.style;
105 let message_separator = match self.config.message_location {
106 MessageLocation::NewLine => "\n",
107 MessageLocation::SameLine => " ",
108 };
109 let s = format!(
110 "%c{}%c {}:{}%c{}{}",
111 record.level(),
112 record.file().unwrap_or_else(|| record.target()),
113 record
114 .line()
115 .map_or_else(|| "[Unknown]".to_string(), |line| line.to_string()),
116 message_separator,
117 record.args(),
118 );
119 let s = JsValue::from_str(&s);
120 let tgt_style = JsValue::from_str(&style.tgt);
121 let args_style = JsValue::from_str(&style.args);
122
123 match record.level() {
124 Level::Trace => console::debug_4(
125 &s,
126 &JsValue::from(&style.lvl_trace),
127 &tgt_style,
128 &args_style,
129 ),
130 Level::Debug => console::log_4(
131 &s,
132 &JsValue::from(&style.lvl_debug),
133 &tgt_style,
134 &args_style,
135 ),
136 Level::Info => {
137 console::info_4(&s, &JsValue::from(&style.lvl_info), &tgt_style, &args_style)
138 }
139 Level::Warn => {
140 console::warn_4(&s, &JsValue::from(&style.lvl_warn), &tgt_style, &args_style)
141 }
142 Level::Error => console::error_4(
143 &s,
144 &JsValue::from(&style.lvl_error),
145 &tgt_style,
146 &args_style,
147 ),
148 }
149 }
150 }
151
152 fn flush(&self) {}
153}
154
155pub fn init(config: Config) {
166 let max_level = config.level;
167 let wl = WasmLogger {
168 config,
169 style: Style::new(),
170 };
171
172 match log::set_boxed_logger(Box::new(wl)) {
173 Ok(_) => log::set_max_level(max_level.to_level_filter()),
174 Err(e) => console::error_1(&JsValue::from(e.to_string())),
175 }
176}