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  }