histogram.go 21 KB

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