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  }