@@ -15,10 +15,19 @@ use std::future::Future;
1515use std:: ops:: Deref ;
1616use std:: panic;
1717
18+ /// A struct to store the message and backtrace from a panic.
19+ #[ derive( Clone ) ]
20+ pub struct PanicInfo {
21+ /// The message from the panic.
22+ pub message : String ,
23+ /// The backtrace from the panic.
24+ pub backtrace : String ,
25+ }
26+
1827tokio:: task_local! {
1928 /// A task_local variable to store the backtrace from a panic, so it can be
2029 /// retrieved later.
21- static BACKTRACE : RefCell <Option <String >>;
30+ static BACKTRACE : RefCell <Option <PanicInfo >>;
2231}
2332
2433/// Call this from the main method of your application, and use it in conjunction
@@ -33,9 +42,23 @@ pub fn set_panic_hook() {
3342 || "unavailable" . to_owned ( ) ,
3443 |loc : & panic:: Location < ' _ > | format ! ( "{}:{}:{}" , loc. file( ) , loc. line( ) , loc. column( ) ) ,
3544 ) ;
45+ // Extract the panic message from the payload
46+ let panic_msg = if let Some ( & s) = info. payload ( ) . downcast_ref :: < & str > ( ) {
47+ s. to_string ( )
48+ } else if let Some ( s) = info. payload ( ) . downcast_ref :: < String > ( ) {
49+ s. clone ( )
50+ } else {
51+ "panic message cannot be downcasted" . to_string ( )
52+ } ;
53+
54+ tracing:: error!( "stacktrace" =%backtrace, "panic at {loc}: {panic_msg}" ) ;
55+
3656 let _result = BACKTRACE . try_with ( |entry| match entry. try_borrow_mut ( ) {
3757 Ok ( mut entry_ref) => {
38- * entry_ref = Some ( format ! ( "panicked at {loc}\n {backtrace}" ) ) ;
58+ * entry_ref = Some ( PanicInfo {
59+ message : panic_msg,
60+ backtrace : format ! ( "panicked at {loc}\n {backtrace}" ) ,
61+ } ) ;
3962 }
4063 Err ( borrow_mut_error) => {
4164 eprintln ! (
@@ -44,7 +67,6 @@ pub fn set_panic_hook() {
4467 ) ;
4568 }
4669 } ) ;
47- tracing:: error!( "stacktrace" =%backtrace, "panic at {loc}" ) ;
4870
4971 // Execute the default hood to preserve the default behavior.
5072 prev ( info) ;
@@ -62,11 +84,11 @@ where
6284
6385/// Take the backtrace from the task_local variable, and reset the task_local to
6486/// None. Return error if the backtrace is not stored, or cannot be retrieved.
65- pub fn take_panic_backtrace ( ) -> Result < String , anyhow:: Error > {
87+ pub fn take_panic_backtrace ( ) -> Result < PanicInfo , anyhow:: Error > {
6688 BACKTRACE . try_with ( |entry| {
6789 entry. try_borrow_mut ( ) . map ( |mut entry_ref| {
6890 let result = match entry_ref. deref ( ) {
69- Some ( bt) => Ok ( bt. to_string ( ) ) ,
91+ Some ( bt) => Ok ( bt. clone ( ) ) ,
7092 None => Err ( anyhow:: anyhow!( "nothing is stored in task_local" ) ) ,
7193 } ;
7294 // Clear the task_local because the backtrace has been retrieve.
@@ -143,11 +165,9 @@ mod tests {
143165 . await ;
144166 assert ! ( result. is_err( ) ) ;
145167 if backtrace_captured {
146- assert ! (
147- take_panic_backtrace( )
148- . unwrap( )
149- . contains( "verify_inner_panic" )
150- ) ;
168+ let info = take_panic_backtrace ( ) . unwrap ( ) ;
169+ assert_eq ! ( info. message, "wow!" ) ;
170+ assert ! ( info. backtrace. contains( "verify_inner_panic" ) ) ;
151171 } else {
152172 assert ! ( take_panic_backtrace( ) . is_err( ) ) ;
153173 }
@@ -169,11 +189,9 @@ mod tests {
169189 assert ! ( result. is_ok( ) ) ;
170190
171191 // Verify the outer task can get its own backtrace.
172- assert ! (
173- take_panic_backtrace( )
174- . unwrap( )
175- . contains( "test_nested_tasks" )
176- ) ;
192+ let info = take_panic_backtrace ( ) . unwrap ( ) ;
193+ assert_eq ! ( info. message, "boom!" ) ;
194+ assert ! ( info. backtrace. contains( "test_nested_tasks" ) ) ;
177195 } )
178196 . await ;
179197 }
0 commit comments