@@ -268,6 +268,7 @@ fn setup_tracing(profiling_enabled: bool) -> Option<TracingGuard> {
268268 use std:: io:: BufWriter ;
269269 use std:: sync:: atomic:: { AtomicU32 , Ordering } ;
270270
271+ use bootstrap:: STEP_NAME_TARGET ;
271272 use chrono:: { DateTime , Utc } ;
272273 use tracing:: field:: { Field , Visit } ;
273274 use tracing:: { Event , Id , Level , Subscriber } ;
@@ -277,18 +278,40 @@ fn setup_tracing(profiling_enabled: bool) -> Option<TracingGuard> {
277278
278279 let filter = EnvFilter :: from_env ( "BOOTSTRAP_TRACING" ) ;
279280
281+ /// Visitor that extracts both known and unknown field values from events and spans.
280282 #[ derive( Default ) ]
281283 struct FieldValues {
284+ /// Main event message
282285 message : Option < String > ,
286+ /// Name of a recorded psna
287+ step_name : Option < String > ,
288+ /// The rest of arbitrary event/span fields
283289 fields : Vec < ( & ' static str , String ) > ,
284290 }
285291
286292 impl Visit for FieldValues {
293+ /// Record fields if possible using `record_str`, to avoid rendering simple strings with
294+ /// their `Debug` representation, which adds extra quotes.
295+ fn record_str ( & mut self , field : & Field , value : & str ) {
296+ match field. name ( ) {
297+ "step_name" => {
298+ self . step_name = Some ( value. to_string ( ) ) ;
299+ }
300+ name => {
301+ self . fields . push ( ( name, value. to_string ( ) ) ) ;
302+ }
303+ }
304+ }
305+
287306 fn record_debug ( & mut self , field : & Field , value : & dyn Debug ) {
288- if field. name ( ) == "message" {
289- self . message = Some ( format ! ( "{value:?}" ) ) ;
290- } else {
291- self . fields . push ( ( field. name ( ) , format ! ( "{value:?}" ) ) ) ;
307+ let formatted = format ! ( "{value:?}" ) ;
308+ match field. name ( ) {
309+ "message" => {
310+ self . message = Some ( formatted) ;
311+ }
312+ name => {
313+ self . fields . push ( ( name, formatted) ) ;
314+ }
292315 }
293316 }
294317 }
@@ -298,6 +321,9 @@ fn setup_tracing(profiling_enabled: bool) -> Option<TracingGuard> {
298321 Enter ,
299322 }
300323
324+ /// Holds the name of a step, stored in `tracing_subscriber`'s extensions.
325+ struct StepNameExtension ( String ) ;
326+
301327 #[ derive( Default ) ]
302328 struct TracingPrinter {
303329 indent : AtomicU32 ,
@@ -369,17 +395,31 @@ fn setup_tracing(profiling_enabled: bool) -> Option<TracingGuard> {
369395 }
370396 }
371397
372- write ! ( writer, "{}" , span. name( ) ) ?;
373- if let Some ( values) = field_values. filter ( |v| !v. fields . is_empty ( ) ) {
374- write ! ( writer, " [" ) ?;
375- for ( index, ( name, value) ) in values. fields . iter ( ) . enumerate ( ) {
376- write ! ( writer, "{name} = {value}" ) ?;
377- if index < values. fields . len ( ) - 1 {
378- write ! ( writer, ", " ) ?;
398+ // We handle steps specially. We instrument them dynamically in `Builder::ensure`,
399+ // and we want to have custom name for each step span. But tracing doesn't allow setting
400+ // dynamic span names. So we detect step spans here and override their name.
401+ if span. metadata ( ) . target ( ) == STEP_NAME_TARGET {
402+ let name = field_values. and_then ( |v| v. step_name . as_deref ( ) ) . unwrap_or ( span. name ( ) ) ;
403+ write ! ( writer, "{name}" ) ?;
404+
405+ // There should be only one more field called `args`
406+ if let Some ( values) = field_values {
407+ let field = & values. fields [ 0 ] ;
408+ write ! ( writer, " {{{}}}" , field. 1 ) ?;
409+ }
410+ } else {
411+ write ! ( writer, "{}" , span. name( ) ) ?;
412+ if let Some ( values) = field_values. filter ( |v| !v. fields . is_empty ( ) ) {
413+ write ! ( writer, " [" ) ?;
414+ for ( index, ( name, value) ) in values. fields . iter ( ) . enumerate ( ) {
415+ write ! ( writer, "{name} = {value}" ) ?;
416+ if index < values. fields . len ( ) - 1 {
417+ write ! ( writer, ", " ) ?;
418+ }
379419 }
420+ write ! ( writer, "]" ) ?;
380421 }
381- write ! ( writer, "]" ) ?;
382- }
422+ } ;
383423
384424 write_location ( writer, span. metadata ( ) ) ?;
385425 writeln ! ( writer) ?;
@@ -424,18 +464,18 @@ fn setup_tracing(profiling_enabled: bool) -> Option<TracingGuard> {
424464 self . write_event ( & mut writer, event) . unwrap ( ) ;
425465 }
426466
427- fn on_new_span (
428- & self ,
429- attrs : & tracing:: span:: Attributes < ' _ > ,
430- id : & Id ,
431- _ctx : Context < ' _ , S > ,
432- ) {
467+ fn on_new_span ( & self , attrs : & tracing:: span:: Attributes < ' _ > , id : & Id , ctx : Context < ' _ , S > ) {
433468 // Record value of span fields
434469 // Note that we do not implement changing values of span fields after they are created.
435470 // For that we would also need to implement the `on_record` method
436-
437471 let mut field_values = FieldValues :: default ( ) ;
438472 attrs. record ( & mut field_values) ;
473+
474+ // We need to propagate the actual name of the span to the Chrome layer below, because
475+ // it cannot access field values. We do that through extensions.
476+ if let Some ( step_name) = field_values. step_name . clone ( ) {
477+ ctx. span ( id) . unwrap ( ) . extensions_mut ( ) . insert ( StepNameExtension ( step_name) ) ;
478+ }
439479 self . span_values . lock ( ) . unwrap ( ) . insert ( id. clone ( ) , field_values) ;
440480 }
441481
@@ -466,8 +506,21 @@ fn setup_tracing(profiling_enabled: bool) -> Option<TracingGuard> {
466506 let chrome_tracing_path = tempdir. path ( ) . join ( "bootstrap-trace.json" ) ;
467507 let file = BufWriter :: new ( File :: create ( & chrome_tracing_path) . unwrap ( ) ) ;
468508
469- let chrome_layer =
470- tracing_chrome:: ChromeLayerBuilder :: new ( ) . writer ( file) . include_args ( true ) ;
509+ let chrome_layer = tracing_chrome:: ChromeLayerBuilder :: new ( )
510+ . writer ( file)
511+ . include_args ( true )
512+ . name_fn ( Box :: new ( |event_or_span| match event_or_span {
513+ tracing_chrome:: EventOrSpan :: Event ( e) => e. metadata ( ) . name ( ) . to_string ( ) ,
514+ tracing_chrome:: EventOrSpan :: Span ( s) => {
515+ if s. metadata ( ) . target ( ) == STEP_NAME_TARGET
516+ && let Some ( extension) = s. extensions ( ) . get :: < StepNameExtension > ( )
517+ {
518+ extension. 0 . clone ( )
519+ } else {
520+ s. metadata ( ) . name ( ) . to_string ( )
521+ }
522+ }
523+ } ) ) ;
471524 let ( chrome_layer, guard) = chrome_layer. build ( ) ;
472525
473526 tracing:: subscriber:: set_global_default ( registry. with ( chrome_layer) ) . unwrap ( ) ;
0 commit comments