Remove BPA from Makefile
[icn.git] / cmd / bpa-operator / vendor / github.com / prometheus / client_golang / prometheus / registry.go
1 // 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
14 package prometheus
15
16 import (
17         "bytes"
18         "fmt"
19         "io/ioutil"
20         "os"
21         "path/filepath"
22         "runtime"
23         "sort"
24         "strings"
25         "sync"
26         "unicode/utf8"
27
28         "github.com/golang/protobuf/proto"
29         "github.com/prometheus/common/expfmt"
30
31         dto "github.com/prometheus/client_model/go"
32
33         "github.com/prometheus/client_golang/prometheus/internal"
34 )
35
36 const (
37         // Capacity for the channel to collect metrics and descriptors.
38         capMetricChan = 1000
39         capDescChan   = 10
40 )
41
42 // DefaultRegisterer and DefaultGatherer are the implementations of the
43 // Registerer and Gatherer interface a number of convenience functions in this
44 // package act on. Initially, both variables point to the same Registry, which
45 // has a process collector (currently on Linux only, see NewProcessCollector)
46 // and a Go collector (see NewGoCollector, in particular the note about
47 // stop-the-world implication with Go versions older than 1.9) already
48 // registered. This approach to keep default instances as global state mirrors
49 // the approach of other packages in the Go standard library. Note that there
50 // are caveats. Change the variables with caution and only if you understand the
51 // consequences. Users who want to avoid global state altogether should not use
52 // the convenience functions and act on custom instances instead.
53 var (
54         defaultRegistry              = NewRegistry()
55         DefaultRegisterer Registerer = defaultRegistry
56         DefaultGatherer   Gatherer   = defaultRegistry
57 )
58
59 func init() {
60         MustRegister(NewProcessCollector(ProcessCollectorOpts{}))
61         MustRegister(NewGoCollector())
62 }
63
64 // NewRegistry creates a new vanilla Registry without any Collectors
65 // pre-registered.
66 func NewRegistry() *Registry {
67         return &Registry{
68                 collectorsByID:  map[uint64]Collector{},
69                 descIDs:         map[uint64]struct{}{},
70                 dimHashesByName: map[string]uint64{},
71         }
72 }
73
74 // NewPedanticRegistry returns a registry that checks during collection if each
75 // collected Metric is consistent with its reported Desc, and if the Desc has
76 // actually been registered with the registry. Unchecked Collectors (those whose
77 // Describe methed does not yield any descriptors) are excluded from the check.
78 //
79 // Usually, a Registry will be happy as long as the union of all collected
80 // Metrics is consistent and valid even if some metrics are not consistent with
81 // their own Desc or a Desc provided by their registered Collector. Well-behaved
82 // Collectors and Metrics will only provide consistent Descs. This Registry is
83 // useful to test the implementation of Collectors and Metrics.
84 func NewPedanticRegistry() *Registry {
85         r := NewRegistry()
86         r.pedanticChecksEnabled = true
87         return r
88 }
89
90 // Registerer is the interface for the part of a registry in charge of
91 // registering and unregistering. Users of custom registries should use
92 // Registerer as type for registration purposes (rather than the Registry type
93 // directly). In that way, they are free to use custom Registerer implementation
94 // (e.g. for testing purposes).
95 type Registerer interface {
96         // Register registers a new Collector to be included in metrics
97         // collection. It returns an error if the descriptors provided by the
98         // Collector are invalid or if they — in combination with descriptors of
99         // already registered Collectors — do not fulfill the consistency and
100         // uniqueness criteria described in the documentation of metric.Desc.
101         //
102         // If the provided Collector is equal to a Collector already registered
103         // (which includes the case of re-registering the same Collector), the
104         // returned error is an instance of AlreadyRegisteredError, which
105         // contains the previously registered Collector.
106         //
107         // A Collector whose Describe method does not yield any Desc is treated
108         // as unchecked. Registration will always succeed. No check for
109         // re-registering (see previous paragraph) is performed. Thus, the
110         // caller is responsible for not double-registering the same unchecked
111         // Collector, and for providing a Collector that will not cause
112         // inconsistent metrics on collection. (This would lead to scrape
113         // errors.)
114         Register(Collector) error
115         // MustRegister works like Register but registers any number of
116         // Collectors and panics upon the first registration that causes an
117         // error.
118         MustRegister(...Collector)
119         // Unregister unregisters the Collector that equals the Collector passed
120         // in as an argument.  (Two Collectors are considered equal if their
121         // Describe method yields the same set of descriptors.) The function
122         // returns whether a Collector was unregistered. Note that an unchecked
123         // Collector cannot be unregistered (as its Describe method does not
124         // yield any descriptor).
125         //
126         // Note that even after unregistering, it will not be possible to
127         // register a new Collector that is inconsistent with the unregistered
128         // Collector, e.g. a Collector collecting metrics with the same name but
129         // a different help string. The rationale here is that the same registry
130         // instance must only collect consistent metrics throughout its
131         // lifetime.
132         Unregister(Collector) bool
133 }
134
135 // Gatherer is the interface for the part of a registry in charge of gathering
136 // the collected metrics into a number of MetricFamilies. The Gatherer interface
137 // comes with the same general implication as described for the Registerer
138 // interface.
139 type Gatherer interface {
140         // Gather calls the Collect method of the registered Collectors and then
141         // gathers the collected metrics into a lexicographically sorted slice
142         // of uniquely named MetricFamily protobufs. Gather ensures that the
143         // returned slice is valid and self-consistent so that it can be used
144         // for valid exposition. As an exception to the strict consistency
145         // requirements described for metric.Desc, Gather will tolerate
146         // different sets of label names for metrics of the same metric family.
147         //
148         // Even if an error occurs, Gather attempts to gather as many metrics as
149         // possible. Hence, if a non-nil error is returned, the returned
150         // MetricFamily slice could be nil (in case of a fatal error that
151         // prevented any meaningful metric collection) or contain a number of
152         // MetricFamily protobufs, some of which might be incomplete, and some
153         // might be missing altogether. The returned error (which might be a
154         // MultiError) explains the details. Note that this is mostly useful for
155         // debugging purposes. If the gathered protobufs are to be used for
156         // exposition in actual monitoring, it is almost always better to not
157         // expose an incomplete result and instead disregard the returned
158         // MetricFamily protobufs in case the returned error is non-nil.
159         Gather() ([]*dto.MetricFamily, error)
160 }
161
162 // Register registers the provided Collector with the DefaultRegisterer.
163 //
164 // Register is a shortcut for DefaultRegisterer.Register(c). See there for more
165 // details.
166 func Register(c Collector) error {
167         return DefaultRegisterer.Register(c)
168 }
169
170 // MustRegister registers the provided Collectors with the DefaultRegisterer and
171 // panics if any error occurs.
172 //
173 // MustRegister is a shortcut for DefaultRegisterer.MustRegister(cs...). See
174 // there for more details.
175 func MustRegister(cs ...Collector) {
176         DefaultRegisterer.MustRegister(cs...)
177 }
178
179 // Unregister removes the registration of the provided Collector from the
180 // DefaultRegisterer.
181 //
182 // Unregister is a shortcut for DefaultRegisterer.Unregister(c). See there for
183 // more details.
184 func Unregister(c Collector) bool {
185         return DefaultRegisterer.Unregister(c)
186 }
187
188 // GathererFunc turns a function into a Gatherer.
189 type GathererFunc func() ([]*dto.MetricFamily, error)
190
191 // Gather implements Gatherer.
192 func (gf GathererFunc) Gather() ([]*dto.MetricFamily, error) {
193         return gf()
194 }
195
196 // AlreadyRegisteredError is returned by the Register method if the Collector to
197 // be registered has already been registered before, or a different Collector
198 // that collects the same metrics has been registered before. Registration fails
199 // in that case, but you can detect from the kind of error what has
200 // happened. The error contains fields for the existing Collector and the
201 // (rejected) new Collector that equals the existing one. This can be used to
202 // find out if an equal Collector has been registered before and switch over to
203 // using the old one, as demonstrated in the example.
204 type AlreadyRegisteredError struct {
205         ExistingCollector, NewCollector Collector
206 }
207
208 func (err AlreadyRegisteredError) Error() string {
209         return "duplicate metrics collector registration attempted"
210 }
211
212 // MultiError is a slice of errors implementing the error interface. It is used
213 // by a Gatherer to report multiple errors during MetricFamily gathering.
214 type MultiError []error
215
216 func (errs MultiError) Error() string {
217         if len(errs) == 0 {
218                 return ""
219         }
220         buf := &bytes.Buffer{}
221         fmt.Fprintf(buf, "%d error(s) occurred:", len(errs))
222         for _, err := range errs {
223                 fmt.Fprintf(buf, "\n* %s", err)
224         }
225         return buf.String()
226 }
227
228 // Append appends the provided error if it is not nil.
229 func (errs *MultiError) Append(err error) {
230         if err != nil {
231                 *errs = append(*errs, err)
232         }
233 }
234
235 // MaybeUnwrap returns nil if len(errs) is 0. It returns the first and only
236 // contained error as error if len(errs is 1). In all other cases, it returns
237 // the MultiError directly. This is helpful for returning a MultiError in a way
238 // that only uses the MultiError if needed.
239 func (errs MultiError) MaybeUnwrap() error {
240         switch len(errs) {
241         case 0:
242                 return nil
243         case 1:
244                 return errs[0]
245         default:
246                 return errs
247         }
248 }
249
250 // Registry registers Prometheus collectors, collects their metrics, and gathers
251 // them into MetricFamilies for exposition. It implements both Registerer and
252 // Gatherer. The zero value is not usable. Create instances with NewRegistry or
253 // NewPedanticRegistry.
254 type Registry struct {
255         mtx                   sync.RWMutex
256         collectorsByID        map[uint64]Collector // ID is a hash of the descIDs.
257         descIDs               map[uint64]struct{}
258         dimHashesByName       map[string]uint64
259         uncheckedCollectors   []Collector
260         pedanticChecksEnabled bool
261 }
262
263 // Register implements Registerer.
264 func (r *Registry) Register(c Collector) error {
265         var (
266                 descChan           = make(chan *Desc, capDescChan)
267                 newDescIDs         = map[uint64]struct{}{}
268                 newDimHashesByName = map[string]uint64{}
269                 collectorID        uint64 // Just a sum of all desc IDs.
270                 duplicateDescErr   error
271         )
272         go func() {
273                 c.Describe(descChan)
274                 close(descChan)
275         }()
276         r.mtx.Lock()
277         defer func() {
278                 // Drain channel in case of premature return to not leak a goroutine.
279                 for range descChan {
280                 }
281                 r.mtx.Unlock()
282         }()
283         // Conduct various tests...
284         for desc := range descChan {
285
286                 // Is the descriptor valid at all?
287                 if desc.err != nil {
288                         return fmt.Errorf("descriptor %s is invalid: %s", desc, desc.err)
289                 }
290
291                 // Is the descID unique?
292                 // (In other words: Is the fqName + constLabel combination unique?)
293                 if _, exists := r.descIDs[desc.id]; exists {
294                         duplicateDescErr = fmt.Errorf("descriptor %s already exists with the same fully-qualified name and const label values", desc)
295                 }
296                 // If it is not a duplicate desc in this collector, add it to
297                 // the collectorID.  (We allow duplicate descs within the same
298                 // collector, but their existence must be a no-op.)
299                 if _, exists := newDescIDs[desc.id]; !exists {
300                         newDescIDs[desc.id] = struct{}{}
301                         collectorID += desc.id
302                 }
303
304                 // Are all the label names and the help string consistent with
305                 // previous descriptors of the same name?
306                 // First check existing descriptors...
307                 if dimHash, exists := r.dimHashesByName[desc.fqName]; exists {
308                         if dimHash != desc.dimHash {
309                                 return fmt.Errorf("a previously registered descriptor with the same fully-qualified name as %s has different label names or a different help string", desc)
310                         }
311                 } else {
312                         // ...then check the new descriptors already seen.
313                         if dimHash, exists := newDimHashesByName[desc.fqName]; exists {
314                                 if dimHash != desc.dimHash {
315                                         return fmt.Errorf("descriptors reported by collector have inconsistent label names or help strings for the same fully-qualified name, offender is %s", desc)
316                                 }
317                         } else {
318                                 newDimHashesByName[desc.fqName] = desc.dimHash
319                         }
320                 }
321         }
322         // A Collector yielding no Desc at all is considered unchecked.
323         if len(newDescIDs) == 0 {
324                 r.uncheckedCollectors = append(r.uncheckedCollectors, c)
325                 return nil
326         }
327         if existing, exists := r.collectorsByID[collectorID]; exists {
328                 return AlreadyRegisteredError{
329                         ExistingCollector: existing,
330                         NewCollector:      c,
331                 }
332         }
333         // If the collectorID is new, but at least one of the descs existed
334         // before, we are in trouble.
335         if duplicateDescErr != nil {
336                 return duplicateDescErr
337         }
338
339         // Only after all tests have passed, actually register.
340         r.collectorsByID[collectorID] = c
341         for hash := range newDescIDs {
342                 r.descIDs[hash] = struct{}{}
343         }
344         for name, dimHash := range newDimHashesByName {
345                 r.dimHashesByName[name] = dimHash
346         }
347         return nil
348 }
349
350 // Unregister implements Registerer.
351 func (r *Registry) Unregister(c Collector) bool {
352         var (
353                 descChan    = make(chan *Desc, capDescChan)
354                 descIDs     = map[uint64]struct{}{}
355                 collectorID uint64 // Just a sum of the desc IDs.
356         )
357         go func() {
358                 c.Describe(descChan)
359                 close(descChan)
360         }()
361         for desc := range descChan {
362                 if _, exists := descIDs[desc.id]; !exists {
363                         collectorID += desc.id
364                         descIDs[desc.id] = struct{}{}
365                 }
366         }
367
368         r.mtx.RLock()
369         if _, exists := r.collectorsByID[collectorID]; !exists {
370                 r.mtx.RUnlock()
371                 return false
372         }
373         r.mtx.RUnlock()
374
375         r.mtx.Lock()
376         defer r.mtx.Unlock()
377
378         delete(r.collectorsByID, collectorID)
379         for id := range descIDs {
380                 delete(r.descIDs, id)
381         }
382         // dimHashesByName is left untouched as those must be consistent
383         // throughout the lifetime of a program.
384         return true
385 }
386
387 // MustRegister implements Registerer.
388 func (r *Registry) MustRegister(cs ...Collector) {
389         for _, c := range cs {
390                 if err := r.Register(c); err != nil {
391                         panic(err)
392                 }
393         }
394 }
395
396 // Gather implements Gatherer.
397 func (r *Registry) Gather() ([]*dto.MetricFamily, error) {
398         var (
399                 checkedMetricChan   = make(chan Metric, capMetricChan)
400                 uncheckedMetricChan = make(chan Metric, capMetricChan)
401                 metricHashes        = map[uint64]struct{}{}
402                 wg                  sync.WaitGroup
403                 errs                MultiError          // The collected errors to return in the end.
404                 registeredDescIDs   map[uint64]struct{} // Only used for pedantic checks
405         )
406
407         r.mtx.RLock()
408         goroutineBudget := len(r.collectorsByID) + len(r.uncheckedCollectors)
409         metricFamiliesByName := make(map[string]*dto.MetricFamily, len(r.dimHashesByName))
410         checkedCollectors := make(chan Collector, len(r.collectorsByID))
411         uncheckedCollectors := make(chan Collector, len(r.uncheckedCollectors))
412         for _, collector := range r.collectorsByID {
413                 checkedCollectors <- collector
414         }
415         for _, collector := range r.uncheckedCollectors {
416                 uncheckedCollectors <- collector
417         }
418         // In case pedantic checks are enabled, we have to copy the map before
419         // giving up the RLock.
420         if r.pedanticChecksEnabled {
421                 registeredDescIDs = make(map[uint64]struct{}, len(r.descIDs))
422                 for id := range r.descIDs {
423                         registeredDescIDs[id] = struct{}{}
424                 }
425         }
426         r.mtx.RUnlock()
427
428         wg.Add(goroutineBudget)
429
430         collectWorker := func() {
431                 for {
432                         select {
433                         case collector := <-checkedCollectors:
434                                 collector.Collect(checkedMetricChan)
435                         case collector := <-uncheckedCollectors:
436                                 collector.Collect(uncheckedMetricChan)
437                         default:
438                                 return
439                         }
440                         wg.Done()
441                 }
442         }
443
444         // Start the first worker now to make sure at least one is running.
445         go collectWorker()
446         goroutineBudget--
447
448         // Close checkedMetricChan and uncheckedMetricChan once all collectors
449         // are collected.
450         go func() {
451                 wg.Wait()
452                 close(checkedMetricChan)
453                 close(uncheckedMetricChan)
454         }()
455
456         // Drain checkedMetricChan and uncheckedMetricChan in case of premature return.
457         defer func() {
458                 if checkedMetricChan != nil {
459                         for range checkedMetricChan {
460                         }
461                 }
462                 if uncheckedMetricChan != nil {
463                         for range uncheckedMetricChan {
464                         }
465                 }
466         }()
467
468         // Copy the channel references so we can nil them out later to remove
469         // them from the select statements below.
470         cmc := checkedMetricChan
471         umc := uncheckedMetricChan
472
473         for {
474                 select {
475                 case metric, ok := <-cmc:
476                         if !ok {
477                                 cmc = nil
478                                 break
479                         }
480                         errs.Append(processMetric(
481                                 metric, metricFamiliesByName,
482                                 metricHashes,
483                                 registeredDescIDs,
484                         ))
485                 case metric, ok := <-umc:
486                         if !ok {
487                                 umc = nil
488                                 break
489                         }
490                         errs.Append(processMetric(
491                                 metric, metricFamiliesByName,
492                                 metricHashes,
493                                 nil,
494                         ))
495                 default:
496                         if goroutineBudget <= 0 || len(checkedCollectors)+len(uncheckedCollectors) == 0 {
497                                 // All collectors are already being worked on or
498                                 // we have already as many goroutines started as
499                                 // there are collectors. Do the same as above,
500                                 // just without the default.
501                                 select {
502                                 case metric, ok := <-cmc:
503                                         if !ok {
504                                                 cmc = nil
505                                                 break
506                                         }
507                                         errs.Append(processMetric(
508                                                 metric, metricFamiliesByName,
509                                                 metricHashes,
510                                                 registeredDescIDs,
511                                         ))
512                                 case metric, ok := <-umc:
513                                         if !ok {
514                                                 umc = nil
515                                                 break
516                                         }
517                                         errs.Append(processMetric(
518                                                 metric, metricFamiliesByName,
519                                                 metricHashes,
520                                                 nil,
521                                         ))
522                                 }
523                                 break
524                         }
525                         // Start more workers.
526                         go collectWorker()
527                         goroutineBudget--
528                         runtime.Gosched()
529                 }
530                 // Once both checkedMetricChan and uncheckdMetricChan are closed
531                 // and drained, the contraption above will nil out cmc and umc,
532                 // and then we can leave the collect loop here.
533                 if cmc == nil && umc == nil {
534                         break
535                 }
536         }
537         return internal.NormalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap()
538 }
539
540 // WriteToTextfile calls Gather on the provided Gatherer, encodes the result in the
541 // Prometheus text format, and writes it to a temporary file. Upon success, the
542 // temporary file is renamed to the provided filename.
543 //
544 // This is intended for use with the textfile collector of the node exporter.
545 // Note that the node exporter expects the filename to be suffixed with ".prom".
546 func WriteToTextfile(filename string, g Gatherer) error {
547         tmp, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename))
548         if err != nil {
549                 return err
550         }
551         defer os.Remove(tmp.Name())
552
553         mfs, err := g.Gather()
554         if err != nil {
555                 return err
556         }
557         for _, mf := range mfs {
558                 if _, err := expfmt.MetricFamilyToText(tmp, mf); err != nil {
559                         return err
560                 }
561         }
562         if err := tmp.Close(); err != nil {
563                 return err
564         }
565
566         if err := os.Chmod(tmp.Name(), 0644); err != nil {
567                 return err
568         }
569         return os.Rename(tmp.Name(), filename)
570 }
571
572 // processMetric is an internal helper method only used by the Gather method.
573 func processMetric(
574         metric Metric,
575         metricFamiliesByName map[string]*dto.MetricFamily,
576         metricHashes map[uint64]struct{},
577         registeredDescIDs map[uint64]struct{},
578 ) error {
579         desc := metric.Desc()
580         // Wrapped metrics collected by an unchecked Collector can have an
581         // invalid Desc.
582         if desc.err != nil {
583                 return desc.err
584         }
585         dtoMetric := &dto.Metric{}
586         if err := metric.Write(dtoMetric); err != nil {
587                 return fmt.Errorf("error collecting metric %v: %s", desc, err)
588         }
589         metricFamily, ok := metricFamiliesByName[desc.fqName]
590         if ok { // Existing name.
591                 if metricFamily.GetHelp() != desc.help {
592                         return fmt.Errorf(
593                                 "collected metric %s %s has help %q but should have %q",
594                                 desc.fqName, dtoMetric, desc.help, metricFamily.GetHelp(),
595                         )
596                 }
597                 // TODO(beorn7): Simplify switch once Desc has type.
598                 switch metricFamily.GetType() {
599                 case dto.MetricType_COUNTER:
600                         if dtoMetric.Counter == nil {
601                                 return fmt.Errorf(
602                                         "collected metric %s %s should be a Counter",
603                                         desc.fqName, dtoMetric,
604                                 )
605                         }
606                 case dto.MetricType_GAUGE:
607                         if dtoMetric.Gauge == nil {
608                                 return fmt.Errorf(
609                                         "collected metric %s %s should be a Gauge",
610                                         desc.fqName, dtoMetric,
611                                 )
612                         }
613                 case dto.MetricType_SUMMARY:
614                         if dtoMetric.Summary == nil {
615                                 return fmt.Errorf(
616                                         "collected metric %s %s should be a Summary",
617                                         desc.fqName, dtoMetric,
618                                 )
619                         }
620                 case dto.MetricType_UNTYPED:
621                         if dtoMetric.Untyped == nil {
622                                 return fmt.Errorf(
623                                         "collected metric %s %s should be Untyped",
624                                         desc.fqName, dtoMetric,
625                                 )
626                         }
627                 case dto.MetricType_HISTOGRAM:
628                         if dtoMetric.Histogram == nil {
629                                 return fmt.Errorf(
630                                         "collected metric %s %s should be a Histogram",
631                                         desc.fqName, dtoMetric,
632                                 )
633                         }
634                 default:
635                         panic("encountered MetricFamily with invalid type")
636                 }
637         } else { // New name.
638                 metricFamily = &dto.MetricFamily{}
639                 metricFamily.Name = proto.String(desc.fqName)
640                 metricFamily.Help = proto.String(desc.help)
641                 // TODO(beorn7): Simplify switch once Desc has type.
642                 switch {
643                 case dtoMetric.Gauge != nil:
644                         metricFamily.Type = dto.MetricType_GAUGE.Enum()
645                 case dtoMetric.Counter != nil:
646                         metricFamily.Type = dto.MetricType_COUNTER.Enum()
647                 case dtoMetric.Summary != nil:
648                         metricFamily.Type = dto.MetricType_SUMMARY.Enum()
649                 case dtoMetric.Untyped != nil:
650                         metricFamily.Type = dto.MetricType_UNTYPED.Enum()
651                 case dtoMetric.Histogram != nil:
652                         metricFamily.Type = dto.MetricType_HISTOGRAM.Enum()
653                 default:
654                         return fmt.Errorf("empty metric collected: %s", dtoMetric)
655                 }
656                 if err := checkSuffixCollisions(metricFamily, metricFamiliesByName); err != nil {
657                         return err
658                 }
659                 metricFamiliesByName[desc.fqName] = metricFamily
660         }
661         if err := checkMetricConsistency(metricFamily, dtoMetric, metricHashes); err != nil {
662                 return err
663         }
664         if registeredDescIDs != nil {
665                 // Is the desc registered at all?
666                 if _, exist := registeredDescIDs[desc.id]; !exist {
667                         return fmt.Errorf(
668                                 "collected metric %s %s with unregistered descriptor %s",
669                                 metricFamily.GetName(), dtoMetric, desc,
670                         )
671                 }
672                 if err := checkDescConsistency(metricFamily, dtoMetric, desc); err != nil {
673                         return err
674                 }
675         }
676         metricFamily.Metric = append(metricFamily.Metric, dtoMetric)
677         return nil
678 }
679
680 // Gatherers is a slice of Gatherer instances that implements the Gatherer
681 // interface itself. Its Gather method calls Gather on all Gatherers in the
682 // slice in order and returns the merged results. Errors returned from the
683 // Gather calls are all returned in a flattened MultiError. Duplicate and
684 // inconsistent Metrics are skipped (first occurrence in slice order wins) and
685 // reported in the returned error.
686 //
687 // Gatherers can be used to merge the Gather results from multiple
688 // Registries. It also provides a way to directly inject existing MetricFamily
689 // protobufs into the gathering by creating a custom Gatherer with a Gather
690 // method that simply returns the existing MetricFamily protobufs. Note that no
691 // registration is involved (in contrast to Collector registration), so
692 // obviously registration-time checks cannot happen. Any inconsistencies between
693 // the gathered MetricFamilies are reported as errors by the Gather method, and
694 // inconsistent Metrics are dropped. Invalid parts of the MetricFamilies
695 // (e.g. syntactically invalid metric or label names) will go undetected.
696 type Gatherers []Gatherer
697
698 // Gather implements Gatherer.
699 func (gs Gatherers) Gather() ([]*dto.MetricFamily, error) {
700         var (
701                 metricFamiliesByName = map[string]*dto.MetricFamily{}
702                 metricHashes         = map[uint64]struct{}{}
703                 errs                 MultiError // The collected errors to return in the end.
704         )
705
706         for i, g := range gs {
707                 mfs, err := g.Gather()
708                 if err != nil {
709                         if multiErr, ok := err.(MultiError); ok {
710                                 for _, err := range multiErr {
711                                         errs = append(errs, fmt.Errorf("[from Gatherer #%d] %s", i+1, err))
712                                 }
713                         } else {
714                                 errs = append(errs, fmt.Errorf("[from Gatherer #%d] %s", i+1, err))
715                         }
716                 }
717                 for _, mf := range mfs {
718                         existingMF, exists := metricFamiliesByName[mf.GetName()]
719                         if exists {
720                                 if existingMF.GetHelp() != mf.GetHelp() {
721                                         errs = append(errs, fmt.Errorf(
722                                                 "gathered metric family %s has help %q but should have %q",
723                                                 mf.GetName(), mf.GetHelp(), existingMF.GetHelp(),
724                                         ))
725                                         continue
726                                 }
727                                 if existingMF.GetType() != mf.GetType() {
728                                         errs = append(errs, fmt.Errorf(
729                                                 "gathered metric family %s has type %s but should have %s",
730                                                 mf.GetName(), mf.GetType(), existingMF.GetType(),
731                                         ))
732                                         continue
733                                 }
734                         } else {
735                                 existingMF = &dto.MetricFamily{}
736                                 existingMF.Name = mf.Name
737                                 existingMF.Help = mf.Help
738                                 existingMF.Type = mf.Type
739                                 if err := checkSuffixCollisions(existingMF, metricFamiliesByName); err != nil {
740                                         errs = append(errs, err)
741                                         continue
742                                 }
743                                 metricFamiliesByName[mf.GetName()] = existingMF
744                         }
745                         for _, m := range mf.Metric {
746                                 if err := checkMetricConsistency(existingMF, m, metricHashes); err != nil {
747                                         errs = append(errs, err)
748                                         continue
749                                 }
750                                 existingMF.Metric = append(existingMF.Metric, m)
751                         }
752                 }
753         }
754         return internal.NormalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap()
755 }
756
757 // checkSuffixCollisions checks for collisions with the “magic” suffixes the
758 // Prometheus text format and the internal metric representation of the
759 // Prometheus server add while flattening Summaries and Histograms.
760 func checkSuffixCollisions(mf *dto.MetricFamily, mfs map[string]*dto.MetricFamily) error {
761         var (
762                 newName              = mf.GetName()
763                 newType              = mf.GetType()
764                 newNameWithoutSuffix = ""
765         )
766         switch {
767         case strings.HasSuffix(newName, "_count"):
768                 newNameWithoutSuffix = newName[:len(newName)-6]
769         case strings.HasSuffix(newName, "_sum"):
770                 newNameWithoutSuffix = newName[:len(newName)-4]
771         case strings.HasSuffix(newName, "_bucket"):
772                 newNameWithoutSuffix = newName[:len(newName)-7]
773         }
774         if newNameWithoutSuffix != "" {
775                 if existingMF, ok := mfs[newNameWithoutSuffix]; ok {
776                         switch existingMF.GetType() {
777                         case dto.MetricType_SUMMARY:
778                                 if !strings.HasSuffix(newName, "_bucket") {
779                                         return fmt.Errorf(
780                                                 "collected metric named %q collides with previously collected summary named %q",
781                                                 newName, newNameWithoutSuffix,
782                                         )
783                                 }
784                         case dto.MetricType_HISTOGRAM:
785                                 return fmt.Errorf(
786                                         "collected metric named %q collides with previously collected histogram named %q",
787                                         newName, newNameWithoutSuffix,
788                                 )
789                         }
790                 }
791         }
792         if newType == dto.MetricType_SUMMARY || newType == dto.MetricType_HISTOGRAM {
793                 if _, ok := mfs[newName+"_count"]; ok {
794                         return fmt.Errorf(
795                                 "collected histogram or summary named %q collides with previously collected metric named %q",
796                                 newName, newName+"_count",
797                         )
798                 }
799                 if _, ok := mfs[newName+"_sum"]; ok {
800                         return fmt.Errorf(
801                                 "collected histogram or summary named %q collides with previously collected metric named %q",
802                                 newName, newName+"_sum",
803                         )
804                 }
805         }
806         if newType == dto.MetricType_HISTOGRAM {
807                 if _, ok := mfs[newName+"_bucket"]; ok {
808                         return fmt.Errorf(
809                                 "collected histogram named %q collides with previously collected metric named %q",
810                                 newName, newName+"_bucket",
811                         )
812                 }
813         }
814         return nil
815 }
816
817 // checkMetricConsistency checks if the provided Metric is consistent with the
818 // provided MetricFamily. It also hashes the Metric labels and the MetricFamily
819 // name. If the resulting hash is already in the provided metricHashes, an error
820 // is returned. If not, it is added to metricHashes.
821 func checkMetricConsistency(
822         metricFamily *dto.MetricFamily,
823         dtoMetric *dto.Metric,
824         metricHashes map[uint64]struct{},
825 ) error {
826         name := metricFamily.GetName()
827
828         // Type consistency with metric family.
829         if metricFamily.GetType() == dto.MetricType_GAUGE && dtoMetric.Gauge == nil ||
830                 metricFamily.GetType() == dto.MetricType_COUNTER && dtoMetric.Counter == nil ||
831                 metricFamily.GetType() == dto.MetricType_SUMMARY && dtoMetric.Summary == nil ||
832                 metricFamily.GetType() == dto.MetricType_HISTOGRAM && dtoMetric.Histogram == nil ||
833                 metricFamily.GetType() == dto.MetricType_UNTYPED && dtoMetric.Untyped == nil {
834                 return fmt.Errorf(
835                         "collected metric %q { %s} is not a %s",
836                         name, dtoMetric, metricFamily.GetType(),
837                 )
838         }
839
840         previousLabelName := ""
841         for _, labelPair := range dtoMetric.GetLabel() {
842                 labelName := labelPair.GetName()
843                 if labelName == previousLabelName {
844                         return fmt.Errorf(
845                                 "collected metric %q { %s} has two or more labels with the same name: %s",
846                                 name, dtoMetric, labelName,
847                         )
848                 }
849                 if !checkLabelName(labelName) {
850                         return fmt.Errorf(
851                                 "collected metric %q { %s} has a label with an invalid name: %s",
852                                 name, dtoMetric, labelName,
853                         )
854                 }
855                 if dtoMetric.Summary != nil && labelName == quantileLabel {
856                         return fmt.Errorf(
857                                 "collected metric %q { %s} must not have an explicit %q label",
858                                 name, dtoMetric, quantileLabel,
859                         )
860                 }
861                 if !utf8.ValidString(labelPair.GetValue()) {
862                         return fmt.Errorf(
863                                 "collected metric %q { %s} has a label named %q whose value is not utf8: %#v",
864                                 name, dtoMetric, labelName, labelPair.GetValue())
865                 }
866                 previousLabelName = labelName
867         }
868
869         // Is the metric unique (i.e. no other metric with the same name and the same labels)?
870         h := hashNew()
871         h = hashAdd(h, name)
872         h = hashAddByte(h, separatorByte)
873         // Make sure label pairs are sorted. We depend on it for the consistency
874         // check.
875         if !sort.IsSorted(labelPairSorter(dtoMetric.Label)) {
876                 // We cannot sort dtoMetric.Label in place as it is immutable by contract.
877                 copiedLabels := make([]*dto.LabelPair, len(dtoMetric.Label))
878                 copy(copiedLabels, dtoMetric.Label)
879                 sort.Sort(labelPairSorter(copiedLabels))
880                 dtoMetric.Label = copiedLabels
881         }
882         for _, lp := range dtoMetric.Label {
883                 h = hashAdd(h, lp.GetName())
884                 h = hashAddByte(h, separatorByte)
885                 h = hashAdd(h, lp.GetValue())
886                 h = hashAddByte(h, separatorByte)
887         }
888         if _, exists := metricHashes[h]; exists {
889                 return fmt.Errorf(
890                         "collected metric %q { %s} was collected before with the same name and label values",
891                         name, dtoMetric,
892                 )
893         }
894         metricHashes[h] = struct{}{}
895         return nil
896 }
897
898 func checkDescConsistency(
899         metricFamily *dto.MetricFamily,
900         dtoMetric *dto.Metric,
901         desc *Desc,
902 ) error {
903         // Desc help consistency with metric family help.
904         if metricFamily.GetHelp() != desc.help {
905                 return fmt.Errorf(
906                         "collected metric %s %s has help %q but should have %q",
907                         metricFamily.GetName(), dtoMetric, metricFamily.GetHelp(), desc.help,
908                 )
909         }
910
911         // Is the desc consistent with the content of the metric?
912         lpsFromDesc := make([]*dto.LabelPair, len(desc.constLabelPairs), len(dtoMetric.Label))
913         copy(lpsFromDesc, desc.constLabelPairs)
914         for _, l := range desc.variableLabels {
915                 lpsFromDesc = append(lpsFromDesc, &dto.LabelPair{
916                         Name: proto.String(l),
917                 })
918         }
919         if len(lpsFromDesc) != len(dtoMetric.Label) {
920                 return fmt.Errorf(
921                         "labels in collected metric %s %s are inconsistent with descriptor %s",
922                         metricFamily.GetName(), dtoMetric, desc,
923                 )
924         }
925         sort.Sort(labelPairSorter(lpsFromDesc))
926         for i, lpFromDesc := range lpsFromDesc {
927                 lpFromMetric := dtoMetric.Label[i]
928                 if lpFromDesc.GetName() != lpFromMetric.GetName() ||
929                         lpFromDesc.Value != nil && lpFromDesc.GetValue() != lpFromMetric.GetValue() {
930                         return fmt.Errorf(
931                                 "labels in collected metric %s %s are inconsistent with descriptor %s",
932                                 metricFamily.GetName(), dtoMetric, desc,
933                         )
934                 }
935         }
936         return nil
937 }