github.com/iotexproject/iotex-core@v1.14.1-rc1/pkg/prometheustimer/timer.go (about) 1 // Copyright (c) 2019 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package prometheustimer 7 8 import ( 9 "github.com/pkg/errors" 10 11 "github.com/facebookgo/clock" 12 "github.com/prometheus/client_golang/prometheus" 13 14 "github.com/iotexproject/iotex-core/pkg/log" 15 ) 16 17 type ( 18 // TimerFactory defines a timer factory to generate timer 19 TimerFactory struct { 20 labelNames []string 21 defaultLabels []string 22 vect *prometheus.GaugeVec 23 clk clock.Clock 24 } 25 // Timer defines a timer to measure performance 26 Timer struct { 27 factory *TimerFactory 28 labels []string 29 startTime int64 30 ended bool 31 } 32 33 // StopWatch is used to measure accumulation of multiple time slices. 34 StopWatch struct { 35 factory *TimerFactory 36 labels []string 37 startTime int64 38 accumulated int64 39 ended bool 40 } 41 ) 42 43 // New returns a new Timer 44 func New(name, tip string, labelNames []string, defaultLabels []string) (*TimerFactory, error) { 45 if len(labelNames) != len(defaultLabels) { 46 return nil, errors.New("label names do not match default labels") 47 } 48 vect := prometheus.NewGaugeVec( 49 prometheus.GaugeOpts{ 50 Name: name, 51 Help: tip, 52 }, 53 labelNames, 54 ) 55 err := prometheus.Register(vect) 56 if _, ok := err.(prometheus.AlreadyRegisteredError); ok { 57 err = nil 58 } 59 60 return &TimerFactory{ 61 labelNames: labelNames, 62 defaultLabels: defaultLabels, 63 vect: vect, 64 clk: clock.New(), 65 }, err 66 } 67 68 // NewTimer returns a timer with start time as now 69 func (factory *TimerFactory) NewTimer(labels ...string) *Timer { 70 if factory == nil { 71 return &Timer{} 72 } 73 if len(labels) > len(factory.labelNames) { 74 log.L().Error("Two many timer labels") 75 return &Timer{} 76 } 77 return &Timer{ 78 factory: factory, 79 labels: labels, 80 startTime: factory.now(), 81 } 82 } 83 84 // End ends the timer 85 func (timer *Timer) End() { 86 f := timer.factory 87 if f == nil || timer.ended { 88 return 89 } 90 f.log(float64(f.now()-timer.startTime), timer.labels...) 91 timer.ended = true 92 } 93 94 func (factory *TimerFactory) log(value float64, labels ...string) { 95 factory.vect.WithLabelValues( 96 append(labels, factory.defaultLabels[len(labels):]...)..., 97 ).Set(value) 98 } 99 100 func (factory *TimerFactory) now() int64 { 101 return factory.clk.Now().UnixNano() 102 } 103 104 // NewStopWatch returns a StopWatch with start time as now 105 func (factory *TimerFactory) NewStopWatch(labels ...string) *StopWatch { 106 if factory == nil { 107 return &StopWatch{} 108 } 109 if len(labels) > len(factory.labelNames) { 110 log.L().Error("Two many timer labels") 111 return &StopWatch{} 112 } 113 return &StopWatch{ 114 factory: factory, 115 labels: labels, 116 startTime: factory.now(), 117 } 118 } 119 120 // Reset cleans out the accumulated time. 121 func (sw *StopWatch) Reset() { sw.accumulated = 0 } 122 123 // Record records time between start time to now into accumulated time. 124 func (sw *StopWatch) Record() { 125 f := sw.factory 126 if f == nil { 127 return 128 } 129 sw.accumulated += f.now() - sw.startTime 130 } 131 132 // Start reset start time to now. 133 func (sw *StopWatch) Start() { 134 f := sw.factory 135 if f == nil { 136 return 137 } 138 sw.startTime = f.now() 139 } 140 141 // End ends the StopWatch and log the total accumulated time. 142 func (sw *StopWatch) End() { 143 f := sw.factory 144 if f == nil || sw.ended { 145 return 146 } 147 f.log(float64(sw.accumulated), sw.labels...) 148 sw.ended = true 149 }