workflow_panic_hook/lib.rs
1//! # `console_error_panic_hook`
2//!
3//! [<img alt="github" src="https://img.shields.io/badge/github-workflow--rs-8da0cb?style=for-the-badge&labelColor=555555&color=8da0cb&logo=github" height="20">](https://github.com/workflow-rs/workflow-rs)
4//! [<img alt="crates.io" src="https://img.shields.io/crates/v/workflow-panic-hook.svg?maxAge=2592000&style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/workflow-panic-hook)
5//! [<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-workflow--panic--hook-56c2a5?maxAge=2592000&style=for-the-badge&logo=docs.rs" height="20">](https://docs.rs/workflow-panic-hook)
6//! <img alt="license" src="https://img.shields.io/crates/l/workflow-panic-hook.svg?maxAge=2592000&color=6ac&style=for-the-badge&logoColor=fff" height="20">
7//! <img src="https://img.shields.io/badge/platform- wasm32/browser -informational?style=for-the-badge&color=50a0f0" height="20">
8//! <img src="https://img.shields.io/badge/platform- wasm32/node.js -informational?style=for-the-badge&color=50a0f0" height="20">
9//!
10//! This crate is based on [console_error_panic_hook](https://crates.io/crates/console_error_panic_hook) but
11//! provides two configuration modes - console output and full-page output, where the panic will create
12//! a full-screen DIV element dumping the stack info in it. This is useful when debugging on devices
13//! without access to console output.
14//!
15//! ## Error.stackTraceLimit
16//!
17//! Many browsers only capture the top 10 frames of a stack trace. In rust programs this is less likely to be enough. To see more frames, you can set the non-standard value `Error.stackTraceLimit`. For more information see the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Microsoft_Extensions/Error.stackTraceLimit) or [v8 docs](https://v8.dev/docs/stack-trace-api).
18//!
19
20#[macro_use]
21extern crate cfg_if;
22
23use std::panic;
24
25cfg_if! {
26 if #[cfg(target_arch = "wasm32")] {
27 extern crate wasm_bindgen;
28 use wasm_bindgen::prelude::*;
29 mod logger;
30
31 #[wasm_bindgen]
32 extern {
33 #[wasm_bindgen(js_namespace = console, js_name="error")]
34 fn console_error(msg: String);
35
36 type Error;
37
38 #[wasm_bindgen(constructor)]
39 fn new() -> Error;
40
41 #[wasm_bindgen(structural, method, getter)]
42 fn stack(error: &Error) -> String;
43 }
44
45 fn process(info: &panic::PanicInfo) -> String{
46 let mut msg = info.to_string();
47
48 // Add the error stack to our message.
49 //
50 // This ensures that even if the `console` implementation doesn't
51 // include stacks for `console.error`, the stack is still available
52 // for the user. Additionally, Firefox's console tries to clean up
53 // stack traces, and ruins Rust symbols in the process
54 // (https://bugzilla.mozilla.org/show_bug.cgi?id=1519569) but since
55 // it only touches the logged message's associated stack, and not
56 // the message's contents, by including the stack in the message
57 // contents we make sure it is available to the user.
58 msg.push_str("\n\nStack:\n\n");
59 let e = Error::new();
60 let stack = e.stack();
61 msg.push_str(&stack);
62
63 // Safari's devtools, on the other hand, _do_ mess with logged
64 // messages' contents, so we attempt to break their heuristics for
65 // doing that by appending some whitespace.
66 // https://github.com/rustwasm/console_error_panic_hook/issues/7
67 msg.push_str("\n\n");
68
69 msg
70 }
71
72
73 fn console_hook(info: &panic::PanicInfo){
74 // Finally, log the panic with `console.error`!
75 console_error(process(info));
76 }
77 fn popup_hook(info: &panic::PanicInfo){
78 // Finally, log the panic with `logger::error`!
79 logger::error(process(info));
80 }
81
82 fn init(logger_type:Type){
83 match logger_type {
84 Type::Console=>{
85 panic::set_hook(Box::new(console_hook));
86 }
87
88 Type::Popup=>{
89 logger::init_logger();
90 panic::set_hook(Box::new(popup_hook));
91 }
92 Type::Native=>{
93 panic!("Native logger not supported under wasm");
94 }
95 }
96
97 }
98 pub use logger::show_logs;
99 } else {
100 use std::io::{self, Write};
101
102 fn hook(info: &panic::PanicInfo) {
103 let _ = writeln!(io::stderr(), "{info}");
104 }
105
106 fn init(_logger_type:Type){
107 panic::set_hook(Box::new(hook));
108 }
109
110 pub fn show_logs(){
111 panic!("Native (non-WASM) platform build doesn't support panic logs");
112 }
113 }
114}
115
116pub enum Type {
117 Console,
118 Popup,
119 Native,
120}
121/// Set the `console.error` panic hook the first time this is called. Subsequent
122/// invocations do nothing.
123#[inline]
124pub fn set_once(logger_type: Type) {
125 use std::sync::Once;
126 static SET_HOOK: Once = Once::new();
127 SET_HOOK.call_once(|| init(logger_type));
128}