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 }