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

     1  package metrics
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/prometheus/client_golang/prometheus"
     7  )
     8  
     9  // A SummaryValue holds the tracking information for a specific Summary or a “Child” of a Summary.
    10  type SummaryValue struct {
    11  	// we want to duplicate this every WithLabels() call,
    12  	// so we don’t use a pointer here.
    13  	metric
    14  
    15  	s  prometheus.Summary
    16  	sv *prometheus.SummaryVec
    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 (s SummaryValue) WithLabels(labels ...Labeler) *SummaryValue {
    26  	// we are working with a new copy, so no mutex is necessary.
    27  	s.s = nil
    28  
    29  	s.labels = s.labels.With(labels...)
    30  
    31  	return &s
    32  }
    33  
    34  // Remove will remove a “Child” that matches the given labels from the metric, no longer exporting it.
    35  func (s *SummaryValue) Remove(labels ...Labeler) bool {
    36  	if s.sv == nil {
    37  		return false
    38  	}
    39  
    40  	return s.sv.Delete(s.labels.getMap())
    41  }
    42  
    43  // Clear removes all “Children” from the metric.
    44  func (s *SummaryValue) Clear() {
    45  	if s.sv != nil {
    46  		s.sv.Reset()
    47  	}
    48  }
    49  
    50  // Summary samples observations (usually things like request durations) over sliding windows of time,
    51  // and provides instantaneous insight into their distributions, frequencies, and sums.
    52  func Summary(name string, help string, options ...Option) *SummaryValue {
    53  	m := newMetric(name, help)
    54  
    55  	// prometheus library has deprecated DefaultObjectives, as the
    56  	// implementation documentation says that Summaries MUST allow for not
    57  	// having quantiles, and that this MUST be the default.
    58  	//
    59  	// As such, we set a default empty map here to override any default
    60  	// currently in use by the wrapped prometheus library.
    61  	m.summarySettings = &summarySettings{
    62  		objectives: make(map[float64]float64),
    63  	}
    64  
    65  	for _, opt := range options {
    66  		// in initialization, we throw the reverting function away
    67  		_ = opt(m)
    68  	}
    69  
    70  	s := &SummaryValue{
    71  		metric: *m,
    72  	}
    73  
    74  	opts := prometheus.SummaryOpts{
    75  		Name:       name,
    76  		Help:       help,
    77  		Objectives: m.objectives,
    78  		MaxAge:     m.maxAge,
    79  		AgeBuckets: m.ageBuckets,
    80  		BufCap:     m.bufCap,
    81  	}
    82  
    83  	if s.labels != nil {
    84  		if _, ok := m.labels.set.canSet["quantile"]; ok {
    85  			panic("summaries cannot allow \"quantile\" as a label")
    86  		}
    87  
    88  		s.sv = prometheus.NewSummaryVec(opts, s.labels.set.keys)
    89  		s.registry.MustRegister(s.sv)
    90  
    91  	} else {
    92  		s.s = prometheus.NewSummary(opts)
    93  		s.registry.MustRegister(s.s)
    94  	}
    95  
    96  	return s
    97  }
    98  
    99  // Observe records the given value into the Summary.
   100  func (s *SummaryValue) Observe(v float64) {
   101  	if s.s == nil {
   102  		// function is idempotent, and won’t step on others’ toes
   103  		s.s = s.sv.With(s.labels.getMap()).(prometheus.Summary)
   104  	}
   105  
   106  	s.s.Observe(v)
   107  }
   108  
   109  // ObserveDuration records the given Duration into the Summary.
   110  func (s *SummaryValue) ObserveDuration(d time.Duration) {
   111  	s.Observe(d.Seconds())
   112  }
   113  
   114  // Timer times a piece of code and records to the Summary its duration in seconds.
   115  //
   116  // (Caller MUST ensure the returned done function is called, and SHOULD use defer.)
   117  func (s *SummaryValue) Timer() (done func()) {
   118  	t := time.Now()
   119  
   120  	return func() {
   121  		// use our s.Observe here to ensure s.s gets set in the same code
   122  		// path as the s.s.Observe() call, otherwise possibly racey.
   123  		s.ObserveDuration(time.Since(t))
   124  	}
   125  }