@@ -26,7 +26,7 @@ fn is_tracing_enabled() -> bool {
26
26
27
27
fn main ( ) {
28
28
#[ cfg( feature = "tracing" ) ]
29
- let guard = setup_tracing ( is_profiling_enabled ( ) ) ;
29
+ let guard = bootstrap :: setup_tracing ( is_profiling_enabled ( ) ) ;
30
30
31
31
let start_time = Instant :: now ( ) ;
32
32
@@ -247,300 +247,3 @@ fn check_version(config: &Config) -> Option<String> {
247
247
248
248
Some ( msg)
249
249
}
250
-
251
- // # Note on `tracing` usage in bootstrap
252
- //
253
- // Due to the conditional compilation via the `tracing` cargo feature, this means that `tracing`
254
- // usages in bootstrap need to be also gated behind the `tracing` feature:
255
- //
256
- // - `tracing` macros with log levels (`trace!`, `debug!`, `warn!`, `info`, `error`) should not be
257
- // used *directly*. You should use the wrapped `tracing` macros which gate the actual invocations
258
- // behind `feature = "tracing"`.
259
- // - `tracing`'s `#[instrument(..)]` macro will need to be gated like `#![cfg_attr(feature =
260
- // "tracing", instrument(..))]`.
261
- #[ cfg( feature = "tracing" ) ]
262
- fn setup_tracing ( profiling_enabled : bool ) -> Option < TracingGuard > {
263
- use std:: fmt:: Debug ;
264
- use std:: fs:: File ;
265
- use std:: io:: BufWriter ;
266
- use std:: sync:: atomic:: { AtomicU32 , Ordering } ;
267
-
268
- use bootstrap:: STEP_NAME_TARGET ;
269
- use chrono:: { DateTime , Utc } ;
270
- use tracing:: field:: { Field , Visit } ;
271
- use tracing:: { Event , Id , Level , Subscriber } ;
272
- use tracing_subscriber:: layer:: { Context , SubscriberExt } ;
273
- use tracing_subscriber:: registry:: { LookupSpan , SpanRef } ;
274
- use tracing_subscriber:: { EnvFilter , Layer } ;
275
-
276
- let filter = EnvFilter :: from_env ( "BOOTSTRAP_TRACING" ) ;
277
-
278
- /// Visitor that extracts both known and unknown field values from events and spans.
279
- #[ derive( Default ) ]
280
- struct FieldValues {
281
- /// Main event message
282
- message : Option < String > ,
283
- /// Name of a recorded psna
284
- step_name : Option < String > ,
285
- /// The rest of arbitrary event/span fields
286
- fields : Vec < ( & ' static str , String ) > ,
287
- }
288
-
289
- impl Visit for FieldValues {
290
- /// Record fields if possible using `record_str`, to avoid rendering simple strings with
291
- /// their `Debug` representation, which adds extra quotes.
292
- fn record_str ( & mut self , field : & Field , value : & str ) {
293
- match field. name ( ) {
294
- "step_name" => {
295
- self . step_name = Some ( value. to_string ( ) ) ;
296
- }
297
- name => {
298
- self . fields . push ( ( name, value. to_string ( ) ) ) ;
299
- }
300
- }
301
- }
302
-
303
- fn record_debug ( & mut self , field : & Field , value : & dyn Debug ) {
304
- let formatted = format ! ( "{value:?}" ) ;
305
- match field. name ( ) {
306
- "message" => {
307
- self . message = Some ( formatted) ;
308
- }
309
- name => {
310
- self . fields . push ( ( name, formatted) ) ;
311
- }
312
- }
313
- }
314
- }
315
-
316
- #[ derive( Copy , Clone ) ]
317
- enum SpanAction {
318
- Enter ,
319
- }
320
-
321
- /// Holds the name of a step, stored in `tracing_subscriber`'s extensions.
322
- struct StepNameExtension ( String ) ;
323
-
324
- #[ derive( Default ) ]
325
- struct TracingPrinter {
326
- indent : AtomicU32 ,
327
- span_values : std:: sync:: Mutex < std:: collections:: HashMap < tracing:: Id , FieldValues > > ,
328
- }
329
-
330
- impl TracingPrinter {
331
- fn format_header < W : Write > (
332
- & self ,
333
- writer : & mut W ,
334
- time : DateTime < Utc > ,
335
- level : & Level ,
336
- ) -> std:: io:: Result < ( ) > {
337
- // Use a fixed-width timestamp without date, that shouldn't be very important
338
- let timestamp = time. format ( "%H:%M:%S.%3f" ) ;
339
- write ! ( writer, "{timestamp} " ) ?;
340
- // Make sure that levels are aligned to the same number of characters, in order not to
341
- // break the layout
342
- write ! ( writer, "{level:>5} " ) ?;
343
- write ! ( writer, "{}" , " " . repeat( self . indent. load( Ordering :: Relaxed ) as usize ) )
344
- }
345
-
346
- fn write_event < W : Write > ( & self , writer : & mut W , event : & Event < ' _ > ) -> std:: io:: Result < ( ) > {
347
- let now = Utc :: now ( ) ;
348
-
349
- self . format_header ( writer, now, event. metadata ( ) . level ( ) ) ?;
350
-
351
- let mut field_values = FieldValues :: default ( ) ;
352
- event. record ( & mut field_values) ;
353
-
354
- if let Some ( msg) = & field_values. message {
355
- write ! ( writer, "{msg}" ) ?;
356
- }
357
-
358
- if !field_values. fields . is_empty ( ) {
359
- if field_values. message . is_some ( ) {
360
- write ! ( writer, " " ) ?;
361
- }
362
- write ! ( writer, "[" ) ?;
363
- for ( index, ( name, value) ) in field_values. fields . iter ( ) . enumerate ( ) {
364
- write ! ( writer, "{name} = {value}" ) ?;
365
- if index < field_values. fields . len ( ) - 1 {
366
- write ! ( writer, ", " ) ?;
367
- }
368
- }
369
- write ! ( writer, "]" ) ?;
370
- }
371
- write_location ( writer, event. metadata ( ) ) ?;
372
- writeln ! ( writer) ?;
373
- Ok ( ( ) )
374
- }
375
-
376
- fn write_span < W : Write , S > (
377
- & self ,
378
- writer : & mut W ,
379
- span : SpanRef < ' _ , S > ,
380
- field_values : Option < & FieldValues > ,
381
- action : SpanAction ,
382
- ) -> std:: io:: Result < ( ) >
383
- where
384
- S : for < ' lookup > LookupSpan < ' lookup > ,
385
- {
386
- let now = Utc :: now ( ) ;
387
-
388
- self . format_header ( writer, now, span. metadata ( ) . level ( ) ) ?;
389
- match action {
390
- SpanAction :: Enter => {
391
- write ! ( writer, "> " ) ?;
392
- }
393
- }
394
-
395
- // We handle steps specially. We instrument them dynamically in `Builder::ensure`,
396
- // and we want to have custom name for each step span. But tracing doesn't allow setting
397
- // dynamic span names. So we detect step spans here and override their name.
398
- if span. metadata ( ) . target ( ) == STEP_NAME_TARGET {
399
- let name = field_values. and_then ( |v| v. step_name . as_deref ( ) ) . unwrap_or ( span. name ( ) ) ;
400
- write ! ( writer, "{name}" ) ?;
401
-
402
- // There should be only one more field called `args`
403
- if let Some ( values) = field_values {
404
- let field = & values. fields [ 0 ] ;
405
- write ! ( writer, " {{{}}}" , field. 1 ) ?;
406
- }
407
- } else {
408
- write ! ( writer, "{}" , span. name( ) ) ?;
409
- if let Some ( values) = field_values. filter ( |v| !v. fields . is_empty ( ) ) {
410
- write ! ( writer, " [" ) ?;
411
- for ( index, ( name, value) ) in values. fields . iter ( ) . enumerate ( ) {
412
- write ! ( writer, "{name} = {value}" ) ?;
413
- if index < values. fields . len ( ) - 1 {
414
- write ! ( writer, ", " ) ?;
415
- }
416
- }
417
- write ! ( writer, "]" ) ?;
418
- }
419
- } ;
420
-
421
- write_location ( writer, span. metadata ( ) ) ?;
422
- writeln ! ( writer) ?;
423
- Ok ( ( ) )
424
- }
425
- }
426
-
427
- fn write_location < W : Write > (
428
- writer : & mut W ,
429
- metadata : & ' static tracing:: Metadata < ' static > ,
430
- ) -> std:: io:: Result < ( ) > {
431
- use std:: path:: { Path , PathBuf } ;
432
-
433
- if let Some ( filename) = metadata. file ( ) {
434
- // Keep only the module name and file name to make it shorter
435
- let filename: PathBuf = Path :: new ( filename)
436
- . components ( )
437
- // Take last two path components
438
- . rev ( )
439
- . take ( 2 )
440
- . collect :: < Vec < _ > > ( )
441
- . into_iter ( )
442
- . rev ( )
443
- . collect ( ) ;
444
-
445
- write ! ( writer, " ({}" , filename. display( ) ) ?;
446
- if let Some ( line) = metadata. line ( ) {
447
- write ! ( writer, ":{line}" ) ?;
448
- }
449
- write ! ( writer, ")" ) ?;
450
- }
451
- Ok ( ( ) )
452
- }
453
-
454
- impl < S > Layer < S > for TracingPrinter
455
- where
456
- S : Subscriber ,
457
- S : for < ' lookup > LookupSpan < ' lookup > ,
458
- {
459
- fn on_event ( & self , event : & Event < ' _ > , _ctx : Context < ' _ , S > ) {
460
- let mut writer = std:: io:: stderr ( ) . lock ( ) ;
461
- self . write_event ( & mut writer, event) . unwrap ( ) ;
462
- }
463
-
464
- fn on_new_span ( & self , attrs : & tracing:: span:: Attributes < ' _ > , id : & Id , ctx : Context < ' _ , S > ) {
465
- // Record value of span fields
466
- // Note that we do not implement changing values of span fields after they are created.
467
- // For that we would also need to implement the `on_record` method
468
- let mut field_values = FieldValues :: default ( ) ;
469
- attrs. record ( & mut field_values) ;
470
-
471
- // We need to propagate the actual name of the span to the Chrome layer below, because
472
- // it cannot access field values. We do that through extensions.
473
- if let Some ( step_name) = field_values. step_name . clone ( ) {
474
- ctx. span ( id) . unwrap ( ) . extensions_mut ( ) . insert ( StepNameExtension ( step_name) ) ;
475
- }
476
- self . span_values . lock ( ) . unwrap ( ) . insert ( id. clone ( ) , field_values) ;
477
- }
478
-
479
- fn on_enter ( & self , id : & Id , ctx : Context < ' _ , S > ) {
480
- if let Some ( span) = ctx. span ( id) {
481
- let mut writer = std:: io:: stderr ( ) . lock ( ) ;
482
- let values = self . span_values . lock ( ) . unwrap ( ) ;
483
- let values = values. get ( id) ;
484
- self . write_span ( & mut writer, span, values, SpanAction :: Enter ) . unwrap ( ) ;
485
- }
486
- self . indent . fetch_add ( 1 , Ordering :: Relaxed ) ;
487
- }
488
-
489
- fn on_exit ( & self , _id : & Id , _ctx : Context < ' _ , S > ) {
490
- self . indent . fetch_sub ( 1 , Ordering :: Relaxed ) ;
491
- }
492
- }
493
-
494
- let registry = tracing_subscriber:: registry ( ) . with ( filter) . with ( TracingPrinter :: default ( ) ) ;
495
-
496
- let guard = if profiling_enabled {
497
- // When we're creating this layer, we do not yet know the location of the tracing output
498
- // directory, because it is stored in the output directory determined after Config is parsed,
499
- // but we already want to make tracing calls during (and before) config parsing.
500
- // So we store the output into a temporary file, and then move it to the tracing directory
501
- // before bootstrap ends.
502
- let tempdir = tempfile:: TempDir :: new ( ) . expect ( "Cannot create temporary directory" ) ;
503
- let chrome_tracing_path = tempdir. path ( ) . join ( "bootstrap-trace.json" ) ;
504
- let file = BufWriter :: new ( File :: create ( & chrome_tracing_path) . unwrap ( ) ) ;
505
-
506
- let chrome_layer = tracing_chrome:: ChromeLayerBuilder :: new ( )
507
- . writer ( file)
508
- . include_args ( true )
509
- . name_fn ( Box :: new ( |event_or_span| match event_or_span {
510
- tracing_chrome:: EventOrSpan :: Event ( e) => e. metadata ( ) . name ( ) . to_string ( ) ,
511
- tracing_chrome:: EventOrSpan :: Span ( s) => {
512
- if s. metadata ( ) . target ( ) == STEP_NAME_TARGET
513
- && let Some ( extension) = s. extensions ( ) . get :: < StepNameExtension > ( )
514
- {
515
- extension. 0 . clone ( )
516
- } else {
517
- s. metadata ( ) . name ( ) . to_string ( )
518
- }
519
- }
520
- } ) ) ;
521
- let ( chrome_layer, guard) = chrome_layer. build ( ) ;
522
-
523
- tracing:: subscriber:: set_global_default ( registry. with ( chrome_layer) ) . unwrap ( ) ;
524
- Some ( TracingGuard { guard, _tempdir : tempdir, chrome_tracing_path } )
525
- } else {
526
- tracing:: subscriber:: set_global_default ( registry) . unwrap ( ) ;
527
- None
528
- } ;
529
-
530
- guard
531
- }
532
-
533
- #[ cfg( feature = "tracing" ) ]
534
- struct TracingGuard {
535
- guard : tracing_chrome:: FlushGuard ,
536
- _tempdir : tempfile:: TempDir ,
537
- chrome_tracing_path : std:: path:: PathBuf ,
538
- }
539
-
540
- #[ cfg( feature = "tracing" ) ]
541
- impl TracingGuard {
542
- fn copy_to_dir ( self , dir : & std:: path:: Path ) {
543
- drop ( self . guard ) ;
544
- std:: fs:: rename ( & self . chrome_tracing_path , dir. join ( "chrome-trace.json" ) ) . unwrap ( ) ;
545
- }
546
- }
0 commit comments