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.

200 lines
6.2 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. "fmt"
  16. "sort"
  17. "github.com/golang/protobuf/proto"
  18. dto "github.com/prometheus/client_model/go"
  19. )
  20. // WrapRegistererWith returns a Registerer wrapping the provided
  21. // Registerer. Collectors registered with the returned Registerer will be
  22. // registered with the wrapped Registerer in a modified way. The modified
  23. // Collector adds the provided Labels to all Metrics it collects (as
  24. // ConstLabels). The Metrics collected by the unmodified Collector must not
  25. // duplicate any of those labels.
  26. //
  27. // WrapRegistererWith provides a way to add fixed labels to a subset of
  28. // Collectors. It should not be used to add fixed labels to all metrics exposed.
  29. //
  30. // Conflicts between Collectors registered through the original Registerer with
  31. // Collectors registered through the wrapping Registerer will still be
  32. // detected. Any AlreadyRegisteredError returned by the Register method of
  33. // either Registerer will contain the ExistingCollector in the form it was
  34. // provided to the respective registry.
  35. //
  36. // The Collector example demonstrates a use of WrapRegistererWith.
  37. func WrapRegistererWith(labels Labels, reg Registerer) Registerer {
  38. return &wrappingRegisterer{
  39. wrappedRegisterer: reg,
  40. labels: labels,
  41. }
  42. }
  43. // WrapRegistererWithPrefix returns a Registerer wrapping the provided
  44. // Registerer. Collectors registered with the returned Registerer will be
  45. // registered with the wrapped Registerer in a modified way. The modified
  46. // Collector adds the provided prefix to the name of all Metrics it collects.
  47. //
  48. // WrapRegistererWithPrefix is useful to have one place to prefix all metrics of
  49. // a sub-system. To make this work, register metrics of the sub-system with the
  50. // wrapping Registerer returned by WrapRegistererWithPrefix. It is rarely useful
  51. // to use the same prefix for all metrics exposed. In particular, do not prefix
  52. // metric names that are standardized across applications, as that would break
  53. // horizontal monitoring, for example the metrics provided by the Go collector
  54. // (see NewGoCollector) and the process collector (see NewProcessCollector). (In
  55. // fact, those metrics are already prefixed with “go_” or “process_”,
  56. // respectively.)
  57. //
  58. // Conflicts between Collectors registered through the original Registerer with
  59. // Collectors registered through the wrapping Registerer will still be
  60. // detected. Any AlreadyRegisteredError returned by the Register method of
  61. // either Registerer will contain the ExistingCollector in the form it was
  62. // provided to the respective registry.
  63. func WrapRegistererWithPrefix(prefix string, reg Registerer) Registerer {
  64. return &wrappingRegisterer{
  65. wrappedRegisterer: reg,
  66. prefix: prefix,
  67. }
  68. }
  69. type wrappingRegisterer struct {
  70. wrappedRegisterer Registerer
  71. prefix string
  72. labels Labels
  73. }
  74. func (r *wrappingRegisterer) Register(c Collector) error {
  75. return r.wrappedRegisterer.Register(&wrappingCollector{
  76. wrappedCollector: c,
  77. prefix: r.prefix,
  78. labels: r.labels,
  79. })
  80. }
  81. func (r *wrappingRegisterer) MustRegister(cs ...Collector) {
  82. for _, c := range cs {
  83. if err := r.Register(c); err != nil {
  84. panic(err)
  85. }
  86. }
  87. }
  88. func (r *wrappingRegisterer) Unregister(c Collector) bool {
  89. return r.wrappedRegisterer.Unregister(&wrappingCollector{
  90. wrappedCollector: c,
  91. prefix: r.prefix,
  92. labels: r.labels,
  93. })
  94. }
  95. type wrappingCollector struct {
  96. wrappedCollector Collector
  97. prefix string
  98. labels Labels
  99. }
  100. func (c *wrappingCollector) Collect(ch chan<- Metric) {
  101. wrappedCh := make(chan Metric)
  102. go func() {
  103. c.wrappedCollector.Collect(wrappedCh)
  104. close(wrappedCh)
  105. }()
  106. for m := range wrappedCh {
  107. ch <- &wrappingMetric{
  108. wrappedMetric: m,
  109. prefix: c.prefix,
  110. labels: c.labels,
  111. }
  112. }
  113. }
  114. func (c *wrappingCollector) Describe(ch chan<- *Desc) {
  115. wrappedCh := make(chan *Desc)
  116. go func() {
  117. c.wrappedCollector.Describe(wrappedCh)
  118. close(wrappedCh)
  119. }()
  120. for desc := range wrappedCh {
  121. ch <- wrapDesc(desc, c.prefix, c.labels)
  122. }
  123. }
  124. func (c *wrappingCollector) unwrapRecursively() Collector {
  125. switch wc := c.wrappedCollector.(type) {
  126. case *wrappingCollector:
  127. return wc.unwrapRecursively()
  128. default:
  129. return wc
  130. }
  131. }
  132. type wrappingMetric struct {
  133. wrappedMetric Metric
  134. prefix string
  135. labels Labels
  136. }
  137. func (m *wrappingMetric) Desc() *Desc {
  138. return wrapDesc(m.wrappedMetric.Desc(), m.prefix, m.labels)
  139. }
  140. func (m *wrappingMetric) Write(out *dto.Metric) error {
  141. if err := m.wrappedMetric.Write(out); err != nil {
  142. return err
  143. }
  144. if len(m.labels) == 0 {
  145. // No wrapping labels.
  146. return nil
  147. }
  148. for ln, lv := range m.labels {
  149. out.Label = append(out.Label, &dto.LabelPair{
  150. Name: proto.String(ln),
  151. Value: proto.String(lv),
  152. })
  153. }
  154. sort.Sort(labelPairSorter(out.Label))
  155. return nil
  156. }
  157. func wrapDesc(desc *Desc, prefix string, labels Labels) *Desc {
  158. constLabels := Labels{}
  159. for _, lp := range desc.constLabelPairs {
  160. constLabels[*lp.Name] = *lp.Value
  161. }
  162. for ln, lv := range labels {
  163. if _, alreadyUsed := constLabels[ln]; alreadyUsed {
  164. return &Desc{
  165. fqName: desc.fqName,
  166. help: desc.help,
  167. variableLabels: desc.variableLabels,
  168. constLabelPairs: desc.constLabelPairs,
  169. err: fmt.Errorf("attempted wrapping with already existing label name %q", ln),
  170. }
  171. }
  172. constLabels[ln] = lv
  173. }
  174. // NewDesc will do remaining validations.
  175. newDesc := NewDesc(prefix+desc.fqName, desc.help, desc.variableLabels, constLabels)
  176. // Propagate errors if there was any. This will override any errer
  177. // created by NewDesc above, i.e. earlier errors get precedence.
  178. if desc.err != nil {
  179. newDesc.err = desc.err
  180. }
  181. return newDesc
  182. }