1#[cfg(not(target_arch = "wasm32"))]
2use log::error;
3
4use crate::config::{Config, LogLevel};
5use std::collections::HashMap;
6
7#[cfg(target_arch = "wasm32")]
8use env_logger::filter::Builder;
9#[cfg(target_arch = "wasm32")]
10use env_logger::filter::Filter;
11use log::LevelFilter;
12#[cfg(target_arch = "wasm32")]
13use log::{Level, Log, Metadata, Record};
14#[cfg(target_arch = "wasm32")]
15use wasm_bindgen::prelude::*;
16#[cfg(target_arch = "wasm32")]
17use web_sys::console;
18
19#[cfg(target_arch = "wasm32")]
21pub struct WasmLogConfig {
22 filter: Filter,
23 message_location: MessageLocation,
24}
25
26#[cfg(target_arch = "wasm32")]
28pub enum MessageLocation {
29 SameLine,
31 NewLine,
33}
34
35#[cfg(target_arch = "wasm32")]
36impl Default for WasmLogConfig {
37 fn default() -> Self {
38 let mut builder = Builder::new();
39 Self {
40 filter: builder.build(),
42 message_location: MessageLocation::SameLine,
43 }
44 }
45}
46
47#[cfg(target_arch = "wasm32")]
48impl WasmLogConfig {
49 pub fn new(filter: Filter) -> Self {
51 Self {
52 filter,
54 message_location: MessageLocation::SameLine,
55 }
56 }
57 #[allow(clippy::return_self_not_must_use)]
60 pub fn message_on_new_line(mut self) -> Self {
61 self.message_location = MessageLocation::NewLine;
62 self
63 }
64}
65
66#[cfg(target_arch = "wasm32")]
68struct Style {
69 lvl_trace: String,
70 lvl_debug: String,
71 lvl_info: String,
72 lvl_warn: String,
73 lvl_error: String,
74 tgt: String,
75 args: String,
76}
77
78#[cfg(target_arch = "wasm32")]
79impl Style {
80 fn new() -> Style {
81 let base = String::from("color: white; padding: 0 3px; background:");
82 Style {
83 lvl_trace: format!("{base} gray;"),
84 lvl_debug: format!("{base} blue;"),
85 lvl_info: format!("{base} green;"),
86 lvl_warn: format!("{base} orange;"),
87 lvl_error: format!("{base} darkred;"),
88 tgt: String::from("font-weight: bold; color: inherit"),
89 args: String::from("background: inherit; color: inherit"),
90 }
91 }
92}
93
94#[cfg(target_arch = "wasm32")]
96struct WasmLogger {
97 config: WasmLogConfig,
98 style: Style,
99}
100
101#[cfg(target_arch = "wasm32")]
102impl Log for WasmLogger {
103 fn enabled(&self, metadata: &Metadata<'_>) -> bool {
104 self.config.filter.enabled(metadata)
105 }
106
107 fn log(&self, record: &Record<'_>) {
108 if self.enabled(record.metadata()) {
109 let style = &self.style;
110 let message_separator = match self.config.message_location {
111 MessageLocation::NewLine => "\n",
112 MessageLocation::SameLine => " ",
113 };
114 let s = format!(
115 "%c{}%c {}:{}%c{}{}",
116 record.level(),
117 record.file().unwrap_or_else(|| record.target()),
118 record.line().map_or_else(|| "[Unknown]".to_string(), |line| line.to_string()),
119 message_separator,
120 record.args(),
121 );
122 let s = JsValue::from_str(&s);
123 let tgt_style = JsValue::from_str(&style.tgt);
124 let args_style = JsValue::from_str(&style.args);
125
126 match record.level() {
127 Level::Trace => console::debug_4(&s, &JsValue::from(&style.lvl_trace), &tgt_style, &args_style),
128 Level::Debug => console::log_4(&s, &JsValue::from(&style.lvl_debug), &tgt_style, &args_style),
129 Level::Info => console::info_4(&s, &JsValue::from(&style.lvl_info), &tgt_style, &args_style),
130 Level::Warn => console::warn_4(&s, &JsValue::from(&style.lvl_warn), &tgt_style, &args_style),
131 Level::Error => console::error_4(&s, &JsValue::from(&style.lvl_error), &tgt_style, &args_style),
132 }
133 }
134 }
135
136 fn flush(&self) {}
137}
138#[cfg(target_arch = "wasm32")]
139
140pub fn init(config: WasmLogConfig) {
141 match try_init(config) {
142 Ok(_) => {}
143 Err(e) => console::error_1(&JsValue::from(e.to_string())),
144 }
145}
146
147#[cfg(target_arch = "wasm32")]
148pub fn try_init(config: WasmLogConfig) -> Result<(), log::SetLoggerError> {
149 let max_level = config.filter.filter();
150 let wl = WasmLogger { config, style: Style::new() };
151
152 match log::set_boxed_logger(Box::new(wl)) {
153 Ok(_) => {
154 log::set_max_level(max_level);
155 Ok(())
156 }
157 Err(e) => Err(e),
158 }
159}
160
161#[derive(Clone)]
162pub struct LogLevelCaps {
163 pub caps: HashMap<String, LogLevel>,
164}
165impl Default for LogLevelCaps {
166 fn default() -> Self {
167 let config = Config::default();
168 Self {
169 caps: config.core.log_level_caps,
170 }
171 }
172}
173
174pub fn gloss_setup_logger_from_config_file(config_path: Option<&str>) {
175 let config = Config::new(config_path);
176 gloss_setup_logger_from_config(&config);
177}
178
179pub fn gloss_setup_logger_from_config(config: &Config) {
180 gloss_setup_logger(
181 config.core.log_level,
182 Some(LogLevelCaps {
183 caps: config.core.log_level_caps.clone(),
184 }),
185 );
186}
187
188pub fn gloss_setup_logger(log_level: LogLevel, log_level_caps: Option<LogLevelCaps>) {
190 let lvl_filter_default: LevelFilter = log_level.into();
191
192 let log_level_caps = log_level_caps.unwrap_or_default();
193
194 cfg_if::cfg_if! {
195 if #[cfg(target_arch = "wasm32")] {
196 let mut builder = env_logger::filter::Builder::new();
197 builder.filter(None, lvl_filter_default); for (module_name, log_lvl) in log_level_caps.caps.iter(){
200 let lvl_filter = <crate::config::LogLevel as Into<LevelFilter>>::into(*log_lvl);
201 let log_lvl_least_verbose = lvl_filter_default.min(lvl_filter);
202 builder.filter_module(module_name, log_lvl_least_verbose);
203 }
204 init(WasmLogConfig::new(builder.build()));
205 } else {
206 let mut builder = env_logger::Builder::from_default_env();
209 builder.format( gloss_utils::logging::format );
210 builder.filter(None, lvl_filter_default); for (module_name, log_lvl) in log_level_caps.caps.iter(){
213 let lvl_filter = <crate::config::LogLevel as Into<LevelFilter>>::into(*log_lvl);
214 let log_lvl_least_verbose = lvl_filter_default.min(lvl_filter);
215 builder.filter_module(module_name, log_lvl_least_verbose);
216 }
217 if let Err(err) = builder.try_init(){
218 error!("Error: {err}.
219 If you have auto_\"auto_create_logger=false\" in your config file, please make sure you are calling gloss_setup_logger() once per process.
220 If you have auto_\"auto_create_logger=true\" in your config file, the gloss_setup_logger() is called automatically so please make sure you are creating the Viewer() only once per proces.");
221 }
222
223 }
224 }
225}