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.

labels.go 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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. // LabelConstraint normalizes label values.
  31. type LabelConstraint func(string) string
  32. // ConstrainedLabels represents a label name and its constrain function
  33. // to normalize label values. This type is commonly used when constructing
  34. // metric vector Collectors.
  35. type ConstrainedLabel struct {
  36. Name string
  37. Constraint LabelConstraint
  38. }
  39. // ConstrainableLabels is an interface that allows creating of labels that can
  40. // be optionally constrained.
  41. //
  42. // prometheus.V2().NewCounterVec(CounterVecOpts{
  43. // CounterOpts: {...}, // Usual CounterOpts fields
  44. // VariableLabels: []ConstrainedLabels{
  45. // {Name: "A"},
  46. // {Name: "B", Constraint: func(v string) string { ... }},
  47. // },
  48. // })
  49. type ConstrainableLabels interface {
  50. compile() *compiledLabels
  51. labelNames() []string
  52. }
  53. // ConstrainedLabels represents a collection of label name -> constrain function
  54. // to normalize label values. This type is commonly used when constructing
  55. // metric vector Collectors.
  56. type ConstrainedLabels []ConstrainedLabel
  57. func (cls ConstrainedLabels) compile() *compiledLabels {
  58. compiled := &compiledLabels{
  59. names: make([]string, len(cls)),
  60. labelConstraints: map[string]LabelConstraint{},
  61. }
  62. for i, label := range cls {
  63. compiled.names[i] = label.Name
  64. if label.Constraint != nil {
  65. compiled.labelConstraints[label.Name] = label.Constraint
  66. }
  67. }
  68. return compiled
  69. }
  70. func (cls ConstrainedLabels) labelNames() []string {
  71. names := make([]string, len(cls))
  72. for i, label := range cls {
  73. names[i] = label.Name
  74. }
  75. return names
  76. }
  77. // UnconstrainedLabels represents collection of label without any constraint on
  78. // their value. Thus, it is simply a collection of label names.
  79. //
  80. // UnconstrainedLabels([]string{ "A", "B" })
  81. //
  82. // is equivalent to
  83. //
  84. // ConstrainedLabels {
  85. // { Name: "A" },
  86. // { Name: "B" },
  87. // }
  88. type UnconstrainedLabels []string
  89. func (uls UnconstrainedLabels) compile() *compiledLabels {
  90. return &compiledLabels{
  91. names: uls,
  92. }
  93. }
  94. func (uls UnconstrainedLabels) labelNames() []string {
  95. return uls
  96. }
  97. type compiledLabels struct {
  98. names []string
  99. labelConstraints map[string]LabelConstraint
  100. }
  101. func (cls *compiledLabels) compile() *compiledLabels {
  102. return cls
  103. }
  104. func (cls *compiledLabels) labelNames() []string {
  105. return cls.names
  106. }
  107. func (cls *compiledLabels) constrain(labelName, value string) string {
  108. if fn, ok := cls.labelConstraints[labelName]; ok && fn != nil {
  109. return fn(value)
  110. }
  111. return value
  112. }
  113. // reservedLabelPrefix is a prefix which is not legal in user-supplied
  114. // label names.
  115. const reservedLabelPrefix = "__"
  116. var errInconsistentCardinality = errors.New("inconsistent label cardinality")
  117. func makeInconsistentCardinalityError(fqName string, labels, labelValues []string) error {
  118. return fmt.Errorf(
  119. "%w: %q has %d variable labels named %q but %d values %q were provided",
  120. errInconsistentCardinality, fqName,
  121. len(labels), labels,
  122. len(labelValues), labelValues,
  123. )
  124. }
  125. func validateValuesInLabels(labels Labels, expectedNumberOfValues int) error {
  126. if len(labels) != expectedNumberOfValues {
  127. return fmt.Errorf(
  128. "%w: expected %d label values but got %d in %#v",
  129. errInconsistentCardinality, expectedNumberOfValues,
  130. len(labels), labels,
  131. )
  132. }
  133. for name, val := range labels {
  134. if !utf8.ValidString(val) {
  135. return fmt.Errorf("label %s: value %q is not valid UTF-8", name, val)
  136. }
  137. }
  138. return nil
  139. }
  140. func validateLabelValues(vals []string, expectedNumberOfValues int) error {
  141. if len(vals) != expectedNumberOfValues {
  142. return fmt.Errorf(
  143. "%w: expected %d label values but got %d in %#v",
  144. errInconsistentCardinality, expectedNumberOfValues,
  145. len(vals), vals,
  146. )
  147. }
  148. for _, val := range vals {
  149. if !utf8.ValidString(val) {
  150. return fmt.Errorf("label value %q is not valid UTF-8", val)
  151. }
  152. }
  153. return nil
  154. }
  155. func checkLabelName(l string) bool {
  156. return model.LabelName(l).IsValid() && !strings.HasPrefix(l, reservedLabelPrefix)
  157. }