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 }