blob: 74ee93280fed18facca71ac8032a27df11d2241f [file] [log] [blame]
khenaidooab1f7bd2019-11-14 14:00:27 -05001// 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
14package prometheus
15
16import (
17 "fmt"
18 "sort"
19
khenaidood948f772021-08-11 17:49:24 -040020 //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
khenaidooab1f7bd2019-11-14 14:00:27 -050021 "github.com/golang/protobuf/proto"
22
23 dto "github.com/prometheus/client_model/go"
24)
25
26// WrapRegistererWith returns a Registerer wrapping the provided
27// Registerer. Collectors registered with the returned Registerer will be
28// registered with the wrapped Registerer in a modified way. The modified
29// Collector adds the provided Labels to all Metrics it collects (as
30// ConstLabels). The Metrics collected by the unmodified Collector must not
khenaidood948f772021-08-11 17:49:24 -040031// duplicate any of those labels. Wrapping a nil value is valid, resulting
32// in a no-op Registerer.
khenaidooab1f7bd2019-11-14 14:00:27 -050033//
34// WrapRegistererWith provides a way to add fixed labels to a subset of
khenaidood948f772021-08-11 17:49:24 -040035// Collectors. It should not be used to add fixed labels to all metrics
36// exposed. See also
37// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels-not-static-scraped-labels
khenaidooab1f7bd2019-11-14 14:00:27 -050038//
39// Conflicts between Collectors registered through the original Registerer with
40// Collectors registered through the wrapping Registerer will still be
41// detected. Any AlreadyRegisteredError returned by the Register method of
42// either Registerer will contain the ExistingCollector in the form it was
43// provided to the respective registry.
44//
45// The Collector example demonstrates a use of WrapRegistererWith.
46func WrapRegistererWith(labels Labels, reg Registerer) Registerer {
47 return &wrappingRegisterer{
48 wrappedRegisterer: reg,
49 labels: labels,
50 }
51}
52
53// WrapRegistererWithPrefix returns a Registerer wrapping the provided
54// Registerer. Collectors registered with the returned Registerer will be
55// registered with the wrapped Registerer in a modified way. The modified
56// Collector adds the provided prefix to the name of all Metrics it collects.
khenaidood948f772021-08-11 17:49:24 -040057// Wrapping a nil value is valid, resulting in a no-op Registerer.
khenaidooab1f7bd2019-11-14 14:00:27 -050058//
59// WrapRegistererWithPrefix is useful to have one place to prefix all metrics of
60// a sub-system. To make this work, register metrics of the sub-system with the
61// wrapping Registerer returned by WrapRegistererWithPrefix. It is rarely useful
62// to use the same prefix for all metrics exposed. In particular, do not prefix
63// metric names that are standardized across applications, as that would break
64// horizontal monitoring, for example the metrics provided by the Go collector
65// (see NewGoCollector) and the process collector (see NewProcessCollector). (In
66// fact, those metrics are already prefixed with “go_” or “process_”,
67// respectively.)
68//
69// Conflicts between Collectors registered through the original Registerer with
70// Collectors registered through the wrapping Registerer will still be
71// detected. Any AlreadyRegisteredError returned by the Register method of
72// either Registerer will contain the ExistingCollector in the form it was
73// provided to the respective registry.
74func WrapRegistererWithPrefix(prefix string, reg Registerer) Registerer {
75 return &wrappingRegisterer{
76 wrappedRegisterer: reg,
77 prefix: prefix,
78 }
79}
80
81type wrappingRegisterer struct {
82 wrappedRegisterer Registerer
83 prefix string
84 labels Labels
85}
86
87func (r *wrappingRegisterer) Register(c Collector) error {
khenaidood948f772021-08-11 17:49:24 -040088 if r.wrappedRegisterer == nil {
89 return nil
90 }
khenaidooab1f7bd2019-11-14 14:00:27 -050091 return r.wrappedRegisterer.Register(&wrappingCollector{
92 wrappedCollector: c,
93 prefix: r.prefix,
94 labels: r.labels,
95 })
96}
97
98func (r *wrappingRegisterer) MustRegister(cs ...Collector) {
khenaidood948f772021-08-11 17:49:24 -040099 if r.wrappedRegisterer == nil {
100 return
101 }
khenaidooab1f7bd2019-11-14 14:00:27 -0500102 for _, c := range cs {
103 if err := r.Register(c); err != nil {
104 panic(err)
105 }
106 }
107}
108
109func (r *wrappingRegisterer) Unregister(c Collector) bool {
khenaidood948f772021-08-11 17:49:24 -0400110 if r.wrappedRegisterer == nil {
111 return false
112 }
khenaidooab1f7bd2019-11-14 14:00:27 -0500113 return r.wrappedRegisterer.Unregister(&wrappingCollector{
114 wrappedCollector: c,
115 prefix: r.prefix,
116 labels: r.labels,
117 })
118}
119
120type wrappingCollector struct {
121 wrappedCollector Collector
122 prefix string
123 labels Labels
124}
125
126func (c *wrappingCollector) Collect(ch chan<- Metric) {
127 wrappedCh := make(chan Metric)
128 go func() {
129 c.wrappedCollector.Collect(wrappedCh)
130 close(wrappedCh)
131 }()
132 for m := range wrappedCh {
133 ch <- &wrappingMetric{
134 wrappedMetric: m,
135 prefix: c.prefix,
136 labels: c.labels,
137 }
138 }
139}
140
141func (c *wrappingCollector) Describe(ch chan<- *Desc) {
142 wrappedCh := make(chan *Desc)
143 go func() {
144 c.wrappedCollector.Describe(wrappedCh)
145 close(wrappedCh)
146 }()
147 for desc := range wrappedCh {
148 ch <- wrapDesc(desc, c.prefix, c.labels)
149 }
150}
151
152func (c *wrappingCollector) unwrapRecursively() Collector {
153 switch wc := c.wrappedCollector.(type) {
154 case *wrappingCollector:
155 return wc.unwrapRecursively()
156 default:
157 return wc
158 }
159}
160
161type wrappingMetric struct {
162 wrappedMetric Metric
163 prefix string
164 labels Labels
165}
166
167func (m *wrappingMetric) Desc() *Desc {
168 return wrapDesc(m.wrappedMetric.Desc(), m.prefix, m.labels)
169}
170
171func (m *wrappingMetric) Write(out *dto.Metric) error {
172 if err := m.wrappedMetric.Write(out); err != nil {
173 return err
174 }
175 if len(m.labels) == 0 {
176 // No wrapping labels.
177 return nil
178 }
179 for ln, lv := range m.labels {
180 out.Label = append(out.Label, &dto.LabelPair{
181 Name: proto.String(ln),
182 Value: proto.String(lv),
183 })
184 }
185 sort.Sort(labelPairSorter(out.Label))
186 return nil
187}
188
189func wrapDesc(desc *Desc, prefix string, labels Labels) *Desc {
190 constLabels := Labels{}
191 for _, lp := range desc.constLabelPairs {
192 constLabels[*lp.Name] = *lp.Value
193 }
194 for ln, lv := range labels {
195 if _, alreadyUsed := constLabels[ln]; alreadyUsed {
196 return &Desc{
197 fqName: desc.fqName,
198 help: desc.help,
199 variableLabels: desc.variableLabels,
200 constLabelPairs: desc.constLabelPairs,
201 err: fmt.Errorf("attempted wrapping with already existing label name %q", ln),
202 }
203 }
204 constLabels[ln] = lv
205 }
206 // NewDesc will do remaining validations.
207 newDesc := NewDesc(prefix+desc.fqName, desc.help, desc.variableLabels, constLabels)
208 // Propagate errors if there was any. This will override any errer
209 // created by NewDesc above, i.e. earlier errors get precedence.
210 if desc.err != nil {
211 newDesc.err = desc.err
212 }
213 return newDesc
214}