github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/timings/timings.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2019 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package timings 21 22 import ( 23 "time" 24 ) 25 26 var timeNow = func() time.Time { 27 return time.Now() 28 } 29 30 // Timings represents a tree of Span time measurements for a single execution of measured activity. 31 // A Timings tree object should be created at the beginning of the activity, 32 // followed by starting at least one Span, and then saved at the end of the activity. 33 // 34 // Calling StartSpan on the Timings objects creates a Span and starts new 35 // performance measurement. Measurement needs to be finished by calling Stop 36 // function on the Span object. 37 // Nested measurements may be collected by calling StartSpan on Span objects. Similar 38 // to the above, nested measurements need to be finished by calling Stop on them. 39 // 40 // Typical usage: 41 // troot := timings.New(map[string]string{"task-id": task.ID(), "change-id": task.Change().ID()}) 42 // t1 := troot.StartSpan("computation", "...") 43 // .... 44 // nestedTiming := t1.StartSpan("sub-computation", "...") 45 // .... 46 // nestedTiming.Stop() 47 // t1.Stop() 48 // troot.Save() 49 // 50 // In addition, a few helpers exist to simplify typical use cases, for example the above example 51 // can be reduced to: 52 // troot := state.TimingsForTask(task) // tags set automatically from task 53 // t1 := troot.StartSpan("computation", "...") 54 // timings.Run(t1, "sub-computation", "...", func(nested *Span) { 55 // ... expensive computation 56 // }) 57 // t1.Stop() 58 // troot.Save(task.State()) 59 type Timings struct { 60 tags map[string]string 61 timings []*Span 62 } 63 64 // Span represents a single performance measurement with optional nested measurements. 65 type Span struct { 66 label, summary string 67 start, stop time.Time 68 timings []*Span 69 } 70 71 type Measurer interface { 72 StartSpan(label, summary string) *Span 73 } 74 75 // New creates a Timings object. Tags provide extra information (such as "task-id" and "change-id") 76 // that can be used by the client when retrieving timings. 77 func New(tags map[string]string) *Timings { 78 return &Timings{ 79 tags: tags, 80 } 81 } 82 83 // AddTag sets a tag on the Timings object. 84 func (t *Timings) AddTag(tag, value string) { 85 if t.tags == nil { 86 t.tags = make(map[string]string) 87 } 88 t.tags[tag] = value 89 } 90 91 func startSpan(label, summary string) *Span { 92 tmeas := &Span{ 93 label: label, 94 summary: summary, 95 start: timeNow(), 96 } 97 return tmeas 98 } 99 100 // StartSpan creates a Span and initiates performance measurement. 101 // Measurement needs to be stopped by calling Stop on it. 102 func (t *Timings) StartSpan(label, summary string) *Span { 103 tmeas := startSpan(label, summary) 104 t.timings = append(t.timings, tmeas) 105 return tmeas 106 } 107 108 // StartSpan creates a new nested Span and initiates performance measurement. 109 // Nested measurements need to be stopped by calling Stop on it. 110 func (t *Span) StartSpan(label, summary string) *Span { 111 tmeas := startSpan(label, summary) 112 t.timings = append(t.timings, tmeas) 113 return tmeas 114 } 115 116 // Stop stops the measurement. 117 func (t *Span) Stop() { 118 if t.stop.IsZero() { 119 t.stop = timeNow() 120 } // else - stopping already stopped timing is an error, but just ignore it 121 }