registry.go 29 KB

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