value.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. // Copyright 2014 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. "errors"
  16. "fmt"
  17. "math"
  18. "sort"
  19. "sync/atomic"
  20. "time"
  21. dto "github.com/prometheus/client_model/go"
  22. "github.com/golang/protobuf/proto"
  23. )
  24. // ValueType is an enumeration of metric types that represent a simple value.
  25. type ValueType int
  26. // Possible values for the ValueType enum.
  27. const (
  28. _ ValueType = iota
  29. CounterValue
  30. GaugeValue
  31. UntypedValue
  32. )
  33. var errInconsistentCardinality = errors.New("inconsistent label cardinality")
  34. // value is a generic metric for simple values. It implements Metric, Collector,
  35. // Counter, Gauge, and Untyped. Its effective type is determined by
  36. // ValueType. This is a low-level building block used by the library to back the
  37. // implementations of Counter, Gauge, and Untyped.
  38. type value struct {
  39. // valBits contains the bits of the represented float64 value. It has
  40. // to go first in the struct to guarantee alignment for atomic
  41. // operations. http://golang.org/pkg/sync/atomic/#pkg-note-BUG
  42. valBits uint64
  43. selfCollector
  44. desc *Desc
  45. valType ValueType
  46. labelPairs []*dto.LabelPair
  47. }
  48. // newValue returns a newly allocated value with the given Desc, ValueType,
  49. // sample value and label values. It panics if the number of label
  50. // values is different from the number of variable labels in Desc.
  51. func newValue(desc *Desc, valueType ValueType, val float64, labelValues ...string) *value {
  52. if len(labelValues) != len(desc.variableLabels) {
  53. panic(errInconsistentCardinality)
  54. }
  55. result := &value{
  56. desc: desc,
  57. valType: valueType,
  58. valBits: math.Float64bits(val),
  59. labelPairs: makeLabelPairs(desc, labelValues),
  60. }
  61. result.init(result)
  62. return result
  63. }
  64. func (v *value) Desc() *Desc {
  65. return v.desc
  66. }
  67. func (v *value) Set(val float64) {
  68. atomic.StoreUint64(&v.valBits, math.Float64bits(val))
  69. }
  70. func (v *value) SetToCurrentTime() {
  71. v.Set(float64(time.Now().UnixNano()) / 1e9)
  72. }
  73. func (v *value) Inc() {
  74. v.Add(1)
  75. }
  76. func (v *value) Dec() {
  77. v.Add(-1)
  78. }
  79. func (v *value) Add(val float64) {
  80. for {
  81. oldBits := atomic.LoadUint64(&v.valBits)
  82. newBits := math.Float64bits(math.Float64frombits(oldBits) + val)
  83. if atomic.CompareAndSwapUint64(&v.valBits, oldBits, newBits) {
  84. return
  85. }
  86. }
  87. }
  88. func (v *value) Sub(val float64) {
  89. v.Add(val * -1)
  90. }
  91. func (v *value) Write(out *dto.Metric) error {
  92. val := math.Float64frombits(atomic.LoadUint64(&v.valBits))
  93. return populateMetric(v.valType, val, v.labelPairs, out)
  94. }
  95. // valueFunc is a generic metric for simple values retrieved on collect time
  96. // from a function. It implements Metric and Collector. Its effective type is
  97. // determined by ValueType. This is a low-level building block used by the
  98. // library to back the implementations of CounterFunc, GaugeFunc, and
  99. // UntypedFunc.
  100. type valueFunc struct {
  101. selfCollector
  102. desc *Desc
  103. valType ValueType
  104. function func() float64
  105. labelPairs []*dto.LabelPair
  106. }
  107. // newValueFunc returns a newly allocated valueFunc with the given Desc and
  108. // ValueType. The value reported is determined by calling the given function
  109. // from within the Write method. Take into account that metric collection may
  110. // happen concurrently. If that results in concurrent calls to Write, like in
  111. // the case where a valueFunc is directly registered with Prometheus, the
  112. // provided function must be concurrency-safe.
  113. func newValueFunc(desc *Desc, valueType ValueType, function func() float64) *valueFunc {
  114. result := &valueFunc{
  115. desc: desc,
  116. valType: valueType,
  117. function: function,
  118. labelPairs: makeLabelPairs(desc, nil),
  119. }
  120. result.init(result)
  121. return result
  122. }
  123. func (v *valueFunc) Desc() *Desc {
  124. return v.desc
  125. }
  126. func (v *valueFunc) Write(out *dto.Metric) error {
  127. return populateMetric(v.valType, v.function(), v.labelPairs, out)
  128. }
  129. // NewConstMetric returns a metric with one fixed value that cannot be
  130. // changed. Users of this package will not have much use for it in regular
  131. // operations. However, when implementing custom Collectors, it is useful as a
  132. // throw-away metric that is generated on the fly to send it to Prometheus in
  133. // the Collect method. NewConstMetric returns an error if the length of
  134. // labelValues is not consistent with the variable labels in Desc.
  135. func NewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues ...string) (Metric, error) {
  136. if len(desc.variableLabels) != len(labelValues) {
  137. return nil, errInconsistentCardinality
  138. }
  139. return &constMetric{
  140. desc: desc,
  141. valType: valueType,
  142. val: value,
  143. labelPairs: makeLabelPairs(desc, labelValues),
  144. }, nil
  145. }
  146. // MustNewConstMetric is a version of NewConstMetric that panics where
  147. // NewConstMetric would have returned an error.
  148. func MustNewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues ...string) Metric {
  149. m, err := NewConstMetric(desc, valueType, value, labelValues...)
  150. if err != nil {
  151. panic(err)
  152. }
  153. return m
  154. }
  155. type constMetric struct {
  156. desc *Desc
  157. valType ValueType
  158. val float64
  159. labelPairs []*dto.LabelPair
  160. }
  161. func (m *constMetric) Desc() *Desc {
  162. return m.desc
  163. }
  164. func (m *constMetric) Write(out *dto.Metric) error {
  165. return populateMetric(m.valType, m.val, m.labelPairs, out)
  166. }
  167. func populateMetric(
  168. t ValueType,
  169. v float64,
  170. labelPairs []*dto.LabelPair,
  171. m *dto.Metric,
  172. ) error {
  173. m.Label = labelPairs
  174. switch t {
  175. case CounterValue:
  176. m.Counter = &dto.Counter{Value: proto.Float64(v)}
  177. case GaugeValue:
  178. m.Gauge = &dto.Gauge{Value: proto.Float64(v)}
  179. case UntypedValue:
  180. m.Untyped = &dto.Untyped{Value: proto.Float64(v)}
  181. default:
  182. return fmt.Errorf("encountered unknown type %v", t)
  183. }
  184. return nil
  185. }
  186. func makeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair {
  187. totalLen := len(desc.variableLabels) + len(desc.constLabelPairs)
  188. if totalLen == 0 {
  189. // Super fast path.
  190. return nil
  191. }
  192. if len(desc.variableLabels) == 0 {
  193. // Moderately fast path.
  194. return desc.constLabelPairs
  195. }
  196. labelPairs := make([]*dto.LabelPair, 0, totalLen)
  197. for i, n := range desc.variableLabels {
  198. labelPairs = append(labelPairs, &dto.LabelPair{
  199. Name: proto.String(n),
  200. Value: proto.String(labelValues[i]),
  201. })
  202. }
  203. for _, lp := range desc.constLabelPairs {
  204. labelPairs = append(labelPairs, lp)
  205. }
  206. sort.Sort(LabelPairSorter(labelPairs))
  207. return labelPairs
  208. }