@@ -115,6 +115,17 @@ pub use subscription::{EventSubscription, LifecycleSubscription};
115115const MIN_PROTOCOL_VERSION : u32 = 3 ;
116116const RUNTIME_SHUTDOWN_TIMEOUT : Duration = Duration :: from_secs ( 10 ) ;
117117
118+ fn record_optional_millis ( span : & tracing:: Span , field : & ' static str , value : Option < u64 > ) {
119+ match value {
120+ Some ( value) => {
121+ span. record ( field, value) ;
122+ }
123+ None => {
124+ span. record ( field, "None" ) ;
125+ }
126+ }
127+ }
128+
118129/// How the SDK communicates with the CLI server.
119130#[ derive( Debug , Default ) ]
120131#[ non_exhaustive]
@@ -1367,25 +1378,29 @@ impl Client {
13671378 ) ;
13681379 }
13691380 timings. total_ms = StartupTimings :: millis ( start_time. elapsed ( ) ) ;
1370- // Single structured event with the full per-phase breakdown, so hosts
1371- // can attribute startup latency to a phase without stitching together
1372- // the individual debug lines above.
1373- debug ! (
1374- program_resolve_ms = timings. program_resolve_ms. unwrap_or_default( ) ,
1375- program_resolve_present = timings. program_resolve_ms. is_some( ) ,
1376- process_spawn_ms = timings. process_spawn_ms. unwrap_or_default( ) ,
1377- process_spawn_present = timings. process_spawn_ms. is_some( ) ,
1378- port_wait_ms = timings. port_wait_ms. unwrap_or_default( ) ,
1379- port_wait_present = timings. port_wait_ms. is_some( ) ,
1381+ // A span allows optional fields to retain their numeric type when
1382+ // present while recording an explicit "None" when a phase did not run.
1383+ let timings_span = tracing:: debug_span!(
1384+ "Client::start timings" ,
1385+ program_resolve_ms = tracing:: field:: Empty ,
1386+ process_spawn_ms = tracing:: field:: Empty ,
1387+ port_wait_ms = tracing:: field:: Empty ,
13801388 transport_setup_ms = timings. transport_setup_ms,
13811389 handshake_ms = timings. handshake_ms,
1382- session_fs_ms = timings. session_fs_ms. unwrap_or_default( ) ,
1383- session_fs_present = timings. session_fs_ms. is_some( ) ,
1384- llm_handler_ms = timings. llm_handler_ms. unwrap_or_default( ) ,
1385- llm_handler_present = timings. llm_handler_ms. is_some( ) ,
1390+ session_fs_ms = tracing:: field:: Empty ,
1391+ llm_handler_ms = tracing:: field:: Empty ,
13861392 total_ms = timings. total_ms,
1387- "Client::start timings"
13881393 ) ;
1394+ record_optional_millis (
1395+ & timings_span,
1396+ "program_resolve_ms" ,
1397+ timings. program_resolve_ms ,
1398+ ) ;
1399+ record_optional_millis ( & timings_span, "process_spawn_ms" , timings. process_spawn_ms ) ;
1400+ record_optional_millis ( & timings_span, "port_wait_ms" , timings. port_wait_ms ) ;
1401+ record_optional_millis ( & timings_span, "session_fs_ms" , timings. session_fs_ms ) ;
1402+ record_optional_millis ( & timings_span, "llm_handler_ms" , timings. llm_handler_ms ) ;
1403+ timings_span. in_scope ( || debug ! ( "Client::start timings" ) ) ;
13891404 let _ = client. inner . startup_timings . set ( timings) ;
13901405 debug ! (
13911406 elapsed_ms = start_time. elapsed( ) . as_millis( ) ,
0 commit comments