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 }