golang.org/x/build@v0.0.0-20240506185731-218518f32b70/internal/coordinator/schedule/span.go (about)

     1  // Copyright 2021 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build linux || darwin
     6  
     7  package schedule
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"strings"
    13  	"time"
    14  
    15  	"golang.org/x/build/internal/coordinator/pool"
    16  	"golang.org/x/build/types"
    17  )
    18  
    19  type Spanner interface {
    20  	SpanRecord(*Span, error) *types.SpanRecord
    21  }
    22  
    23  // Span is an event covering a region of time.
    24  // A Span ultimately ends in an error or success, and will eventually
    25  // be visualized and logged.
    26  type Span struct {
    27  	event   string // event name like "get_foo" or "write_bar"
    28  	optText string // optional details for event
    29  	start   time.Time
    30  	end     time.Time
    31  	el      pool.EventTimeLogger // where we log to at the end; TODO: this will change
    32  }
    33  
    34  // Event is the span's event.
    35  func (s *Span) Event() string {
    36  	return s.event
    37  }
    38  
    39  // OptText is the optional text for a span.
    40  func (s *Span) OptText() string {
    41  	return s.optText
    42  }
    43  
    44  // Start is the start time for the span.
    45  func (s *Span) Start() time.Time {
    46  	return s.start
    47  }
    48  
    49  // End is the end time for an span.
    50  func (s *Span) End() time.Time {
    51  	return s.end
    52  }
    53  
    54  // CreateSpan creates a span with the appropriate metadata. It also starts the span.
    55  func CreateSpan(el pool.EventTimeLogger, event string, optText ...string) *Span {
    56  	start := time.Now()
    57  	el.LogEventTime(event, optText...)
    58  	return &Span{
    59  		el:      el,
    60  		event:   event,
    61  		start:   start,
    62  		optText: strings.Join(optText, " "),
    63  	}
    64  }
    65  
    66  // Done ends a span.
    67  // It is legal to call Done multiple times. Only the first call
    68  // logs.
    69  // Done always returns its input argument.
    70  func (s *Span) Done(err error) error {
    71  	if !s.end.IsZero() {
    72  		return err
    73  	}
    74  	t1 := time.Now()
    75  	s.end = t1
    76  	td := t1.Sub(s.start)
    77  	var text bytes.Buffer
    78  	fmt.Fprintf(&text, "after %s", friendlyDuration(td))
    79  	if err != nil {
    80  		fmt.Fprintf(&text, "; err=%v", err)
    81  	}
    82  	if s.optText != "" {
    83  		fmt.Fprintf(&text, "; %v", s.optText)
    84  	}
    85  	if st, ok := s.el.(Spanner); ok {
    86  		pool.CoordinatorProcess().PutSpanRecord(st.SpanRecord(s, err))
    87  	}
    88  	s.el.LogEventTime("finish_"+s.event, text.String())
    89  	return err
    90  }
    91  
    92  // TODO: This is a copy of the function in cmd/coordinator/status.go. This should be removed once status
    93  // is moved into it's own package.
    94  func friendlyDuration(d time.Duration) string {
    95  	if d > 10*time.Second {
    96  		d2 := ((d + 50*time.Millisecond) / (100 * time.Millisecond)) * (100 * time.Millisecond)
    97  		return d2.String()
    98  	}
    99  	if d > time.Second {
   100  		d2 := ((d + 5*time.Millisecond) / (10 * time.Millisecond)) * (10 * time.Millisecond)
   101  		return d2.String()
   102  	}
   103  	d2 := ((d + 50*time.Microsecond) / (100 * time.Microsecond)) * (100 * time.Microsecond)
   104  	return d2.String()
   105  }