@@ -26,7 +26,7 @@ fn is_tracing_enabled() -> bool {
2626
2727fn main ( ) {
2828 #[ cfg( feature = "tracing" ) ]
29- let guard = setup_tracing ( is_profiling_enabled ( ) ) ;
29+ let guard = bootstrap :: setup_tracing ( is_profiling_enabled ( ) ) ;
3030
3131 let start_time = Instant :: now ( ) ;
3232
@@ -247,300 +247,3 @@ fn check_version(config: &Config) -> Option<String> {
247247
248248 Some ( msg)
249249}
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