@@ -31,6 +31,7 @@ pub const CONTROL_PLANE_SCHEMA: u32 = 1;
3131pub const REDACTED : & str = "[redacted]" ;
3232
3333static PANIC_REPORTED : AtomicBool = AtomicBool :: new ( false ) ;
34+ static PANIC_PAYLOAD_REPORTING_ENABLED : AtomicBool = AtomicBool :: new ( true ) ;
3435static STDERR_WRITE_LOCK : Mutex < ( ) > = Mutex :: new ( ( ) ) ;
3536
3637/// Baseline exit-code classes shared by ADR-T-010 command-line tools.
@@ -116,11 +117,13 @@ pub enum ControlPlaneFields {
116117 UsageError { exit_code : u8 , clap_error_kind : String } ,
117118 /// Details for stdout TTY refusal.
118119 TtyRefusal { exit_code : u8 , stream : StandardStream } ,
119- /// Details for a panic diagnostic. The panic payload is deliberately omitted .
120+ /// Details for a panic diagnostic. The panic payload is only exposed when debug diagnostics are enabled .
120121 Panic {
121122 exit_code : u8 ,
122123 thread : Option < String > ,
123124 location : Option < String > ,
125+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
126+ payload : Option < String > ,
124127 } ,
125128}
126129
@@ -207,7 +210,7 @@ impl ControlPlaneRecord {
207210
208211 /// Build a panic diagnostic record.
209212 #[ must_use]
210- pub fn panic ( command : & str , thread : Option < & str > , location : Option < & str > ) -> Self {
213+ pub fn panic ( command : & str , thread : Option < & str > , location : Option < & str > , payload : Option < & str > ) -> Self {
211214 Self :: new (
212215 command,
213216 ControlPlaneRecordKind :: Panic ,
@@ -216,6 +219,7 @@ impl ControlPlaneRecord {
216219 exit_code : CommandExit :: Failure . code ( ) ,
217220 thread : thread. map ( str:: to_string) ,
218221 location : location. map ( str:: to_string) ,
222+ payload : payload. map ( str:: to_string) ,
219223 } ) ,
220224 )
221225 }
@@ -421,6 +425,9 @@ fn level_directive(level: tracing::Level) -> String {
421425///
422426/// Returns [`CliExit`] when parsing should stop and the caller should write the
423427/// enclosed record to stderr before exiting with the enclosed exit class.
428+ /// The error value is intentionally returned by value because this path is only
429+ /// used when parsing stops, and boxing it would complicate the public helper API.
430+ #[ allow( clippy:: result_large_err) ]
424431pub fn parse_args_from < T , I , A > ( args : I ) -> Result < T , CliExit >
425432where
426433 T : Parser ,
@@ -531,14 +538,41 @@ pub fn install_json_panic_hook(command_name: &str) {
531538 let location = panic_info
532539 . location ( )
533540 . map ( |location| format ! ( "{}:{}:{}" , location. file( ) , location. line( ) , location. column( ) ) ) ;
534- let record = ControlPlaneRecord :: panic ( & command_name, thread_name, location. as_deref ( ) ) ;
541+ let payload = panic_payload_reporting_enabled ( )
542+ . then ( || panic_payload_message ( panic_info) )
543+ . flatten ( ) ;
544+ let record = ControlPlaneRecord :: panic ( & command_name, thread_name, location. as_deref ( ) , payload) ;
535545 let _ignored = try_emit_control_plane_record ( & record) ;
536546 }
537547
538548 exit_with ( CommandExit :: Failure ) ;
539549 } ) ) ;
540550}
541551
552+ /// Enable or disable string panic payloads in JSON panic diagnostics.
553+ ///
554+ /// Payload reporting starts enabled so panics before argument parsing still
555+ /// include their string payload. Call this with the parsed `--debug` value once
556+ /// arguments are available.
557+ pub fn set_panic_payload_reporting_enabled ( enabled : bool ) {
558+ PANIC_PAYLOAD_REPORTING_ENABLED . store ( enabled, Ordering :: SeqCst ) ;
559+ }
560+
561+ fn panic_payload_reporting_enabled ( ) -> bool {
562+ PANIC_PAYLOAD_REPORTING_ENABLED . load ( Ordering :: SeqCst )
563+ }
564+
565+ fn panic_payload_message < ' a > ( info : & ' a std:: panic:: PanicHookInfo < ' _ > ) -> Option < & ' a str > {
566+ panic_payload_message_from_payload ( info. payload ( ) )
567+ }
568+
569+ fn panic_payload_message_from_payload ( payload : & ( dyn std:: any:: Any + Send ) ) -> Option < & str > {
570+ payload
571+ . downcast_ref :: < & str > ( )
572+ . copied ( )
573+ . or_else ( || payload. downcast_ref :: < String > ( ) . map ( String :: as_str) )
574+ }
575+
542576/// Exit the current process with an ADR-T-010 exit class.
543577#[ allow( clippy:: exit) ]
544578pub fn exit_with ( exit : CommandExit ) -> ! {
@@ -633,6 +667,7 @@ where
633667 CommandError : std:: fmt:: Display ,
634668 Run : FnOnce ( ) -> Result < Output , CommandError > ,
635669{
670+ set_panic_payload_reporting_enabled ( debug) ;
636671 install_json_panic_hook ( command_name) ;
637672 init_json_tracing_with_debug ( debug, default_level) ;
638673
@@ -671,6 +706,7 @@ where
671706 CommandError : std:: fmt:: Display ,
672707 Run : FnOnce ( ) -> Result < ( ) , CommandError > ,
673708{
709+ set_panic_payload_reporting_enabled ( debug) ;
674710 install_json_panic_hook ( command_name) ;
675711 init_json_tracing_with_debug ( debug, default_level) ;
676712
@@ -699,6 +735,7 @@ where
699735 Run : FnOnce ( ) -> RunFuture ,
700736 RunFuture : Future < Output = Result < ( ) , CommandError > > ,
701737{
738+ set_panic_payload_reporting_enabled ( debug) ;
702739 install_json_panic_hook ( command_name) ;
703740 init_json_tracing_with_debug ( debug, default_level) ;
704741
0 commit comments