1use std::fmt;
3use std::panic::Location;
4use std::panic::PanicHookInfo;
5
6use backtrace::Backtrace;
7use clap::ValueEnum;
8use tracing::Level;
9use tracing_log::LogTracer;
10use tracing_subscriber::layer::SubscriberExt;
11use tracing_subscriber::Registry;
12
13#[cfg(feature = "browser")]
14pub use self::browser::init_logging;
15#[cfg(feature = "node")]
16pub use self::node::init_logging;
17use crate::prelude::wasm_export;
18
19#[repr(C)]
20#[wasm_export]
21#[derive(ValueEnum, Debug, Clone)]
22pub enum LogLevel {
23 Debug,
24 Info,
25 Warn,
26 Error,
27 Trace,
28}
29
30impl From<LogLevel> for Level {
31 fn from(val: LogLevel) -> Self {
32 match val {
33 LogLevel::Trace => Level::TRACE,
34 LogLevel::Debug => Level::DEBUG,
35 LogLevel::Info => Level::INFO,
36 LogLevel::Warn => Level::WARN,
37 LogLevel::Error => Level::ERROR,
38 }
39 }
40}
41
42impl std::str::FromStr for LogLevel {
43 type Err = crate::error::Error;
44 fn from_str(s: &str) -> Result<Self, Self::Err> {
45 match s.to_uppercase().as_str() {
46 "TRACE" => Ok(LogLevel::Trace),
47 "DEBUG" => Ok(LogLevel::Debug),
48 "INFO" => Ok(LogLevel::Info),
49 "WARN" => Ok(LogLevel::Warn),
50 "ERROR" => Ok(LogLevel::Error),
51 x => Err(crate::error::Error::InvalidLoggingLevel(x.to_string())),
52 }
53 }
54}
55
56#[derive(Debug, Clone)]
58pub struct PanicLocation {
59 file: String,
60 line: String,
61 column: String,
62}
63
64impl<'a, T> From<T> for PanicLocation
65where T: Into<Location<'a>>
66{
67 fn from(lo: T) -> Self {
68 let lo: Location = lo.into();
69 Self {
70 file: lo.file().to_string(),
71 line: lo.line().to_string(),
72 column: lo.file().to_string(),
73 }
74 }
75}
76
77#[derive(Debug, Clone)]
79pub struct PanicData<'a> {
80 message: &'a PanicHookInfo<'a>,
81 backtrace: String,
82 location: Option<PanicLocation>,
83}
84
85impl<'a, T> From<T> for PanicData<'a>
86where T: Into<&'a PanicHookInfo<'a>>
87{
88 fn from(panic: T) -> PanicData<'a> {
89 let panic = panic.into();
90 let backtrace = Backtrace::new();
91 let backtrace = format!("{backtrace:?}");
92 let location: Option<PanicLocation> = panic.location().map(|l| PanicLocation::from(*l));
93 PanicData {
94 message: panic,
95 backtrace,
96 location,
97 }
98 }
99}
100
101impl fmt::Display for PanicLocation {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 write!(f, "{}:{}:{}", self.file, self.line, self.column)
104 }
105}
106
107impl<'a> fmt::Display for PanicData<'a> {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 match &self.location {
110 Some(l) => write!(f, "{}, {} \n\n {}", self.message, l, self.backtrace),
111 None => write!(f, "{} \n\n {}", self.message, self.backtrace),
112 }
113 }
114}
115
116fn log_panic(panic: &PanicHookInfo) {
117 let data: PanicData = panic.into();
118 tracing::error!("{}", data)
119}
120
121pub fn set_panic_hook() {
123 std::panic::set_hook(Box::new(|panic| {
130 log_panic(panic);
131 }));
132}
133
134#[cfg(feature = "node")]
135pub mod node {
137 use tracing_subscriber::filter;
138 use tracing_subscriber::fmt;
139 use tracing_subscriber::Layer;
140
141 use super::*;
142
143 #[no_mangle]
144 pub extern "C" fn init_logging(level: LogLevel) {
145 set_panic_hook();
146
147 let subscriber = Registry::default();
148 let level_filter = filter::LevelFilter::from_level(level.into());
149
150 let subscriber = subscriber.with(
152 fmt::layer()
153 .with_writer(std::io::stderr)
154 .with_filter(level_filter),
155 );
156 let _ = LogTracer::init();
159
160 let _ = tracing::subscriber::set_global_default(subscriber);
162 }
163}
164
165#[cfg(feature = "browser")]
166pub mod browser {
167 use tracing_wasm::ConsoleConfig;
168 use tracing_wasm::WASMLayer;
169 use tracing_wasm::WASMLayerConfigBuilder;
170
171 use super::*;
172 #[wasm_export]
173 pub fn init_logging(level: LogLevel) {
174 set_panic_hook();
175
176 let subscriber = Registry::default();
177
178 let subscriber = subscriber.with(WASMLayer::new(
180 WASMLayerConfigBuilder::new()
181 .set_max_level(level.into())
182 .set_console_config(ConsoleConfig::ReportWithoutConsoleColor)
183 .build(),
184 ));
185
186 let _ = LogTracer::init();
189
190 let _ = tracing::subscriber::set_global_default(subscriber);
192 }
193}