registry.go 31 KB

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