Tizen_4.0 base
[platform/upstream/docker-engine.git] / 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         "sort"
20         "sync/atomic"
21
22         "github.com/golang/protobuf/proto"
23
24         dto "github.com/prometheus/client_model/go"
25 )
26
27 // A Histogram counts individual observations from an event or sample stream in
28 // configurable buckets. Similar to a summary, it also provides a sum of
29 // observations and an observation count.
30 //
31 // On the Prometheus server, quantiles can be calculated from a Histogram using
32 // the histogram_quantile function in the query language.
33 //
34 // Note that Histograms, in contrast to Summaries, can be aggregated with the
35 // Prometheus query language (see the documentation for detailed
36 // procedures). However, Histograms require the user to pre-define suitable
37 // buckets, and they are in general less accurate. The Observe method of a
38 // Histogram has a very low performance overhead in comparison with the Observe
39 // method of a Summary.
40 //
41 // To create Histogram instances, use NewHistogram.
42 type Histogram interface {
43         Metric
44         Collector
45
46         // Observe adds a single observation to the histogram.
47         Observe(float64)
48 }
49
50 // bucketLabel is used for the label that defines the upper bound of a
51 // bucket of a histogram ("le" -> "less or equal").
52 const bucketLabel = "le"
53
54 var (
55         // DefBuckets are the default Histogram buckets. The default buckets are
56         // tailored to broadly measure the response time (in seconds) of a
57         // network service. Most likely, however, you will be required to define
58         // buckets customized to your use case.
59         DefBuckets = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10}
60
61         errBucketLabelNotAllowed = fmt.Errorf(
62                 "%q is not allowed as label name in histograms", bucketLabel,
63         )
64 )
65
66 // LinearBuckets creates 'count' buckets, each 'width' wide, where the lowest
67 // bucket has an upper bound of 'start'. The final +Inf bucket is not counted
68 // and not included in the returned slice. The returned slice is meant to be
69 // used for the Buckets field of HistogramOpts.
70 //
71 // The function panics if 'count' is zero or negative.
72 func LinearBuckets(start, width float64, count int) []float64 {
73         if count < 1 {
74                 panic("LinearBuckets needs a positive count")
75         }
76         buckets := make([]float64, count)
77         for i := range buckets {
78                 buckets[i] = start
79                 start += width
80         }
81         return buckets
82 }
83
84 // ExponentialBuckets creates 'count' buckets, where the lowest bucket has an
85 // upper bound of 'start' and each following bucket's upper bound is 'factor'
86 // times the previous bucket's upper bound. The final +Inf bucket is not counted
87 // and not included in the returned slice. The returned slice is meant to be
88 // used for the Buckets field of HistogramOpts.
89 //
90 // The function panics if 'count' is 0 or negative, if 'start' is 0 or negative,
91 // or if 'factor' is less than or equal 1.
92 func ExponentialBuckets(start, factor float64, count int) []float64 {
93         if count < 1 {
94                 panic("ExponentialBuckets needs a positive count")
95         }
96         if start <= 0 {
97                 panic("ExponentialBuckets needs a positive start value")
98         }
99         if factor <= 1 {
100                 panic("ExponentialBuckets needs a factor greater than 1")
101         }
102         buckets := make([]float64, count)
103         for i := range buckets {
104                 buckets[i] = start
105                 start *= factor
106         }
107         return buckets
108 }
109
110 // HistogramOpts bundles the options for creating a Histogram metric. It is
111 // mandatory to set Name and Help to a non-empty string. All other fields are
112 // optional and can safely be left at their zero value.
113 type HistogramOpts struct {
114         // Namespace, Subsystem, and Name are components of the fully-qualified
115         // name of the Histogram (created by joining these components with
116         // "_"). Only Name is mandatory, the others merely help structuring the
117         // name. Note that the fully-qualified name of the Histogram must be a
118         // valid Prometheus metric name.
119         Namespace string
120         Subsystem string
121         Name      string
122
123         // Help provides information about this Histogram. Mandatory!
124         //
125         // Metrics with the same fully-qualified name must have the same Help
126         // string.
127         Help string
128
129         // ConstLabels are used to attach fixed labels to this
130         // Histogram. Histograms with the same fully-qualified name must have the
131         // same label names in their ConstLabels.
132         //
133         // Note that in most cases, labels have a value that varies during the
134         // lifetime of a process. Those labels are usually managed with a
135         // HistogramVec. ConstLabels serve only special purposes. One is for the
136         // special case where the value of a label does not change during the
137         // lifetime of a process, e.g. if the revision of the running binary is
138         // put into a label. Another, more advanced purpose is if more than one
139         // Collector needs to collect Histograms with the same fully-qualified
140         // name. In that case, those Summaries must differ in the values of
141         // their ConstLabels. See the Collector examples.
142         //
143         // If the value of a label never changes (not even between binaries),
144         // that label most likely should not be a label at all (but part of the
145         // metric name).
146         ConstLabels Labels
147
148         // Buckets defines the buckets into which observations are counted. Each
149         // element in the slice is the upper inclusive bound of a bucket. The
150         // values must be sorted in strictly increasing order. There is no need
151         // to add a highest bucket with +Inf bound, it will be added
152         // implicitly. The default value is DefBuckets.
153         Buckets []float64
154 }
155
156 // NewHistogram creates a new Histogram based on the provided HistogramOpts. It
157 // panics if the buckets in HistogramOpts are not in strictly increasing order.
158 func NewHistogram(opts HistogramOpts) Histogram {
159         return newHistogram(
160                 NewDesc(
161                         BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
162                         opts.Help,
163                         nil,
164                         opts.ConstLabels,
165                 ),
166                 opts,
167         )
168 }
169
170 func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogram {
171         if len(desc.variableLabels) != len(labelValues) {
172                 panic(errInconsistentCardinality)
173         }
174
175         for _, n := range desc.variableLabels {
176                 if n == bucketLabel {
177                         panic(errBucketLabelNotAllowed)
178                 }
179         }
180         for _, lp := range desc.constLabelPairs {
181                 if lp.GetName() == bucketLabel {
182                         panic(errBucketLabelNotAllowed)
183                 }
184         }
185
186         if len(opts.Buckets) == 0 {
187                 opts.Buckets = DefBuckets
188         }
189
190         h := &histogram{
191                 desc:        desc,
192                 upperBounds: opts.Buckets,
193                 labelPairs:  makeLabelPairs(desc, labelValues),
194         }
195         for i, upperBound := range h.upperBounds {
196                 if i < len(h.upperBounds)-1 {
197                         if upperBound >= h.upperBounds[i+1] {
198                                 panic(fmt.Errorf(
199                                         "histogram buckets must be in increasing order: %f >= %f",
200                                         upperBound, h.upperBounds[i+1],
201                                 ))
202                         }
203                 } else {
204                         if math.IsInf(upperBound, +1) {
205                                 // The +Inf bucket is implicit. Remove it here.
206                                 h.upperBounds = h.upperBounds[:i]
207                         }
208                 }
209         }
210         // Finally we know the final length of h.upperBounds and can make counts.
211         h.counts = make([]uint64, len(h.upperBounds))
212
213         h.Init(h) // Init self-collection.
214         return h
215 }
216
217 type histogram struct {
218         // sumBits contains the bits of the float64 representing the sum of all
219         // observations. sumBits and count have to go first in the struct to
220         // guarantee alignment for atomic operations.
221         // http://golang.org/pkg/sync/atomic/#pkg-note-BUG
222         sumBits uint64
223         count   uint64
224
225         SelfCollector
226         // Note that there is no mutex required.
227
228         desc *Desc
229
230         upperBounds []float64
231         counts      []uint64
232
233         labelPairs []*dto.LabelPair
234 }
235
236 func (h *histogram) Desc() *Desc {
237         return h.desc
238 }
239
240 func (h *histogram) Observe(v float64) {
241         // TODO(beorn7): For small numbers of buckets (<30), a linear search is
242         // slightly faster than the binary search. If we really care, we could
243         // switch from one search strategy to the other depending on the number
244         // of buckets.
245         //
246         // Microbenchmarks (BenchmarkHistogramNoLabels):
247         // 11 buckets: 38.3 ns/op linear - binary 48.7 ns/op
248         // 100 buckets: 78.1 ns/op linear - binary 54.9 ns/op
249         // 300 buckets: 154 ns/op linear - binary 61.6 ns/op
250         i := sort.SearchFloat64s(h.upperBounds, v)
251         if i < len(h.counts) {
252                 atomic.AddUint64(&h.counts[i], 1)
253         }
254         atomic.AddUint64(&h.count, 1)
255         for {
256                 oldBits := atomic.LoadUint64(&h.sumBits)
257                 newBits := math.Float64bits(math.Float64frombits(oldBits) + v)
258                 if atomic.CompareAndSwapUint64(&h.sumBits, oldBits, newBits) {
259                         break
260                 }
261         }
262 }
263
264 func (h *histogram) Write(out *dto.Metric) error {
265         his := &dto.Histogram{}
266         buckets := make([]*dto.Bucket, len(h.upperBounds))
267
268         his.SampleSum = proto.Float64(math.Float64frombits(atomic.LoadUint64(&h.sumBits)))
269         his.SampleCount = proto.Uint64(atomic.LoadUint64(&h.count))
270         var count uint64
271         for i, upperBound := range h.upperBounds {
272                 count += atomic.LoadUint64(&h.counts[i])
273                 buckets[i] = &dto.Bucket{
274                         CumulativeCount: proto.Uint64(count),
275                         UpperBound:      proto.Float64(upperBound),
276                 }
277         }
278         his.Bucket = buckets
279         out.Histogram = his
280         out.Label = h.labelPairs
281         return nil
282 }
283
284 // HistogramVec is a Collector that bundles a set of Histograms that all share the
285 // same Desc, but have different values for their variable labels. This is used
286 // if you want to count the same thing partitioned by various dimensions
287 // (e.g. HTTP request latencies, partitioned by status code and method). Create
288 // instances with NewHistogramVec.
289 type HistogramVec struct {
290         MetricVec
291 }
292
293 // NewHistogramVec creates a new HistogramVec based on the provided HistogramOpts and
294 // partitioned by the given label names. At least one label name must be
295 // provided.
296 func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec {
297         desc := NewDesc(
298                 BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
299                 opts.Help,
300                 labelNames,
301                 opts.ConstLabels,
302         )
303         return &HistogramVec{
304                 MetricVec: MetricVec{
305                         children: map[uint64]Metric{},
306                         desc:     desc,
307                         newMetric: func(lvs ...string) Metric {
308                                 return newHistogram(desc, opts, lvs...)
309                         },
310                 },
311         }
312 }
313
314 // GetMetricWithLabelValues replaces the method of the same name in
315 // MetricVec. The difference is that this method returns a Histogram and not a
316 // Metric so that no type conversion is required.
317 func (m *HistogramVec) GetMetricWithLabelValues(lvs ...string) (Histogram, error) {
318         metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...)
319         if metric != nil {
320                 return metric.(Histogram), err
321         }
322         return nil, err
323 }
324
325 // GetMetricWith replaces the method of the same name in MetricVec. The
326 // difference is that this method returns a Histogram and not a Metric so that no
327 // type conversion is required.
328 func (m *HistogramVec) GetMetricWith(labels Labels) (Histogram, error) {
329         metric, err := m.MetricVec.GetMetricWith(labels)
330         if metric != nil {
331                 return metric.(Histogram), err
332         }
333         return nil, err
334 }
335
336 // WithLabelValues works as GetMetricWithLabelValues, but panics where
337 // GetMetricWithLabelValues would have returned an error. By not returning an
338 // error, WithLabelValues allows shortcuts like
339 //     myVec.WithLabelValues("404", "GET").Observe(42.21)
340 func (m *HistogramVec) WithLabelValues(lvs ...string) Histogram {
341         return m.MetricVec.WithLabelValues(lvs...).(Histogram)
342 }
343
344 // With works as GetMetricWith, but panics where GetMetricWithLabels would have
345 // returned an error. By not returning an error, With allows shortcuts like
346 //     myVec.With(Labels{"code": "404", "method": "GET"}).Observe(42.21)
347 func (m *HistogramVec) With(labels Labels) Histogram {
348         return m.MetricVec.With(labels).(Histogram)
349 }
350
351 type constHistogram struct {
352         desc       *Desc
353         count      uint64
354         sum        float64
355         buckets    map[float64]uint64
356         labelPairs []*dto.LabelPair
357 }
358
359 func (h *constHistogram) Desc() *Desc {
360         return h.desc
361 }
362
363 func (h *constHistogram) Write(out *dto.Metric) error {
364         his := &dto.Histogram{}
365         buckets := make([]*dto.Bucket, 0, len(h.buckets))
366
367         his.SampleCount = proto.Uint64(h.count)
368         his.SampleSum = proto.Float64(h.sum)
369
370         for upperBound, count := range h.buckets {
371                 buckets = append(buckets, &dto.Bucket{
372                         CumulativeCount: proto.Uint64(count),
373                         UpperBound:      proto.Float64(upperBound),
374                 })
375         }
376
377         if len(buckets) > 0 {
378                 sort.Sort(buckSort(buckets))
379         }
380         his.Bucket = buckets
381
382         out.Histogram = his
383         out.Label = h.labelPairs
384
385         return nil
386 }
387
388 // NewConstHistogram returns a metric representing a Prometheus histogram with
389 // fixed values for the count, sum, and bucket counts. As those parameters
390 // cannot be changed, the returned value does not implement the Histogram
391 // interface (but only the Metric interface). Users of this package will not
392 // have much use for it in regular operations. However, when implementing custom
393 // Collectors, it is useful as a throw-away metric that is generated on the fly
394 // to send it to Prometheus in the Collect method.
395 //
396 // buckets is a map of upper bounds to cumulative counts, excluding the +Inf
397 // bucket.
398 //
399 // NewConstHistogram returns an error if the length of labelValues is not
400 // consistent with the variable labels in Desc.
401 func NewConstHistogram(
402         desc *Desc,
403         count uint64,
404         sum float64,
405         buckets map[float64]uint64,
406         labelValues ...string,
407 ) (Metric, error) {
408         if len(desc.variableLabels) != len(labelValues) {
409                 return nil, errInconsistentCardinality
410         }
411         return &constHistogram{
412                 desc:       desc,
413                 count:      count,
414                 sum:        sum,
415                 buckets:    buckets,
416                 labelPairs: makeLabelPairs(desc, labelValues),
417         }, nil
418 }
419
420 // MustNewConstHistogram is a version of NewConstHistogram that panics where
421 // NewConstMetric would have returned an error.
422 func MustNewConstHistogram(
423         desc *Desc,
424         count uint64,
425         sum float64,
426         buckets map[float64]uint64,
427         labelValues ...string,
428 ) Metric {
429         m, err := NewConstHistogram(desc, count, sum, buckets, labelValues...)
430         if err != nil {
431                 panic(err)
432         }
433         return m
434 }
435
436 type buckSort []*dto.Bucket
437
438 func (s buckSort) Len() int {
439         return len(s)
440 }
441
442 func (s buckSort) Swap(i, j int) {
443         s[i], s[j] = s[j], s[i]
444 }
445
446 func (s buckSort) Less(i, j int) bool {
447         return s[i].GetUpperBound() < s[j].GetUpperBound()
448 }