Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / github.com / prometheus / client_golang / prometheus / histogram.go
1 // Copyright 2015 The Prometheus Authors
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 package prometheus
15
16 import (
17         "fmt"
18         "math"
19         "runtime"
20         "sort"
21         "sync"
22         "sync/atomic"
23
24         "github.com/golang/protobuf/proto"
25
26         dto "github.com/prometheus/client_model/go"
27 )
28
29 // A Histogram counts individual observations from an event or sample stream in
30 // configurable buckets. Similar to a summary, it also provides a sum of
31 // observations and an observation count.
32 //
33 // On the Prometheus server, quantiles can be calculated from a Histogram using
34 // the histogram_quantile function in the query language.
35 //
36 // Note that Histograms, in contrast to Summaries, can be aggregated with the
37 // Prometheus query language (see the documentation for detailed
38 // procedures). However, Histograms require the user to pre-define suitable
39 // buckets, and they are in general less accurate. The Observe method of a
40 // Histogram has a very low performance overhead in comparison with the Observe
41 // method of a Summary.
42 //
43 // To create Histogram instances, use NewHistogram.
44 type Histogram interface {
45         Metric
46         Collector
47
48         // Observe adds a single observation to the histogram.
49         Observe(float64)
50 }
51
52 // bucketLabel is used for the label that defines the upper bound of a
53 // bucket of a histogram ("le" -> "less or equal").
54 const bucketLabel = "le"
55
56 // DefBuckets are the default Histogram buckets. The default buckets are
57 // tailored to broadly measure the response time (in seconds) of a network
58 // service. Most likely, however, you will be required to define buckets
59 // customized to your use case.
60 var (
61         DefBuckets = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10}
62
63         errBucketLabelNotAllowed = fmt.Errorf(
64                 "%q is not allowed as label name in histograms", bucketLabel,
65         )
66 )
67
68 // LinearBuckets creates 'count' buckets, each 'width' wide, where the lowest
69 // bucket has an upper bound of 'start'. The final +Inf bucket is not counted
70 // and not included in the returned slice. The returned slice is meant to be
71 // used for the Buckets field of HistogramOpts.
72 //
73 // The function panics if 'count' is zero or negative.
74 func LinearBuckets(start, width float64, count int) []float64 {
75         if count < 1 {
76                 panic("LinearBuckets needs a positive count")
77         }
78         buckets := make([]float64, count)
79         for i := range buckets {
80                 buckets[i] = start
81                 start += width
82         }
83         return buckets
84 }
85
86 // ExponentialBuckets creates 'count' buckets, where the lowest bucket has an
87 // upper bound of 'start' and each following bucket's upper bound is 'factor'
88 // times the previous bucket's upper bound. The final +Inf bucket is not counted
89 // and not included in the returned slice. The returned slice is meant to be
90 // used for the Buckets field of HistogramOpts.
91 //
92 // The function panics if 'count' is 0 or negative, if 'start' is 0 or negative,
93 // or if 'factor' is less than or equal 1.
94 func ExponentialBuckets(start, factor float64, count int) []float64 {
95         if count < 1 {
96                 panic("ExponentialBuckets needs a positive count")
97         }
98         if start <= 0 {
99                 panic("ExponentialBuckets needs a positive start value")
100         }
101         if factor <= 1 {
102                 panic("ExponentialBuckets needs a factor greater than 1")
103         }
104         buckets := make([]float64, count)
105         for i := range buckets {
106                 buckets[i] = start
107                 start *= factor
108         }
109         return buckets
110 }
111
112 // HistogramOpts bundles the options for creating a Histogram metric. It is
113 // mandatory to set Name to a non-empty string. All other fields are optional
114 // and can safely be left at their zero value, although it is strongly
115 // encouraged to set a Help string.
116 type HistogramOpts struct {
117         // Namespace, Subsystem, and Name are components of the fully-qualified
118         // name of the Histogram (created by joining these components with
119         // "_"). Only Name is mandatory, the others merely help structuring the
120         // name. Note that the fully-qualified name of the Histogram must be a
121         // valid Prometheus metric name.
122         Namespace string
123         Subsystem string
124         Name      string
125
126         // Help provides information about this Histogram.
127         //
128         // Metrics with the same fully-qualified name must have the same Help
129         // string.
130         Help string
131
132         // ConstLabels are used to attach fixed labels to this metric. Metrics
133         // with the same fully-qualified name must have the same label names in
134         // their ConstLabels.
135         //
136         // ConstLabels are only used rarely. In particular, do not use them to
137         // attach the same labels to all your metrics. Those use cases are
138         // better covered by target labels set by the scraping Prometheus
139         // server, or by one specific metric (e.g. a build_info or a
140         // machine_role metric). See also
141         // https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels
142         ConstLabels Labels
143
144         // Buckets defines the buckets into which observations are counted. Each
145         // element in the slice is the upper inclusive bound of a bucket. The
146         // values must be sorted in strictly increasing order. There is no need
147         // to add a highest bucket with +Inf bound, it will be added
148         // implicitly. The default value is DefBuckets.
149         Buckets []float64
150 }
151
152 // NewHistogram creates a new Histogram based on the provided HistogramOpts. It
153 // panics if the buckets in HistogramOpts are not in strictly increasing order.
154 func NewHistogram(opts HistogramOpts) Histogram {
155         return newHistogram(
156                 NewDesc(
157                         BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
158                         opts.Help,
159                         nil,
160                         opts.ConstLabels,
161                 ),
162                 opts,
163         )
164 }
165
166 func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogram {
167         if len(desc.variableLabels) != len(labelValues) {
168                 panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, labelValues))
169         }
170
171         for _, n := range desc.variableLabels {
172                 if n == bucketLabel {
173                         panic(errBucketLabelNotAllowed)
174                 }
175         }
176         for _, lp := range desc.constLabelPairs {
177                 if lp.GetName() == bucketLabel {
178                         panic(errBucketLabelNotAllowed)
179                 }
180         }
181
182         if len(opts.Buckets) == 0 {
183                 opts.Buckets = DefBuckets
184         }
185
186         h := &histogram{
187                 desc:        desc,
188                 upperBounds: opts.Buckets,
189                 labelPairs:  makeLabelPairs(desc, labelValues),
190                 counts:      [2]*histogramCounts{&histogramCounts{}, &histogramCounts{}},
191         }
192         for i, upperBound := range h.upperBounds {
193                 if i < len(h.upperBounds)-1 {
194                         if upperBound >= h.upperBounds[i+1] {
195                                 panic(fmt.Errorf(
196                                         "histogram buckets must be in increasing order: %f >= %f",
197                                         upperBound, h.upperBounds[i+1],
198                                 ))
199                         }
200                 } else {
201                         if math.IsInf(upperBound, +1) {
202                                 // The +Inf bucket is implicit. Remove it here.
203                                 h.upperBounds = h.upperBounds[:i]
204                         }
205                 }
206         }
207         // Finally we know the final length of h.upperBounds and can make buckets
208         // for both counts:
209         h.counts[0].buckets = make([]uint64, len(h.upperBounds))
210         h.counts[1].buckets = make([]uint64, len(h.upperBounds))
211
212         h.init(h) // Init self-collection.
213         return h
214 }
215
216 type histogramCounts struct {
217         // sumBits contains the bits of the float64 representing the sum of all
218         // observations. sumBits and count have to go first in the struct to
219         // guarantee alignment for atomic operations.
220         // http://golang.org/pkg/sync/atomic/#pkg-note-BUG
221         sumBits uint64
222         count   uint64
223         buckets []uint64
224 }
225
226 type histogram struct {
227         // countAndHotIdx is a complicated one. For lock-free yet atomic
228         // observations, we need to save the total count of observations again,
229         // combined with the index of the currently-hot counts struct, so that
230         // we can perform the operation on both values atomically. The least
231         // significant bit defines the hot counts struct. The remaining 63 bits
232         // represent the total count of observations. This happens under the
233         // assumption that the 63bit count will never overflow. Rationale: An
234         // observations takes about 30ns. Let's assume it could happen in
235         // 10ns. Overflowing the counter will then take at least (2^63)*10ns,
236         // which is about 3000 years.
237         //
238         // This has to be first in the struct for 64bit alignment. See
239         // http://golang.org/pkg/sync/atomic/#pkg-note-BUG
240         countAndHotIdx uint64
241
242         selfCollector
243         desc     *Desc
244         writeMtx sync.Mutex // Only used in the Write method.
245
246         upperBounds []float64
247
248         // Two counts, one is "hot" for lock-free observations, the other is
249         // "cold" for writing out a dto.Metric. It has to be an array of
250         // pointers to guarantee 64bit alignment of the histogramCounts, see
251         // http://golang.org/pkg/sync/atomic/#pkg-note-BUG.
252         counts [2]*histogramCounts
253         hotIdx int // Index of currently-hot counts. Only used within Write.
254
255         labelPairs []*dto.LabelPair
256 }
257
258 func (h *histogram) Desc() *Desc {
259         return h.desc
260 }
261
262 func (h *histogram) Observe(v float64) {
263         // TODO(beorn7): For small numbers of buckets (<30), a linear search is
264         // slightly faster than the binary search. If we really care, we could
265         // switch from one search strategy to the other depending on the number
266         // of buckets.
267         //
268         // Microbenchmarks (BenchmarkHistogramNoLabels):
269         // 11 buckets: 38.3 ns/op linear - binary 48.7 ns/op
270         // 100 buckets: 78.1 ns/op linear - binary 54.9 ns/op
271         // 300 buckets: 154 ns/op linear - binary 61.6 ns/op
272         i := sort.SearchFloat64s(h.upperBounds, v)
273
274         // We increment h.countAndHotIdx by 2 so that the counter in the upper
275         // 63 bits gets incremented by 1. At the same time, we get the new value
276         // back, which we can use to find the currently-hot counts.
277         n := atomic.AddUint64(&h.countAndHotIdx, 2)
278         hotCounts := h.counts[n%2]
279
280         if i < len(h.upperBounds) {
281                 atomic.AddUint64(&hotCounts.buckets[i], 1)
282         }
283         for {
284                 oldBits := atomic.LoadUint64(&hotCounts.sumBits)
285                 newBits := math.Float64bits(math.Float64frombits(oldBits) + v)
286                 if atomic.CompareAndSwapUint64(&hotCounts.sumBits, oldBits, newBits) {
287                         break
288                 }
289         }
290         // Increment count last as we take it as a signal that the observation
291         // is complete.
292         atomic.AddUint64(&hotCounts.count, 1)
293 }
294
295 func (h *histogram) Write(out *dto.Metric) error {
296         var (
297                 his                   = &dto.Histogram{}
298                 buckets               = make([]*dto.Bucket, len(h.upperBounds))
299                 hotCounts, coldCounts *histogramCounts
300                 count                 uint64
301         )
302
303         // For simplicity, we mutex the rest of this method. It is not in the
304         // hot path, i.e.  Observe is called much more often than Write. The
305         // complication of making Write lock-free isn't worth it.
306         h.writeMtx.Lock()
307         defer h.writeMtx.Unlock()
308
309         // This is a bit arcane, which is why the following spells out this if
310         // clause in English:
311         //
312         // If the currently-hot counts struct is #0, we atomically increment
313         // h.countAndHotIdx by 1 so that from now on Observe will use the counts
314         // struct #1. Furthermore, the atomic increment gives us the new value,
315         // which, in its most significant 63 bits, tells us the count of
316         // observations done so far up to and including currently ongoing
317         // observations still using the counts struct just changed from hot to
318         // cold. To have a normal uint64 for the count, we bitshift by 1 and
319         // save the result in count. We also set h.hotIdx to 1 for the next
320         // Write call, and we will refer to counts #1 as hotCounts and to counts
321         // #0 as coldCounts.
322         //
323         // If the currently-hot counts struct is #1, we do the corresponding
324         // things the other way round. We have to _decrement_ h.countAndHotIdx
325         // (which is a bit arcane in itself, as we have to express -1 with an
326         // unsigned int...).
327         if h.hotIdx == 0 {
328                 count = atomic.AddUint64(&h.countAndHotIdx, 1) >> 1
329                 h.hotIdx = 1
330                 hotCounts = h.counts[1]
331                 coldCounts = h.counts[0]
332         } else {
333                 count = atomic.AddUint64(&h.countAndHotIdx, ^uint64(0)) >> 1 // Decrement.
334                 h.hotIdx = 0
335                 hotCounts = h.counts[0]
336                 coldCounts = h.counts[1]
337         }
338
339         // Now we have to wait for the now-declared-cold counts to actually cool
340         // down, i.e. wait for all observations still using it to finish. That's
341         // the case once the count in the cold counts struct is the same as the
342         // one atomically retrieved from the upper 63bits of h.countAndHotIdx.
343         for {
344                 if count == atomic.LoadUint64(&coldCounts.count) {
345                         break
346                 }
347                 runtime.Gosched() // Let observations get work done.
348         }
349
350         his.SampleCount = proto.Uint64(count)
351         his.SampleSum = proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits)))
352         var cumCount uint64
353         for i, upperBound := range h.upperBounds {
354                 cumCount += atomic.LoadUint64(&coldCounts.buckets[i])
355                 buckets[i] = &dto.Bucket{
356                         CumulativeCount: proto.Uint64(cumCount),
357                         UpperBound:      proto.Float64(upperBound),
358                 }
359         }
360
361         his.Bucket = buckets
362         out.Histogram = his
363         out.Label = h.labelPairs
364
365         // Finally add all the cold counts to the new hot counts and reset the cold counts.
366         atomic.AddUint64(&hotCounts.count, count)
367         atomic.StoreUint64(&coldCounts.count, 0)
368         for {
369                 oldBits := atomic.LoadUint64(&hotCounts.sumBits)
370                 newBits := math.Float64bits(math.Float64frombits(oldBits) + his.GetSampleSum())
371                 if atomic.CompareAndSwapUint64(&hotCounts.sumBits, oldBits, newBits) {
372                         atomic.StoreUint64(&coldCounts.sumBits, 0)
373                         break
374                 }
375         }
376         for i := range h.upperBounds {
377                 atomic.AddUint64(&hotCounts.buckets[i], atomic.LoadUint64(&coldCounts.buckets[i]))
378                 atomic.StoreUint64(&coldCounts.buckets[i], 0)
379         }
380         return nil
381 }
382
383 // HistogramVec is a Collector that bundles a set of Histograms that all share the
384 // same Desc, but have different values for their variable labels. This is used
385 // if you want to count the same thing partitioned by various dimensions
386 // (e.g. HTTP request latencies, partitioned by status code and method). Create
387 // instances with NewHistogramVec.
388 type HistogramVec struct {
389         *metricVec
390 }
391
392 // NewHistogramVec creates a new HistogramVec based on the provided HistogramOpts and
393 // partitioned by the given label names.
394 func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec {
395         desc := NewDesc(
396                 BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
397                 opts.Help,
398                 labelNames,
399                 opts.ConstLabels,
400         )
401         return &HistogramVec{
402                 metricVec: newMetricVec(desc, func(lvs ...string) Metric {
403                         return newHistogram(desc, opts, lvs...)
404                 }),
405         }
406 }
407
408 // GetMetricWithLabelValues returns the Histogram for the given slice of label
409 // values (same order as the VariableLabels in Desc). If that combination of
410 // label values is accessed for the first time, a new Histogram is created.
411 //
412 // It is possible to call this method without using the returned Histogram to only
413 // create the new Histogram but leave it at its starting value, a Histogram without
414 // any observations.
415 //
416 // Keeping the Histogram for later use is possible (and should be considered if
417 // performance is critical), but keep in mind that Reset, DeleteLabelValues and
418 // Delete can be used to delete the Histogram from the HistogramVec. In that case, the
419 // Histogram will still exist, but it will not be exported anymore, even if a
420 // Histogram with the same label values is created later. See also the CounterVec
421 // example.
422 //
423 // An error is returned if the number of label values is not the same as the
424 // number of VariableLabels in Desc (minus any curried labels).
425 //
426 // Note that for more than one label value, this method is prone to mistakes
427 // caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
428 // an alternative to avoid that type of mistake. For higher label numbers, the
429 // latter has a much more readable (albeit more verbose) syntax, but it comes
430 // with a performance overhead (for creating and processing the Labels map).
431 // See also the GaugeVec example.
432 func (v *HistogramVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) {
433         metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
434         if metric != nil {
435                 return metric.(Observer), err
436         }
437         return nil, err
438 }
439
440 // GetMetricWith returns the Histogram for the given Labels map (the label names
441 // must match those of the VariableLabels in Desc). If that label map is
442 // accessed for the first time, a new Histogram is created. Implications of
443 // creating a Histogram without using it and keeping the Histogram for later use
444 // are the same as for GetMetricWithLabelValues.
445 //
446 // An error is returned if the number and names of the Labels are inconsistent
447 // with those of the VariableLabels in Desc (minus any curried labels).
448 //
449 // This method is used for the same purpose as
450 // GetMetricWithLabelValues(...string). See there for pros and cons of the two
451 // methods.
452 func (v *HistogramVec) GetMetricWith(labels Labels) (Observer, error) {
453         metric, err := v.metricVec.getMetricWith(labels)
454         if metric != nil {
455                 return metric.(Observer), err
456         }
457         return nil, err
458 }
459
460 // WithLabelValues works as GetMetricWithLabelValues, but panics where
461 // GetMetricWithLabelValues would have returned an error. Not returning an
462 // error allows shortcuts like
463 //     myVec.WithLabelValues("404", "GET").Observe(42.21)
464 func (v *HistogramVec) WithLabelValues(lvs ...string) Observer {
465         h, err := v.GetMetricWithLabelValues(lvs...)
466         if err != nil {
467                 panic(err)
468         }
469         return h
470 }
471
472 // With works as GetMetricWith but panics where GetMetricWithLabels would have
473 // returned an error. Not returning an error allows shortcuts like
474 //     myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21)
475 func (v *HistogramVec) With(labels Labels) Observer {
476         h, err := v.GetMetricWith(labels)
477         if err != nil {
478                 panic(err)
479         }
480         return h
481 }
482
483 // CurryWith returns a vector curried with the provided labels, i.e. the
484 // returned vector has those labels pre-set for all labeled operations performed
485 // on it. The cardinality of the curried vector is reduced accordingly. The
486 // order of the remaining labels stays the same (just with the curried labels
487 // taken out of the sequence – which is relevant for the
488 // (GetMetric)WithLabelValues methods). It is possible to curry a curried
489 // vector, but only with labels not yet used for currying before.
490 //
491 // The metrics contained in the HistogramVec are shared between the curried and
492 // uncurried vectors. They are just accessed differently. Curried and uncurried
493 // vectors behave identically in terms of collection. Only one must be
494 // registered with a given registry (usually the uncurried version). The Reset
495 // method deletes all metrics, even if called on a curried vector.
496 func (v *HistogramVec) CurryWith(labels Labels) (ObserverVec, error) {
497         vec, err := v.curryWith(labels)
498         if vec != nil {
499                 return &HistogramVec{vec}, err
500         }
501         return nil, err
502 }
503
504 // MustCurryWith works as CurryWith but panics where CurryWith would have
505 // returned an error.
506 func (v *HistogramVec) MustCurryWith(labels Labels) ObserverVec {
507         vec, err := v.CurryWith(labels)
508         if err != nil {
509                 panic(err)
510         }
511         return vec
512 }
513
514 type constHistogram struct {
515         desc       *Desc
516         count      uint64
517         sum        float64
518         buckets    map[float64]uint64
519         labelPairs []*dto.LabelPair
520 }
521
522 func (h *constHistogram) Desc() *Desc {
523         return h.desc
524 }
525
526 func (h *constHistogram) Write(out *dto.Metric) error {
527         his := &dto.Histogram{}
528         buckets := make([]*dto.Bucket, 0, len(h.buckets))
529
530         his.SampleCount = proto.Uint64(h.count)
531         his.SampleSum = proto.Float64(h.sum)
532
533         for upperBound, count := range h.buckets {
534                 buckets = append(buckets, &dto.Bucket{
535                         CumulativeCount: proto.Uint64(count),
536                         UpperBound:      proto.Float64(upperBound),
537                 })
538         }
539
540         if len(buckets) > 0 {
541                 sort.Sort(buckSort(buckets))
542         }
543         his.Bucket = buckets
544
545         out.Histogram = his
546         out.Label = h.labelPairs
547
548         return nil
549 }
550
551 // NewConstHistogram returns a metric representing a Prometheus histogram with
552 // fixed values for the count, sum, and bucket counts. As those parameters
553 // cannot be changed, the returned value does not implement the Histogram
554 // interface (but only the Metric interface). Users of this package will not
555 // have much use for it in regular operations. However, when implementing custom
556 // Collectors, it is useful as a throw-away metric that is generated on the fly
557 // to send it to Prometheus in the Collect method.
558 //
559 // buckets is a map of upper bounds to cumulative counts, excluding the +Inf
560 // bucket.
561 //
562 // NewConstHistogram returns an error if the length of labelValues is not
563 // consistent with the variable labels in Desc or if Desc is invalid.
564 func NewConstHistogram(
565         desc *Desc,
566         count uint64,
567         sum float64,
568         buckets map[float64]uint64,
569         labelValues ...string,
570 ) (Metric, error) {
571         if desc.err != nil {
572                 return nil, desc.err
573         }
574         if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
575                 return nil, err
576         }
577         return &constHistogram{
578                 desc:       desc,
579                 count:      count,
580                 sum:        sum,
581                 buckets:    buckets,
582                 labelPairs: makeLabelPairs(desc, labelValues),
583         }, nil
584 }
585
586 // MustNewConstHistogram is a version of NewConstHistogram that panics where
587 // NewConstMetric would have returned an error.
588 func MustNewConstHistogram(
589         desc *Desc,
590         count uint64,
591         sum float64,
592         buckets map[float64]uint64,
593         labelValues ...string,
594 ) Metric {
595         m, err := NewConstHistogram(desc, count, sum, buckets, labelValues...)
596         if err != nil {
597                 panic(err)
598         }
599         return m
600 }
601
602 type buckSort []*dto.Bucket
603
604 func (s buckSort) Len() int {
605         return len(s)
606 }
607
608 func (s buckSort) Swap(i, j int) {
609         s[i], s[j] = s[j], s[i]
610 }
611
612 func (s buckSort) Less(i, j int) bool {
613         return s[i].GetUpperBound() < s[j].GetUpperBound()
614 }