gloss_renderer/
logger.rs

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/// Specify what to be logged
20#[cfg(target_arch = "wasm32")]
21pub struct WasmLogConfig {
22    filter: Filter,
23    message_location: MessageLocation,
24}
25
26/// Specify where the message will be logged.
27#[cfg(target_arch = "wasm32")]
28pub enum MessageLocation {
29    /// The message will be on the same line as other info (level, path...)
30    SameLine,
31    /// The message will be on its own line, a new after other info.
32    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            // level: Level::Debug,
41            filter: builder.build(),
42            message_location: MessageLocation::SameLine,
43        }
44    }
45}
46
47#[cfg(target_arch = "wasm32")]
48impl WasmLogConfig {
49    /// Specify the maximum level you want to log
50    pub fn new(filter: Filter) -> Self {
51        Self {
52            // level,
53            filter,
54            message_location: MessageLocation::SameLine,
55        }
56    }
57    /// Put the message on a new line, separated from other information
58    /// such as level, file path, line number.
59    #[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/// The log styles
67#[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/// The logger
95#[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
188// If there is no level caps provided we use the default level caps
189pub 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); //by default everything info and above will be printed
198            // Apply filters
199            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            // Setup logger with output for fileline and color
207            // https://stackoverflow.com/a/65084368
208            let mut builder =  env_logger::Builder::from_default_env();
209            builder.format( gloss_utils::logging::format );
210            builder.filter(None, lvl_filter_default); //by default everything info and above will be printed
211            // Apply filters
212            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}