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