use std::sync::OnceLock;
use crate::error_monitoring::wire::{auth_header, envelope, ingest_url, parse_dsn};
pub fn init(dsn: Option<&str>, environment: &str) {
let config = dsn.filter(|value| !value.is_empty()).map(|value| BrowserConfig {
dsn: value.to_string(),
environment: environment.to_string(),
});
let _ = CONFIG.set(config);
install_panic_hook();
}
pub fn report_error(message: &str) {
let Some(Some(config)) = CONFIG.get() else {
return;
};
let Some(dsn) = parse_dsn(&config.dsn) else {
return;
};
let url = ingest_url(&dsn);
let auth = auth_header(&dsn);
let body = envelope(&config.environment, &new_event_id(), message);
wasm_bindgen_futures::spawn_local(async move {
let _ = reqwest::Client::new()
.post(url)
.header("X-Sentry-Auth", auth)
.header("Content-Type", "application/x-sentry-envelope")
.body(body)
.send()
.await;
});
}
struct BrowserConfig {
dsn: String,
environment: String,
}
static CONFIG: OnceLock<Option<BrowserConfig>> = OnceLock::new();
fn install_panic_hook() {
use std::sync::Once;
static HOOK: Once = Once::new();
HOOK.call_once(|| {
let previous = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
report_error(&info.to_string());
previous(info);
}));
});
}
fn new_event_id() -> String {
fn chunk() -> u32 {
(js_sys::Math::random() * f64::from(u32::MAX)) as u32
}
format!("{:08x}{:08x}{:08x}{:08x}", chunk(), chunk(), chunk(), chunk())
}