github.com/puellanivis/breton@v0.2.16/lib/metrics/gauge.go (about)

     1  package metrics
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/prometheus/client_golang/prometheus"
     7  )
     8  
     9  // A GaugeValue holds the tracking information for a specific Gauge or a “Child” of a Gauge.
    10  type GaugeValue struct {
    11  	// we want to duplicate this every WithLabels() call,
    12  	// so we don’t use a pointer here.
    13  	metric
    14  
    15  	g  prometheus.Gauge
    16  	gv *prometheus.GaugeVec
    17  }
    18  
    19  // WithLabels provides access to a labeled dimension of the metric, and
    20  // returns a “Child” wherein the given labels are set.
    21  // The “Child” returned is cacheable by the Caller, so as to avoid having
    22  // to look it up again—this matters in latency-critical code.
    23  //
    24  // Caller MUST NOT attempt to set a label value that has been defined to be constant.
    25  func (g GaugeValue) WithLabels(labels ...Labeler) *GaugeValue {
    26  	// we are working with a new copy, so no mutex is necessary.
    27  	g.g = nil
    28  
    29  	g.labels = g.labels.With(labels...)
    30  
    31  	return &g
    32  }
    33  
    34  // Remove will remove a “Child” that matches the given labels from the metric, no longer exporting it.
    35  func (g *GaugeValue) Remove(labels ...Labeler) bool {
    36  	if g.gv == nil {
    37  		return false
    38  	}
    39  
    40  	return g.gv.Delete(g.labels.getMap())
    41  }
    42  
    43  // Clear removes all “Children” from the metric.
    44  func (g *GaugeValue) Clear() {
    45  	if g.gv != nil {
    46  		g.gv.Reset()
    47  	}
    48  }
    49  
    50  // Gauge represents a value that can go up and down.
    51  func Gauge(name string, help string, options ...Option) *GaugeValue {
    52  	m := newMetric(name, help)
    53  
    54  	for _, opt := range options {
    55  		// in initialization, we throw the reverting function away
    56  		_ = opt(m)
    57  	}
    58  
    59  	g := &GaugeValue{
    60  		metric: *m,
    61  	}
    62  
    63  	opts := prometheus.GaugeOpts{
    64  		Name: name,
    65  		Help: help,
    66  	}
    67  
    68  	if g.labels != nil {
    69  		g.gv = prometheus.NewGaugeVec(opts, g.labels.set.keys)
    70  		g.registry.MustRegister(g.gv)
    71  
    72  	} else {
    73  		g.g = prometheus.NewGauge(opts)
    74  		g.registry.MustRegister(g.g)
    75  	}
    76  
    77  	return g
    78  }
    79  
    80  // Inc increments the Gauge by 1.
    81  func (g *GaugeValue) Inc() {
    82  	if g.g == nil {
    83  		// function is idempotent, and won’t step on others’ toes
    84  		g.g = g.gv.With(g.labels.getMap())
    85  	}
    86  
    87  	g.g.Inc()
    88  }
    89  
    90  // Add increments the Gauge by the given value.
    91  func (g *GaugeValue) Add(v float64) {
    92  	if g.g == nil {
    93  		// function is idempotent, and won’t step on others’ toes
    94  		g.g = g.gv.With(g.labels.getMap())
    95  	}
    96  
    97  	g.g.Add(v)
    98  }
    99  
   100  // Dec decrements the Gauge by 1.
   101  func (g *GaugeValue) Dec() {
   102  	if g.g == nil {
   103  		// function is idempotent, and won’t step on others’ toes
   104  		g.g = g.gv.With(g.labels.getMap())
   105  	}
   106  
   107  	g.g.Dec()
   108  }
   109  
   110  // Sub decrements the Gauge by the given value.
   111  func (g *GaugeValue) Sub(v float64) {
   112  	if g.g == nil {
   113  		// function is idempotent, and won’t step on others’ toes
   114  		g.g = g.gv.With(g.labels.getMap())
   115  	}
   116  
   117  	g.g.Sub(v)
   118  }
   119  
   120  // Observe is an alias for Set in order to implement the Observer interface.
   121  func (g *GaugeValue) Observe(v float64) {
   122  	g.Set(v)
   123  }
   124  
   125  // Set sets the Gauge to the given value.
   126  func (g *GaugeValue) Set(v float64) {
   127  	if g.g == nil {
   128  		// function is idempotent, and won’t step on others’ toes
   129  		g.g = g.gv.With(g.labels.getMap())
   130  	}
   131  
   132  	g.g.Set(v)
   133  }
   134  
   135  // SetToTime sets the Gauge to the given Time in seconds.
   136  func (g *GaugeValue) SetToTime(t time.Time) {
   137  	g.Set(float64(t.UnixNano()) / 1e9)
   138  }
   139  
   140  // SetToDuration sets the Gauge to the given Duration in seconds.
   141  func (g *GaugeValue) SetToDuration(d time.Duration) {
   142  	g.Set(d.Seconds())
   143  }
   144  
   145  // Timer times a piece of code and sets the Gauge to its duration in seconds.
   146  // This is useful for batch jobs. The Timer will commit the duration when the done function is called.
   147  //
   148  // (Caller MUST ensure the returned done function is called, and SHOULD use defer.)
   149  func (g *GaugeValue) Timer() (done func()) {
   150  	// get start time as fast as possible, then set the Gauge to zero.
   151  	// reference: https://prometheus.io/docs/practices/instrumentation/#avoid-missing-metrics
   152  	t := time.Now()
   153  	g.Set(0)
   154  
   155  	return func() {
   156  		// use our g.Set here to ensure g.g gets set in the same code
   157  		// path as the g.g.Set() call, otherwise possibly racey.
   158  		g.SetToDuration(time.Since(t))
   159  	}
   160  }