You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

value.go 8.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  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. "sort"
  18. "time"
  19. "unicode/utf8"
  20. "github.com/prometheus/client_golang/prometheus/internal"
  21. dto "github.com/prometheus/client_model/go"
  22. "google.golang.org/protobuf/proto"
  23. "google.golang.org/protobuf/types/known/timestamppb"
  24. )
  25. // ValueType is an enumeration of metric types that represent a simple value.
  26. type ValueType int
  27. // Possible values for the ValueType enum. Use UntypedValue to mark a metric
  28. // with an unknown type.
  29. const (
  30. _ ValueType = iota
  31. CounterValue
  32. GaugeValue
  33. UntypedValue
  34. )
  35. var (
  36. CounterMetricTypePtr = func() *dto.MetricType { d := dto.MetricType_COUNTER; return &d }()
  37. GaugeMetricTypePtr = func() *dto.MetricType { d := dto.MetricType_GAUGE; return &d }()
  38. UntypedMetricTypePtr = func() *dto.MetricType { d := dto.MetricType_UNTYPED; return &d }()
  39. )
  40. func (v ValueType) ToDTO() *dto.MetricType {
  41. switch v {
  42. case CounterValue:
  43. return CounterMetricTypePtr
  44. case GaugeValue:
  45. return GaugeMetricTypePtr
  46. default:
  47. return UntypedMetricTypePtr
  48. }
  49. }
  50. // valueFunc is a generic metric for simple values retrieved on collect time
  51. // from a function. It implements Metric and Collector. Its effective type is
  52. // determined by ValueType. This is a low-level building block used by the
  53. // library to back the implementations of CounterFunc, GaugeFunc, and
  54. // UntypedFunc.
  55. type valueFunc struct {
  56. selfCollector
  57. desc *Desc
  58. valType ValueType
  59. function func() float64
  60. labelPairs []*dto.LabelPair
  61. }
  62. // newValueFunc returns a newly allocated valueFunc with the given Desc and
  63. // ValueType. The value reported is determined by calling the given function
  64. // from within the Write method. Take into account that metric collection may
  65. // happen concurrently. If that results in concurrent calls to Write, like in
  66. // the case where a valueFunc is directly registered with Prometheus, the
  67. // provided function must be concurrency-safe.
  68. func newValueFunc(desc *Desc, valueType ValueType, function func() float64) *valueFunc {
  69. result := &valueFunc{
  70. desc: desc,
  71. valType: valueType,
  72. function: function,
  73. labelPairs: MakeLabelPairs(desc, nil),
  74. }
  75. result.init(result)
  76. return result
  77. }
  78. func (v *valueFunc) Desc() *Desc {
  79. return v.desc
  80. }
  81. func (v *valueFunc) Write(out *dto.Metric) error {
  82. return populateMetric(v.valType, v.function(), v.labelPairs, nil, out, nil)
  83. }
  84. // NewConstMetric returns a metric with one fixed value that cannot be
  85. // changed. Users of this package will not have much use for it in regular
  86. // operations. However, when implementing custom Collectors, it is useful as a
  87. // throw-away metric that is generated on the fly to send it to Prometheus in
  88. // the Collect method. NewConstMetric returns an error if the length of
  89. // labelValues is not consistent with the variable labels in Desc or if Desc is
  90. // invalid.
  91. func NewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues ...string) (Metric, error) {
  92. if desc.err != nil {
  93. return nil, desc.err
  94. }
  95. if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
  96. return nil, err
  97. }
  98. metric := &dto.Metric{}
  99. if err := populateMetric(valueType, value, MakeLabelPairs(desc, labelValues), nil, metric, nil); err != nil {
  100. return nil, err
  101. }
  102. return &constMetric{
  103. desc: desc,
  104. metric: metric,
  105. }, nil
  106. }
  107. // MustNewConstMetric is a version of NewConstMetric that panics where
  108. // NewConstMetric would have returned an error.
  109. func MustNewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues ...string) Metric {
  110. m, err := NewConstMetric(desc, valueType, value, labelValues...)
  111. if err != nil {
  112. panic(err)
  113. }
  114. return m
  115. }
  116. // NewConstMetricWithCreatedTimestamp does the same thing as NewConstMetric, but generates Counters
  117. // with created timestamp set and returns an error for other metric types.
  118. func NewConstMetricWithCreatedTimestamp(desc *Desc, valueType ValueType, value float64, ct time.Time, labelValues ...string) (Metric, error) {
  119. if desc.err != nil {
  120. return nil, desc.err
  121. }
  122. if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
  123. return nil, err
  124. }
  125. switch valueType {
  126. case CounterValue:
  127. break
  128. default:
  129. return nil, errors.New("created timestamps are only supported for counters")
  130. }
  131. metric := &dto.Metric{}
  132. if err := populateMetric(valueType, value, MakeLabelPairs(desc, labelValues), nil, metric, timestamppb.New(ct)); err != nil {
  133. return nil, err
  134. }
  135. return &constMetric{
  136. desc: desc,
  137. metric: metric,
  138. }, nil
  139. }
  140. // MustNewConstMetricWithCreatedTimestamp is a version of NewConstMetricWithCreatedTimestamp that panics where
  141. // NewConstMetricWithCreatedTimestamp would have returned an error.
  142. func MustNewConstMetricWithCreatedTimestamp(desc *Desc, valueType ValueType, value float64, ct time.Time, labelValues ...string) Metric {
  143. m, err := NewConstMetricWithCreatedTimestamp(desc, valueType, value, ct, labelValues...)
  144. if err != nil {
  145. panic(err)
  146. }
  147. return m
  148. }
  149. type constMetric struct {
  150. desc *Desc
  151. metric *dto.Metric
  152. }
  153. func (m *constMetric) Desc() *Desc {
  154. return m.desc
  155. }
  156. func (m *constMetric) Write(out *dto.Metric) error {
  157. out.Label = m.metric.Label
  158. out.Counter = m.metric.Counter
  159. out.Gauge = m.metric.Gauge
  160. out.Untyped = m.metric.Untyped
  161. return nil
  162. }
  163. func populateMetric(
  164. t ValueType,
  165. v float64,
  166. labelPairs []*dto.LabelPair,
  167. e *dto.Exemplar,
  168. m *dto.Metric,
  169. ct *timestamppb.Timestamp,
  170. ) error {
  171. m.Label = labelPairs
  172. switch t {
  173. case CounterValue:
  174. m.Counter = &dto.Counter{Value: proto.Float64(v), Exemplar: e, CreatedTimestamp: ct}
  175. case GaugeValue:
  176. m.Gauge = &dto.Gauge{Value: proto.Float64(v)}
  177. case UntypedValue:
  178. m.Untyped = &dto.Untyped{Value: proto.Float64(v)}
  179. default:
  180. return fmt.Errorf("encountered unknown type %v", t)
  181. }
  182. return nil
  183. }
  184. // MakeLabelPairs is a helper function to create protobuf LabelPairs from the
  185. // variable and constant labels in the provided Desc. The values for the
  186. // variable labels are defined by the labelValues slice, which must be in the
  187. // same order as the corresponding variable labels in the Desc.
  188. //
  189. // This function is only needed for custom Metric implementations. See MetricVec
  190. // example.
  191. func MakeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair {
  192. totalLen := len(desc.variableLabels.names) + len(desc.constLabelPairs)
  193. if totalLen == 0 {
  194. // Super fast path.
  195. return nil
  196. }
  197. if len(desc.variableLabels.names) == 0 {
  198. // Moderately fast path.
  199. return desc.constLabelPairs
  200. }
  201. labelPairs := make([]*dto.LabelPair, 0, totalLen)
  202. for i, l := range desc.variableLabels.names {
  203. labelPairs = append(labelPairs, &dto.LabelPair{
  204. Name: proto.String(l),
  205. Value: proto.String(labelValues[i]),
  206. })
  207. }
  208. labelPairs = append(labelPairs, desc.constLabelPairs...)
  209. sort.Sort(internal.LabelPairSorter(labelPairs))
  210. return labelPairs
  211. }
  212. // ExemplarMaxRunes is the max total number of runes allowed in exemplar labels.
  213. const ExemplarMaxRunes = 128
  214. // newExemplar creates a new dto.Exemplar from the provided values. An error is
  215. // returned if any of the label names or values are invalid or if the total
  216. // number of runes in the label names and values exceeds ExemplarMaxRunes.
  217. func newExemplar(value float64, ts time.Time, l Labels) (*dto.Exemplar, error) {
  218. e := &dto.Exemplar{}
  219. e.Value = proto.Float64(value)
  220. tsProto := timestamppb.New(ts)
  221. if err := tsProto.CheckValid(); err != nil {
  222. return nil, err
  223. }
  224. e.Timestamp = tsProto
  225. labelPairs := make([]*dto.LabelPair, 0, len(l))
  226. var runes int
  227. for name, value := range l {
  228. if !checkLabelName(name) {
  229. return nil, fmt.Errorf("exemplar label name %q is invalid", name)
  230. }
  231. runes += utf8.RuneCountInString(name)
  232. if !utf8.ValidString(value) {
  233. return nil, fmt.Errorf("exemplar label value %q is not valid UTF-8", value)
  234. }
  235. runes += utf8.RuneCountInString(value)
  236. labelPairs = append(labelPairs, &dto.LabelPair{
  237. Name: proto.String(name),
  238. Value: proto.String(value),
  239. })
  240. }
  241. if runes > ExemplarMaxRunes {
  242. return nil, fmt.Errorf("exemplar labels have %d runes, exceeding the limit of %d", runes, ExemplarMaxRunes)
  243. }
  244. e.Label = labelPairs
  245. return e, nil
  246. }