github.com/puellanivis/breton@v0.2.16/lib/metrics/histogram.go (about) 1 package metrics 2 3 import ( 4 "time" 5 6 "github.com/prometheus/client_golang/prometheus" 7 ) 8 9 // A HistogramValue holds the tracking information for a specific Histogram or a “Child” of a Histogram. 10 type HistogramValue struct { 11 // we want to duplicate this every WithLabels() call, 12 // so we don’t use a pointer here. 13 metric 14 15 h prometheus.Histogram 16 hv *prometheus.HistogramVec 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 (h HistogramValue) WithLabels(labels ...Labeler) *HistogramValue { 26 // we are working with a new copy, so no mutex is necessary. 27 h.h = nil 28 29 h.labels = h.labels.With(labels...) 30 31 return &h 32 } 33 34 // Remove will remove a “Child” that matches the given labels from the metric, no longer exporting it. 35 func (h *HistogramValue) Remove(labels ...Labeler) bool { 36 if h.hv == nil { 37 return false 38 } 39 40 return h.hv.Delete(h.labels.getMap()) 41 } 42 43 // Clear removes all “Children” from the metric. 44 func (h *HistogramValue) Clear() { 45 if h.hv != nil { 46 h.hv.Reset() 47 } 48 } 49 50 // Histogram allows aggregated distributions of events, such as request latencies. 51 // This is at its core a Counter per bucket. 52 func Histogram(name string, help string, options ...Option) *HistogramValue { 53 m := newMetric(name, help) 54 55 m.histogramSettings = new(histogramSettings) 56 57 for _, opt := range options { 58 // in initialization, we throw the reverting function away 59 _ = opt(m) 60 } 61 62 h := &HistogramValue{ 63 metric: *m, 64 } 65 66 opts := prometheus.HistogramOpts{ 67 Name: name, 68 Help: help, 69 Buckets: m.buckets, 70 } 71 72 if h.labels != nil { 73 if _, ok := m.labels.set.canSet["le"]; ok { 74 panic("histograms cannot allow \"le\" as a label") 75 } 76 77 h.hv = prometheus.NewHistogramVec(opts, h.labels.set.keys) 78 h.registry.MustRegister(h.hv) 79 80 } else { 81 h.h = prometheus.NewHistogram(opts) 82 h.registry.MustRegister(h.h) 83 } 84 85 return h 86 } 87 88 // Observe records the given value into the Histogram. 89 func (h *HistogramValue) Observe(v float64) { 90 if h.h == nil { 91 // function is idempotent, and won’t step on others’ toes 92 h.h = h.hv.With(h.labels.getMap()).(prometheus.Histogram) 93 } 94 95 h.h.Observe(v) 96 } 97 98 // ObserveDuration records the given Duration into the Histogram. 99 func (h *HistogramValue) ObserveDuration(d time.Duration) { 100 h.Observe(d.Seconds()) 101 } 102 103 // Timer times a piece of code and records to the Histogram its duration in seconds. 104 // 105 // (Caller MUST ensure the returned done function is called, and SHOULD use defer.) 106 func (h *HistogramValue) Timer() (done func()) { 107 t := time.Now() 108 109 return func() { 110 // use our h.Observe here to ensure h.h gets set in the same code 111 // path as the h.h.Observe() call, otherwise possibly racey. 112 h.ObserveDuration(time.Since(t)) 113 } 114 }