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.
 
 

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