use std::{
collections::VecDeque,
sync::{
atomic::{AtomicU8, Ordering},
Mutex, Once,
},
};
use log::{Level, LevelFilter, Log, Metadata, Record};
const MAX_CAPTURED_RECORDS: usize = 4_000;
const TRIM_RECORDS: usize = 400;
#[derive(Debug)]
pub(crate) struct CapturedLog {
pub(crate) level: Level,
pub(crate) target: String,
pub(crate) message: String,
}
struct NebulusLogger {
level: AtomicU8,
captured: Mutex<VecDeque<CapturedLog>>,
}
impl NebulusLogger {
const fn new() -> Self {
Self {
level: AtomicU8::new(LevelFilter::Info as u8),
captured: Mutex::new(VecDeque::new()),
}
}
}
impl Log for NebulusLogger {
fn enabled(&self, metadata: &Metadata<'_>) -> bool {
metadata.level() as u8 <= self.level.load(Ordering::Relaxed)
}
fn log(&self, record: &Record<'_>) {
if !self.enabled(record.metadata()) {
return;
}
let message = record.args().to_string();
platform_output(record.level(), record.target(), &message);
if record.target().starts_with("egui") {
return;
}
let mut captured = self.captured.lock().expect("log capture poisoned");
if captured.len() >= MAX_CAPTURED_RECORDS {
captured.drain(..TRIM_RECORDS);
}
captured.push_back(CapturedLog {
level: record.level(),
target: record.target().to_owned(),
message,
});
}
fn flush(&self) {}
}
static LOGGER: NebulusLogger = NebulusLogger::new();
static INIT: Once = Once::new();
pub(crate) fn init() {
INIT.call_once(|| {
if log::set_logger(&LOGGER).is_ok() {
log::set_max_level(LevelFilter::Trace);
}
});
}
pub(crate) fn set_level(level: LevelFilter) {
LOGGER.level.store(level as u8, Ordering::Relaxed);
}
pub(crate) fn drain() -> Vec<CapturedLog> {
LOGGER
.captured
.lock()
.expect("log capture poisoned")
.drain(..)
.collect()
}
#[cfg(not(target_arch = "wasm32"))]
fn platform_output(level: Level, target: &str, message: &str) {
eprintln!("{level:<5} {target}: {message}");
}
#[cfg(target_arch = "wasm32")]
fn platform_output(level: Level, target: &str, message: &str) {
use wasm_bindgen::JsValue;
let message = JsValue::from_str(&format!("{level:<5} {target}: {message}"));
match level {
Level::Error => web_sys::console::error_1(&message),
Level::Warn => web_sys::console::warn_1(&message),
Level::Info => web_sys::console::info_1(&message),
Level::Debug | Level::Trace => web_sys::console::debug_1(&message),
}
}