@@ -268,6 +268,7 @@ fn setup_tracing(profiling_enabled: bool) -> Option<TracingGuard> {
268
268
use std:: io:: BufWriter ;
269
269
use std:: sync:: atomic:: { AtomicU32 , Ordering } ;
270
270
271
+ use bootstrap:: STEP_NAME_TARGET ;
271
272
use chrono:: { DateTime , Utc } ;
272
273
use tracing:: field:: { Field , Visit } ;
273
274
use tracing:: { Event , Id , Level , Subscriber } ;
@@ -277,18 +278,40 @@ fn setup_tracing(profiling_enabled: bool) -> Option<TracingGuard> {
277
278
278
279
let filter = EnvFilter :: from_env ( "BOOTSTRAP_TRACING" ) ;
279
280
281
+ /// Visitor that extracts both known and unknown field values from events and spans.
280
282
#[ derive( Default ) ]
281
283
struct FieldValues {
284
+ /// Main event message
282
285
message : Option < String > ,
286
+ /// Name of a recorded psna
287
+ step_name : Option < String > ,
288
+ /// The rest of arbitrary event/span fields
283
289
fields : Vec < ( & ' static str , String ) > ,
284
290
}
285
291
286
292
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
+
287
306
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
+ }
292
315
}
293
316
}
294
317
}
@@ -298,6 +321,9 @@ fn setup_tracing(profiling_enabled: bool) -> Option<TracingGuard> {
298
321
Enter ,
299
322
}
300
323
324
+ /// Holds the name of a step, stored in `tracing_subscriber`'s extensions.
325
+ struct StepNameExtension ( String ) ;
326
+
301
327
#[ derive( Default ) ]
302
328
struct TracingPrinter {
303
329
indent : AtomicU32 ,
@@ -369,17 +395,31 @@ fn setup_tracing(profiling_enabled: bool) -> Option<TracingGuard> {
369
395
}
370
396
}
371
397
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
+ }
379
419
}
420
+ write ! ( writer, "]" ) ?;
380
421
}
381
- write ! ( writer, "]" ) ?;
382
- }
422
+ } ;
383
423
384
424
write_location ( writer, span. metadata ( ) ) ?;
385
425
writeln ! ( writer) ?;
@@ -424,18 +464,18 @@ fn setup_tracing(profiling_enabled: bool) -> Option<TracingGuard> {
424
464
self . write_event ( & mut writer, event) . unwrap ( ) ;
425
465
}
426
466
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 > ) {
433
468
// Record value of span fields
434
469
// Note that we do not implement changing values of span fields after they are created.
435
470
// For that we would also need to implement the `on_record` method
436
-
437
471
let mut field_values = FieldValues :: default ( ) ;
438
472
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
+ }
439
479
self . span_values . lock ( ) . unwrap ( ) . insert ( id. clone ( ) , field_values) ;
440
480
}
441
481
@@ -466,8 +506,21 @@ fn setup_tracing(profiling_enabled: bool) -> Option<TracingGuard> {
466
506
let chrome_tracing_path = tempdir. path ( ) . join ( "bootstrap-trace.json" ) ;
467
507
let file = BufWriter :: new ( File :: create ( & chrome_tracing_path) . unwrap ( ) ) ;
468
508
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
+ } ) ) ;
471
524
let ( chrome_layer, guard) = chrome_layer. build ( ) ;
472
525
473
526
tracing:: subscriber:: set_global_default ( registry. with ( chrome_layer) ) . unwrap ( ) ;
0 commit comments