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
6 // http://www.apache.org/licenses/LICENSE-2.0
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.
20 "github.com/golang/protobuf/proto"
22 dto "github.com/prometheus/client_model/go"
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.
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.
35 // The Collector example demonstrates a use of WrapRegistererWith.
36 func WrapRegistererWith(labels Labels, reg Registerer) Registerer {
37 return &wrappingRegisterer{
38 wrappedRegisterer: reg,
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.
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_”,
57 func WrapRegistererWithPrefix(prefix string, reg Registerer) Registerer {
58 return &wrappingRegisterer{
59 wrappedRegisterer: reg,
64 type wrappingRegisterer struct {
65 wrappedRegisterer Registerer
70 func (r *wrappingRegisterer) Register(c Collector) error {
71 return r.wrappedRegisterer.Register(&wrappingCollector{
78 func (r *wrappingRegisterer) MustRegister(cs ...Collector) {
79 for _, c := range cs {
80 if err := r.Register(c); err != nil {
86 func (r *wrappingRegisterer) Unregister(c Collector) bool {
87 return r.wrappedRegisterer.Unregister(&wrappingCollector{
94 type wrappingCollector struct {
95 wrappedCollector Collector
100 func (c *wrappingCollector) Collect(ch chan<- Metric) {
101 wrappedCh := make(chan Metric)
103 c.wrappedCollector.Collect(wrappedCh)
106 for m := range wrappedCh {
107 ch <- &wrappingMetric{
115 func (c *wrappingCollector) Describe(ch chan<- *Desc) {
116 wrappedCh := make(chan *Desc)
118 c.wrappedCollector.Describe(wrappedCh)
121 for desc := range wrappedCh {
122 ch <- wrapDesc(desc, c.prefix, c.labels)
126 type wrappingMetric struct {
132 func (m *wrappingMetric) Desc() *Desc {
133 return wrapDesc(m.wrappedMetric.Desc(), m.prefix, m.labels)
136 func (m *wrappingMetric) Write(out *dto.Metric) error {
137 if err := m.wrappedMetric.Write(out); err != nil {
140 if len(m.labels) == 0 {
141 // No wrapping labels.
144 for ln, lv := range m.labels {
145 out.Label = append(out.Label, &dto.LabelPair{
146 Name: proto.String(ln),
147 Value: proto.String(lv),
150 sort.Sort(labelPairSorter(out.Label))
154 func wrapDesc(desc *Desc, prefix string, labels Labels) *Desc {
155 constLabels := Labels{}
156 for _, lp := range desc.constLabelPairs {
157 constLabels[*lp.Name] = *lp.Value
159 for ln, lv := range labels {
160 if _, alreadyUsed := constLabels[ln]; alreadyUsed {
164 variableLabels: desc.variableLabels,
165 constLabelPairs: desc.constLabelPairs,
166 err: fmt.Errorf("attempted wrapping with already existing label name %q", ln),
171 // NewDesc will do remaining validations.
172 newDesc := NewDesc(prefix+desc.fqName, desc.help, desc.variableLabels, constLabels)
173 // Propagate errors if there was any. This will override any errer
174 // created by NewDesc above, i.e. earlier errors get precedence.
176 newDesc.err = desc.err