blob: 7f3eef9a46707813913a49bc466528ccd87a6c2f [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001// Copyright 2014 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 "sync"
19
20 "github.com/prometheus/common/model"
21)
22
23// MetricVec is a Collector to bundle metrics of the same name that
24// differ in their label values. MetricVec is usually not used directly but as a
25// building block for implementations of vectors of a given metric
26// type. GaugeVec, CounterVec, SummaryVec, and UntypedVec are examples already
27// provided in this package.
28type MetricVec struct {
29 mtx sync.RWMutex // Protects the children.
30 children map[uint64][]metricWithLabelValues
31 desc *Desc
32
33 newMetric func(labelValues ...string) Metric
34 hashAdd func(h uint64, s string) uint64 // replace hash function for testing collision handling
35 hashAddByte func(h uint64, b byte) uint64
36}
37
38// newMetricVec returns an initialized MetricVec. The concrete value is
39// returned for embedding into another struct.
40func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *MetricVec {
41 return &MetricVec{
42 children: map[uint64][]metricWithLabelValues{},
43 desc: desc,
44 newMetric: newMetric,
45 hashAdd: hashAdd,
46 hashAddByte: hashAddByte,
47 }
48}
49
50// metricWithLabelValues provides the metric and its label values for
51// disambiguation on hash collision.
52type metricWithLabelValues struct {
53 values []string
54 metric Metric
55}
56
57// Describe implements Collector. The length of the returned slice
58// is always one.
59func (m *MetricVec) Describe(ch chan<- *Desc) {
60 ch <- m.desc
61}
62
63// Collect implements Collector.
64func (m *MetricVec) Collect(ch chan<- Metric) {
65 m.mtx.RLock()
66 defer m.mtx.RUnlock()
67
68 for _, metrics := range m.children {
69 for _, metric := range metrics {
70 ch <- metric.metric
71 }
72 }
73}
74
75// GetMetricWithLabelValues returns the Metric for the given slice of label
76// values (same order as the VariableLabels in Desc). If that combination of
77// label values is accessed for the first time, a new Metric is created.
78//
79// It is possible to call this method without using the returned Metric to only
80// create the new Metric but leave it at its start value (e.g. a Summary or
81// Histogram without any observations). See also the SummaryVec example.
82//
83// Keeping the Metric for later use is possible (and should be considered if
84// performance is critical), but keep in mind that Reset, DeleteLabelValues and
85// Delete can be used to delete the Metric from the MetricVec. In that case, the
86// Metric will still exist, but it will not be exported anymore, even if a
87// Metric with the same label values is created later. See also the CounterVec
88// example.
89//
90// An error is returned if the number of label values is not the same as the
91// number of VariableLabels in Desc.
92//
93// Note that for more than one label value, this method is prone to mistakes
94// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
95// an alternative to avoid that type of mistake. For higher label numbers, the
96// latter has a much more readable (albeit more verbose) syntax, but it comes
97// with a performance overhead (for creating and processing the Labels map).
98// See also the GaugeVec example.
99func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) {
100 h, err := m.hashLabelValues(lvs)
101 if err != nil {
102 return nil, err
103 }
104
105 return m.getOrCreateMetricWithLabelValues(h, lvs), nil
106}
107
108// GetMetricWith returns the Metric for the given Labels map (the label names
109// must match those of the VariableLabels in Desc). If that label map is
110// accessed for the first time, a new Metric is created. Implications of
111// creating a Metric without using it and keeping the Metric for later use are
112// the same as for GetMetricWithLabelValues.
113//
114// An error is returned if the number and names of the Labels are inconsistent
115// with those of the VariableLabels in Desc.
116//
117// This method is used for the same purpose as
118// GetMetricWithLabelValues(...string). See there for pros and cons of the two
119// methods.
120func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
121 h, err := m.hashLabels(labels)
122 if err != nil {
123 return nil, err
124 }
125
126 return m.getOrCreateMetricWithLabels(h, labels), nil
127}
128
129// WithLabelValues works as GetMetricWithLabelValues, but panics if an error
130// occurs. The method allows neat syntax like:
131// httpReqs.WithLabelValues("404", "POST").Inc()
132func (m *MetricVec) WithLabelValues(lvs ...string) Metric {
133 metric, err := m.GetMetricWithLabelValues(lvs...)
134 if err != nil {
135 panic(err)
136 }
137 return metric
138}
139
140// With works as GetMetricWith, but panics if an error occurs. The method allows
141// neat syntax like:
142// httpReqs.With(Labels{"status":"404", "method":"POST"}).Inc()
143func (m *MetricVec) With(labels Labels) Metric {
144 metric, err := m.GetMetricWith(labels)
145 if err != nil {
146 panic(err)
147 }
148 return metric
149}
150
151// DeleteLabelValues removes the metric where the variable labels are the same
152// as those passed in as labels (same order as the VariableLabels in Desc). It
153// returns true if a metric was deleted.
154//
155// It is not an error if the number of label values is not the same as the
156// number of VariableLabels in Desc. However, such inconsistent label count can
157// never match an actual Metric, so the method will always return false in that
158// case.
159//
160// Note that for more than one label value, this method is prone to mistakes
161// caused by an incorrect order of arguments. Consider Delete(Labels) as an
162// alternative to avoid that type of mistake. For higher label numbers, the
163// latter has a much more readable (albeit more verbose) syntax, but it comes
164// with a performance overhead (for creating and processing the Labels map).
165// See also the CounterVec example.
166func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
167 m.mtx.Lock()
168 defer m.mtx.Unlock()
169
170 h, err := m.hashLabelValues(lvs)
171 if err != nil {
172 return false
173 }
174 return m.deleteByHashWithLabelValues(h, lvs)
175}
176
177// Delete deletes the metric where the variable labels are the same as those
178// passed in as labels. It returns true if a metric was deleted.
179//
180// It is not an error if the number and names of the Labels are inconsistent
181// with those of the VariableLabels in the Desc of the MetricVec. However, such
182// inconsistent Labels can never match an actual Metric, so the method will
183// always return false in that case.
184//
185// This method is used for the same purpose as DeleteLabelValues(...string). See
186// there for pros and cons of the two methods.
187func (m *MetricVec) Delete(labels Labels) bool {
188 m.mtx.Lock()
189 defer m.mtx.Unlock()
190
191 h, err := m.hashLabels(labels)
192 if err != nil {
193 return false
194 }
195
196 return m.deleteByHashWithLabels(h, labels)
197}
198
199// deleteByHashWithLabelValues removes the metric from the hash bucket h. If
200// there are multiple matches in the bucket, use lvs to select a metric and
201// remove only that metric.
202func (m *MetricVec) deleteByHashWithLabelValues(h uint64, lvs []string) bool {
203 metrics, ok := m.children[h]
204 if !ok {
205 return false
206 }
207
208 i := m.findMetricWithLabelValues(metrics, lvs)
209 if i >= len(metrics) {
210 return false
211 }
212
213 if len(metrics) > 1 {
214 m.children[h] = append(metrics[:i], metrics[i+1:]...)
215 } else {
216 delete(m.children, h)
217 }
218 return true
219}
220
221// deleteByHashWithLabels removes the metric from the hash bucket h. If there
222// are multiple matches in the bucket, use lvs to select a metric and remove
223// only that metric.
224func (m *MetricVec) deleteByHashWithLabels(h uint64, labels Labels) bool {
225 metrics, ok := m.children[h]
226 if !ok {
227 return false
228 }
229 i := m.findMetricWithLabels(metrics, labels)
230 if i >= len(metrics) {
231 return false
232 }
233
234 if len(metrics) > 1 {
235 m.children[h] = append(metrics[:i], metrics[i+1:]...)
236 } else {
237 delete(m.children, h)
238 }
239 return true
240}
241
242// Reset deletes all metrics in this vector.
243func (m *MetricVec) Reset() {
244 m.mtx.Lock()
245 defer m.mtx.Unlock()
246
247 for h := range m.children {
248 delete(m.children, h)
249 }
250}
251
252func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
253 if len(vals) != len(m.desc.variableLabels) {
254 return 0, errInconsistentCardinality
255 }
256 h := hashNew()
257 for _, val := range vals {
258 h = m.hashAdd(h, val)
259 h = m.hashAddByte(h, model.SeparatorByte)
260 }
261 return h, nil
262}
263
264func (m *MetricVec) hashLabels(labels Labels) (uint64, error) {
265 if len(labels) != len(m.desc.variableLabels) {
266 return 0, errInconsistentCardinality
267 }
268 h := hashNew()
269 for _, label := range m.desc.variableLabels {
270 val, ok := labels[label]
271 if !ok {
272 return 0, fmt.Errorf("label name %q missing in label map", label)
273 }
274 h = m.hashAdd(h, val)
275 h = m.hashAddByte(h, model.SeparatorByte)
276 }
277 return h, nil
278}
279
280// getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
281// or creates it and returns the new one.
282//
283// This function holds the mutex.
284func (m *MetricVec) getOrCreateMetricWithLabelValues(hash uint64, lvs []string) Metric {
285 m.mtx.RLock()
286 metric, ok := m.getMetricWithLabelValues(hash, lvs)
287 m.mtx.RUnlock()
288 if ok {
289 return metric
290 }
291
292 m.mtx.Lock()
293 defer m.mtx.Unlock()
294 metric, ok = m.getMetricWithLabelValues(hash, lvs)
295 if !ok {
296 // Copy to avoid allocation in case wo don't go down this code path.
297 copiedLVs := make([]string, len(lvs))
298 copy(copiedLVs, lvs)
299 metric = m.newMetric(copiedLVs...)
300 m.children[hash] = append(m.children[hash], metricWithLabelValues{values: copiedLVs, metric: metric})
301 }
302 return metric
303}
304
305// getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
306// or creates it and returns the new one.
307//
308// This function holds the mutex.
309func (m *MetricVec) getOrCreateMetricWithLabels(hash uint64, labels Labels) Metric {
310 m.mtx.RLock()
311 metric, ok := m.getMetricWithLabels(hash, labels)
312 m.mtx.RUnlock()
313 if ok {
314 return metric
315 }
316
317 m.mtx.Lock()
318 defer m.mtx.Unlock()
319 metric, ok = m.getMetricWithLabels(hash, labels)
320 if !ok {
321 lvs := m.extractLabelValues(labels)
322 metric = m.newMetric(lvs...)
323 m.children[hash] = append(m.children[hash], metricWithLabelValues{values: lvs, metric: metric})
324 }
325 return metric
326}
327
328// getMetricWithLabelValues gets a metric while handling possible collisions in
329// the hash space. Must be called while holding read mutex.
330func (m *MetricVec) getMetricWithLabelValues(h uint64, lvs []string) (Metric, bool) {
331 metrics, ok := m.children[h]
332 if ok {
333 if i := m.findMetricWithLabelValues(metrics, lvs); i < len(metrics) {
334 return metrics[i].metric, true
335 }
336 }
337 return nil, false
338}
339
340// getMetricWithLabels gets a metric while handling possible collisions in
341// the hash space. Must be called while holding read mutex.
342func (m *MetricVec) getMetricWithLabels(h uint64, labels Labels) (Metric, bool) {
343 metrics, ok := m.children[h]
344 if ok {
345 if i := m.findMetricWithLabels(metrics, labels); i < len(metrics) {
346 return metrics[i].metric, true
347 }
348 }
349 return nil, false
350}
351
352// findMetricWithLabelValues returns the index of the matching metric or
353// len(metrics) if not found.
354func (m *MetricVec) findMetricWithLabelValues(metrics []metricWithLabelValues, lvs []string) int {
355 for i, metric := range metrics {
356 if m.matchLabelValues(metric.values, lvs) {
357 return i
358 }
359 }
360 return len(metrics)
361}
362
363// findMetricWithLabels returns the index of the matching metric or len(metrics)
364// if not found.
365func (m *MetricVec) findMetricWithLabels(metrics []metricWithLabelValues, labels Labels) int {
366 for i, metric := range metrics {
367 if m.matchLabels(metric.values, labels) {
368 return i
369 }
370 }
371 return len(metrics)
372}
373
374func (m *MetricVec) matchLabelValues(values []string, lvs []string) bool {
375 if len(values) != len(lvs) {
376 return false
377 }
378 for i, v := range values {
379 if v != lvs[i] {
380 return false
381 }
382 }
383 return true
384}
385
386func (m *MetricVec) matchLabels(values []string, labels Labels) bool {
387 if len(labels) != len(values) {
388 return false
389 }
390 for i, k := range m.desc.variableLabels {
391 if values[i] != labels[k] {
392 return false
393 }
394 }
395 return true
396}
397
398func (m *MetricVec) extractLabelValues(labels Labels) []string {
399 labelValues := make([]string, len(labels))
400 for i, k := range m.desc.variableLabels {
401 labelValues[i] = labels[k]
402 }
403 return labelValues
404}