blob: 49159bf3eb05f60d75cce7ca7f8e9ba3e19b8aa4 [file] [log] [blame]
khenaidooffe076b2019-01-15 16:08:08 -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// The Collector example demonstrates a use of WrapRegistererWith.
36func WrapRegistererWith(labels Labels, reg Registerer) Registerer {
37 return &wrappingRegisterer{
38 wrappedRegisterer: reg,
39 labels: labels,
40 }
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.)
57func WrapRegistererWithPrefix(prefix string, reg Registerer) Registerer {
58 return &wrappingRegisterer{
59 wrappedRegisterer: reg,
60 prefix: prefix,
61 }
62}
63
64type wrappingRegisterer struct {
65 wrappedRegisterer Registerer
66 prefix string
67 labels Labels
68}
69
70func (r *wrappingRegisterer) Register(c Collector) error {
71 return r.wrappedRegisterer.Register(&wrappingCollector{
72 wrappedCollector: c,
73 prefix: r.prefix,
74 labels: r.labels,
75 })
76}
77
78func (r *wrappingRegisterer) MustRegister(cs ...Collector) {
79 for _, c := range cs {
80 if err := r.Register(c); err != nil {
81 panic(err)
82 }
83 }
84}
85
86func (r *wrappingRegisterer) Unregister(c Collector) bool {
87 return r.wrappedRegisterer.Unregister(&wrappingCollector{
88 wrappedCollector: c,
89 prefix: r.prefix,
90 labels: r.labels,
91 })
92}
93
94type wrappingCollector struct {
95 wrappedCollector Collector
96 prefix string
97 labels Labels
98}
99
100func (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
115func (c *wrappingCollector) Describe(ch chan<- *Desc) {
116 wrappedCh := make(chan *Desc)
117 go func() {
118 c.wrappedCollector.Describe(wrappedCh)
119 close(wrappedCh)
120 }()
121 for desc := range wrappedCh {
122 ch <- wrapDesc(desc, c.prefix, c.labels)
123 }
124}
125
126type wrappingMetric struct {
127 wrappedMetric Metric
128 prefix string
129 labels Labels
130}
131
132func (m *wrappingMetric) Desc() *Desc {
133 return wrapDesc(m.wrappedMetric.Desc(), m.prefix, m.labels)
134}
135
136func (m *wrappingMetric) Write(out *dto.Metric) error {
137 if err := m.wrappedMetric.Write(out); err != nil {
138 return err
139 }
140 if len(m.labels) == 0 {
141 // No wrapping labels.
142 return nil
143 }
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),
148 })
149 }
150 sort.Sort(labelPairSorter(out.Label))
151 return nil
152}
153
154func wrapDesc(desc *Desc, prefix string, labels Labels) *Desc {
155 constLabels := Labels{}
156 for _, lp := range desc.constLabelPairs {
157 constLabels[*lp.Name] = *lp.Value
158 }
159 for ln, lv := range labels {
160 if _, alreadyUsed := constLabels[ln]; alreadyUsed {
161 return &Desc{
162 fqName: desc.fqName,
163 help: desc.help,
164 variableLabels: desc.variableLabels,
165 constLabelPairs: desc.constLabelPairs,
166 err: fmt.Errorf("attempted wrapping with already existing label name %q", ln),
167 }
168 }
169 constLabels[ln] = lv
170 }
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.
175 if desc.err != nil {
176 newDesc.err = desc.err
177 }
178 return newDesc
179}