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.
 
 

213 regels
6.6 KiB

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