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.
 
 

161 lines
4.4 KiB

  1. // Copyright 2018 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. "strings"
  18. "unicode/utf8"
  19. "github.com/prometheus/common/model"
  20. )
  21. // Labels represents a collection of label name -> value mappings. This type is
  22. // commonly used with the With(Labels) and GetMetricWith(Labels) methods of
  23. // metric vector Collectors, e.g.:
  24. //
  25. // myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
  26. //
  27. // The other use-case is the specification of constant label pairs in Opts or to
  28. // create a Desc.
  29. type Labels map[string]string
  30. // ConstrainedLabels represents a label name and its constrain function
  31. // to normalize label values. This type is commonly used when constructing
  32. // metric vector Collectors.
  33. type ConstrainedLabel struct {
  34. Name string
  35. Constraint func(string) string
  36. }
  37. func (cl ConstrainedLabel) Constrain(v string) string {
  38. if cl.Constraint == nil {
  39. return v
  40. }
  41. return cl.Constraint(v)
  42. }
  43. // ConstrainableLabels is an interface that allows creating of labels that can
  44. // be optionally constrained.
  45. //
  46. // prometheus.V2().NewCounterVec(CounterVecOpts{
  47. // CounterOpts: {...}, // Usual CounterOpts fields
  48. // VariableLabels: []ConstrainedLabels{
  49. // {Name: "A"},
  50. // {Name: "B", Constraint: func(v string) string { ... }},
  51. // },
  52. // })
  53. type ConstrainableLabels interface {
  54. constrainedLabels() ConstrainedLabels
  55. labelNames() []string
  56. }
  57. // ConstrainedLabels represents a collection of label name -> constrain function
  58. // to normalize label values. This type is commonly used when constructing
  59. // metric vector Collectors.
  60. type ConstrainedLabels []ConstrainedLabel
  61. func (cls ConstrainedLabels) constrainedLabels() ConstrainedLabels {
  62. return cls
  63. }
  64. func (cls ConstrainedLabels) labelNames() []string {
  65. names := make([]string, len(cls))
  66. for i, label := range cls {
  67. names[i] = label.Name
  68. }
  69. return names
  70. }
  71. // UnconstrainedLabels represents collection of label without any constraint on
  72. // their value. Thus, it is simply a collection of label names.
  73. //
  74. // UnconstrainedLabels([]string{ "A", "B" })
  75. //
  76. // is equivalent to
  77. //
  78. // ConstrainedLabels {
  79. // { Name: "A" },
  80. // { Name: "B" },
  81. // }
  82. type UnconstrainedLabels []string
  83. func (uls UnconstrainedLabels) constrainedLabels() ConstrainedLabels {
  84. constrainedLabels := make([]ConstrainedLabel, len(uls))
  85. for i, l := range uls {
  86. constrainedLabels[i] = ConstrainedLabel{Name: l}
  87. }
  88. return constrainedLabels
  89. }
  90. func (uls UnconstrainedLabels) labelNames() []string {
  91. return uls
  92. }
  93. // reservedLabelPrefix is a prefix which is not legal in user-supplied
  94. // label names.
  95. const reservedLabelPrefix = "__"
  96. var errInconsistentCardinality = errors.New("inconsistent label cardinality")
  97. func makeInconsistentCardinalityError(fqName string, labels, labelValues []string) error {
  98. return fmt.Errorf(
  99. "%w: %q has %d variable labels named %q but %d values %q were provided",
  100. errInconsistentCardinality, fqName,
  101. len(labels), labels,
  102. len(labelValues), labelValues,
  103. )
  104. }
  105. func validateValuesInLabels(labels Labels, expectedNumberOfValues int) error {
  106. if len(labels) != expectedNumberOfValues {
  107. return fmt.Errorf(
  108. "%w: expected %d label values but got %d in %#v",
  109. errInconsistentCardinality, expectedNumberOfValues,
  110. len(labels), labels,
  111. )
  112. }
  113. for name, val := range labels {
  114. if !utf8.ValidString(val) {
  115. return fmt.Errorf("label %s: value %q is not valid UTF-8", name, val)
  116. }
  117. }
  118. return nil
  119. }
  120. func validateLabelValues(vals []string, expectedNumberOfValues int) error {
  121. if len(vals) != expectedNumberOfValues {
  122. return fmt.Errorf(
  123. "%w: expected %d label values but got %d in %#v",
  124. errInconsistentCardinality, expectedNumberOfValues,
  125. len(vals), vals,
  126. )
  127. }
  128. for _, val := range vals {
  129. if !utf8.ValidString(val) {
  130. return fmt.Errorf("label value %q is not valid UTF-8", val)
  131. }
  132. }
  133. return nil
  134. }
  135. func checkLabelName(l string) bool {
  136. return model.LabelName(l).IsValid() && !strings.HasPrefix(l, reservedLabelPrefix)
  137. }