Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

643 rader
20 KiB

  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. "fmt"
  16. "sync"
  17. "github.com/prometheus/common/model"
  18. )
  19. // MetricVec is a Collector to bundle metrics of the same name that differ in
  20. // their label values. MetricVec is not used directly but as a building block
  21. // for implementations of vectors of a given metric type, like GaugeVec,
  22. // CounterVec, SummaryVec, and HistogramVec. It is exported so that it can be
  23. // used for custom Metric implementations.
  24. //
  25. // To create a FooVec for custom Metric Foo, embed a pointer to MetricVec in
  26. // FooVec and initialize it with NewMetricVec. Implement wrappers for
  27. // GetMetricWithLabelValues and GetMetricWith that return (Foo, error) rather
  28. // than (Metric, error). Similarly, create a wrapper for CurryWith that returns
  29. // (*FooVec, error) rather than (*MetricVec, error). It is recommended to also
  30. // add the convenience methods WithLabelValues, With, and MustCurryWith, which
  31. // panic instead of returning errors. See also the MetricVec example.
  32. type MetricVec struct {
  33. *metricMap
  34. curry []curriedLabelValue
  35. // hashAdd and hashAddByte can be replaced for testing collision handling.
  36. hashAdd func(h uint64, s string) uint64
  37. hashAddByte func(h uint64, b byte) uint64
  38. }
  39. // NewMetricVec returns an initialized metricVec.
  40. func NewMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *MetricVec {
  41. return &MetricVec{
  42. metricMap: &metricMap{
  43. metrics: map[uint64][]metricWithLabelValues{},
  44. desc: desc,
  45. newMetric: newMetric,
  46. },
  47. hashAdd: hashAdd,
  48. hashAddByte: hashAddByte,
  49. }
  50. }
  51. // DeleteLabelValues removes the metric where the variable labels are the same
  52. // as those passed in as labels (same order as the VariableLabels in Desc). It
  53. // returns true if a metric was deleted.
  54. //
  55. // It is not an error if the number of label values is not the same as the
  56. // number of VariableLabels in Desc. However, such inconsistent label count can
  57. // never match an actual metric, so the method will always return false in that
  58. // case.
  59. //
  60. // Note that for more than one label value, this method is prone to mistakes
  61. // caused by an incorrect order of arguments. Consider Delete(Labels) as an
  62. // alternative to avoid that type of mistake. For higher label numbers, the
  63. // latter has a much more readable (albeit more verbose) syntax, but it comes
  64. // with a performance overhead (for creating and processing the Labels map).
  65. // See also the CounterVec example.
  66. func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
  67. h, err := m.hashLabelValues(lvs)
  68. if err != nil {
  69. return false
  70. }
  71. return m.metricMap.deleteByHashWithLabelValues(h, lvs, m.curry)
  72. }
  73. // Delete deletes the metric where the variable labels are the same as those
  74. // passed in as labels. It returns true if a metric was deleted.
  75. //
  76. // It is not an error if the number and names of the Labels are inconsistent
  77. // with those of the VariableLabels in Desc. However, such inconsistent Labels
  78. // can never match an actual metric, so the method will always return false in
  79. // that case.
  80. //
  81. // This method is used for the same purpose as DeleteLabelValues(...string). See
  82. // there for pros and cons of the two methods.
  83. func (m *MetricVec) Delete(labels Labels) bool {
  84. h, err := m.hashLabels(labels)
  85. if err != nil {
  86. return false
  87. }
  88. return m.metricMap.deleteByHashWithLabels(h, labels, m.curry)
  89. }
  90. // DeletePartialMatch deletes all metrics where the variable labels contain all of those
  91. // passed in as labels. The order of the labels does not matter.
  92. // It returns the number of metrics deleted.
  93. //
  94. // Note that curried labels will never be matched if deleting from the curried vector.
  95. // To match curried labels with DeletePartialMatch, it must be called on the base vector.
  96. func (m *MetricVec) DeletePartialMatch(labels Labels) int {
  97. return m.metricMap.deleteByLabels(labels, m.curry)
  98. }
  99. // Without explicit forwarding of Describe, Collect, Reset, those methods won't
  100. // show up in GoDoc.
  101. // Describe implements Collector.
  102. func (m *MetricVec) Describe(ch chan<- *Desc) { m.metricMap.Describe(ch) }
  103. // Collect implements Collector.
  104. func (m *MetricVec) Collect(ch chan<- Metric) { m.metricMap.Collect(ch) }
  105. // Reset deletes all metrics in this vector.
  106. func (m *MetricVec) Reset() { m.metricMap.Reset() }
  107. // CurryWith returns a vector curried with the provided labels, i.e. the
  108. // returned vector has those labels pre-set for all labeled operations performed
  109. // on it. The cardinality of the curried vector is reduced accordingly. The
  110. // order of the remaining labels stays the same (just with the curried labels
  111. // taken out of the sequence – which is relevant for the
  112. // (GetMetric)WithLabelValues methods). It is possible to curry a curried
  113. // vector, but only with labels not yet used for currying before.
  114. //
  115. // The metrics contained in the MetricVec are shared between the curried and
  116. // uncurried vectors. They are just accessed differently. Curried and uncurried
  117. // vectors behave identically in terms of collection. Only one must be
  118. // registered with a given registry (usually the uncurried version). The Reset
  119. // method deletes all metrics, even if called on a curried vector.
  120. //
  121. // Note that CurryWith is usually not called directly but through a wrapper
  122. // around MetricVec, implementing a vector for a specific Metric
  123. // implementation, for example GaugeVec.
  124. func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) {
  125. var (
  126. newCurry []curriedLabelValue
  127. oldCurry = m.curry
  128. iCurry int
  129. )
  130. for i, label := range m.desc.variableLabels {
  131. val, ok := labels[label]
  132. if iCurry < len(oldCurry) && oldCurry[iCurry].index == i {
  133. if ok {
  134. return nil, fmt.Errorf("label name %q is already curried", label)
  135. }
  136. newCurry = append(newCurry, oldCurry[iCurry])
  137. iCurry++
  138. } else {
  139. if !ok {
  140. continue // Label stays uncurried.
  141. }
  142. newCurry = append(newCurry, curriedLabelValue{i, val})
  143. }
  144. }
  145. if l := len(oldCurry) + len(labels) - len(newCurry); l > 0 {
  146. return nil, fmt.Errorf("%d unknown label(s) found during currying", l)
  147. }
  148. return &MetricVec{
  149. metricMap: m.metricMap,
  150. curry: newCurry,
  151. hashAdd: m.hashAdd,
  152. hashAddByte: m.hashAddByte,
  153. }, nil
  154. }
  155. // GetMetricWithLabelValues returns the Metric for the given slice of label
  156. // values (same order as the variable labels in Desc). If that combination of
  157. // label values is accessed for the first time, a new Metric is created (by
  158. // calling the newMetric function provided during construction of the
  159. // MetricVec).
  160. //
  161. // It is possible to call this method without using the returned Metric to only
  162. // create the new Metric but leave it in its initial state.
  163. //
  164. // Keeping the Metric for later use is possible (and should be considered if
  165. // performance is critical), but keep in mind that Reset, DeleteLabelValues and
  166. // Delete can be used to delete the Metric from the MetricVec. In that case, the
  167. // Metric will still exist, but it will not be exported anymore, even if a
  168. // Metric with the same label values is created later.
  169. //
  170. // An error is returned if the number of label values is not the same as the
  171. // number of variable labels in Desc (minus any curried labels).
  172. //
  173. // Note that for more than one label value, this method is prone to mistakes
  174. // caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
  175. // an alternative to avoid that type of mistake. For higher label numbers, the
  176. // latter has a much more readable (albeit more verbose) syntax, but it comes
  177. // with a performance overhead (for creating and processing the Labels map).
  178. //
  179. // Note that GetMetricWithLabelValues is usually not called directly but through
  180. // a wrapper around MetricVec, implementing a vector for a specific Metric
  181. // implementation, for example GaugeVec.
  182. func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) {
  183. h, err := m.hashLabelValues(lvs)
  184. if err != nil {
  185. return nil, err
  186. }
  187. return m.metricMap.getOrCreateMetricWithLabelValues(h, lvs, m.curry), nil
  188. }
  189. // GetMetricWith returns the Metric for the given Labels map (the label names
  190. // must match those of the variable labels in Desc). If that label map is
  191. // accessed for the first time, a new Metric is created. Implications of
  192. // creating a Metric without using it and keeping the Metric for later use
  193. // are the same as for GetMetricWithLabelValues.
  194. //
  195. // An error is returned if the number and names of the Labels are inconsistent
  196. // with those of the variable labels in Desc (minus any curried labels).
  197. //
  198. // This method is used for the same purpose as
  199. // GetMetricWithLabelValues(...string). See there for pros and cons of the two
  200. // methods.
  201. //
  202. // Note that GetMetricWith is usually not called directly but through a wrapper
  203. // around MetricVec, implementing a vector for a specific Metric implementation,
  204. // for example GaugeVec.
  205. func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
  206. h, err := m.hashLabels(labels)
  207. if err != nil {
  208. return nil, err
  209. }
  210. return m.metricMap.getOrCreateMetricWithLabels(h, labels, m.curry), nil
  211. }
  212. func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
  213. if err := validateLabelValues(vals, len(m.desc.variableLabels)-len(m.curry)); err != nil {
  214. return 0, err
  215. }
  216. var (
  217. h = hashNew()
  218. curry = m.curry
  219. iVals, iCurry int
  220. )
  221. for i := 0; i < len(m.desc.variableLabels); i++ {
  222. if iCurry < len(curry) && curry[iCurry].index == i {
  223. h = m.hashAdd(h, curry[iCurry].value)
  224. iCurry++
  225. } else {
  226. h = m.hashAdd(h, vals[iVals])
  227. iVals++
  228. }
  229. h = m.hashAddByte(h, model.SeparatorByte)
  230. }
  231. return h, nil
  232. }
  233. func (m *MetricVec) hashLabels(labels Labels) (uint64, error) {
  234. if err := validateValuesInLabels(labels, len(m.desc.variableLabels)-len(m.curry)); err != nil {
  235. return 0, err
  236. }
  237. var (
  238. h = hashNew()
  239. curry = m.curry
  240. iCurry int
  241. )
  242. for i, label := range m.desc.variableLabels {
  243. val, ok := labels[label]
  244. if iCurry < len(curry) && curry[iCurry].index == i {
  245. if ok {
  246. return 0, fmt.Errorf("label name %q is already curried", label)
  247. }
  248. h = m.hashAdd(h, curry[iCurry].value)
  249. iCurry++
  250. } else {
  251. if !ok {
  252. return 0, fmt.Errorf("label name %q missing in label map", label)
  253. }
  254. h = m.hashAdd(h, val)
  255. }
  256. h = m.hashAddByte(h, model.SeparatorByte)
  257. }
  258. return h, nil
  259. }
  260. // metricWithLabelValues provides the metric and its label values for
  261. // disambiguation on hash collision.
  262. type metricWithLabelValues struct {
  263. values []string
  264. metric Metric
  265. }
  266. // curriedLabelValue sets the curried value for a label at the given index.
  267. type curriedLabelValue struct {
  268. index int
  269. value string
  270. }
  271. // metricMap is a helper for metricVec and shared between differently curried
  272. // metricVecs.
  273. type metricMap struct {
  274. mtx sync.RWMutex // Protects metrics.
  275. metrics map[uint64][]metricWithLabelValues
  276. desc *Desc
  277. newMetric func(labelValues ...string) Metric
  278. }
  279. // Describe implements Collector. It will send exactly one Desc to the provided
  280. // channel.
  281. func (m *metricMap) Describe(ch chan<- *Desc) {
  282. ch <- m.desc
  283. }
  284. // Collect implements Collector.
  285. func (m *metricMap) Collect(ch chan<- Metric) {
  286. m.mtx.RLock()
  287. defer m.mtx.RUnlock()
  288. for _, metrics := range m.metrics {
  289. for _, metric := range metrics {
  290. ch <- metric.metric
  291. }
  292. }
  293. }
  294. // Reset deletes all metrics in this vector.
  295. func (m *metricMap) Reset() {
  296. m.mtx.Lock()
  297. defer m.mtx.Unlock()
  298. for h := range m.metrics {
  299. delete(m.metrics, h)
  300. }
  301. }
  302. // deleteByHashWithLabelValues removes the metric from the hash bucket h. If
  303. // there are multiple matches in the bucket, use lvs to select a metric and
  304. // remove only that metric.
  305. func (m *metricMap) deleteByHashWithLabelValues(
  306. h uint64, lvs []string, curry []curriedLabelValue,
  307. ) bool {
  308. m.mtx.Lock()
  309. defer m.mtx.Unlock()
  310. metrics, ok := m.metrics[h]
  311. if !ok {
  312. return false
  313. }
  314. i := findMetricWithLabelValues(metrics, lvs, curry)
  315. if i >= len(metrics) {
  316. return false
  317. }
  318. if len(metrics) > 1 {
  319. old := metrics
  320. m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
  321. old[len(old)-1] = metricWithLabelValues{}
  322. } else {
  323. delete(m.metrics, h)
  324. }
  325. return true
  326. }
  327. // deleteByHashWithLabels removes the metric from the hash bucket h. If there
  328. // are multiple matches in the bucket, use lvs to select a metric and remove
  329. // only that metric.
  330. func (m *metricMap) deleteByHashWithLabels(
  331. h uint64, labels Labels, curry []curriedLabelValue,
  332. ) bool {
  333. m.mtx.Lock()
  334. defer m.mtx.Unlock()
  335. metrics, ok := m.metrics[h]
  336. if !ok {
  337. return false
  338. }
  339. i := findMetricWithLabels(m.desc, metrics, labels, curry)
  340. if i >= len(metrics) {
  341. return false
  342. }
  343. if len(metrics) > 1 {
  344. old := metrics
  345. m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
  346. old[len(old)-1] = metricWithLabelValues{}
  347. } else {
  348. delete(m.metrics, h)
  349. }
  350. return true
  351. }
  352. // deleteByLabels deletes a metric if the given labels are present in the metric.
  353. func (m *metricMap) deleteByLabels(labels Labels, curry []curriedLabelValue) int {
  354. m.mtx.Lock()
  355. defer m.mtx.Unlock()
  356. var numDeleted int
  357. for h, metrics := range m.metrics {
  358. i := findMetricWithPartialLabels(m.desc, metrics, labels, curry)
  359. if i >= len(metrics) {
  360. // Didn't find matching labels in this metric slice.
  361. continue
  362. }
  363. delete(m.metrics, h)
  364. numDeleted++
  365. }
  366. return numDeleted
  367. }
  368. // findMetricWithPartialLabel returns the index of the matching metric or
  369. // len(metrics) if not found.
  370. func findMetricWithPartialLabels(
  371. desc *Desc, metrics []metricWithLabelValues, labels Labels, curry []curriedLabelValue,
  372. ) int {
  373. for i, metric := range metrics {
  374. if matchPartialLabels(desc, metric.values, labels, curry) {
  375. return i
  376. }
  377. }
  378. return len(metrics)
  379. }
  380. // indexOf searches the given slice of strings for the target string and returns
  381. // the index or len(items) as well as a boolean whether the search succeeded.
  382. func indexOf(target string, items []string) (int, bool) {
  383. for i, l := range items {
  384. if l == target {
  385. return i, true
  386. }
  387. }
  388. return len(items), false
  389. }
  390. // valueMatchesVariableOrCurriedValue determines if a value was previously curried,
  391. // and returns whether it matches either the "base" value or the curried value accordingly.
  392. // It also indicates whether the match is against a curried or uncurried value.
  393. func valueMatchesVariableOrCurriedValue(targetValue string, index int, values []string, curry []curriedLabelValue) (bool, bool) {
  394. for _, curriedValue := range curry {
  395. if curriedValue.index == index {
  396. // This label was curried. See if the curried value matches our target.
  397. return curriedValue.value == targetValue, true
  398. }
  399. }
  400. // This label was not curried. See if the current value matches our target label.
  401. return values[index] == targetValue, false
  402. }
  403. // matchPartialLabels searches the current metric and returns whether all of the target label:value pairs are present.
  404. func matchPartialLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool {
  405. for l, v := range labels {
  406. // Check if the target label exists in our metrics and get the index.
  407. varLabelIndex, validLabel := indexOf(l, desc.variableLabels)
  408. if validLabel {
  409. // Check the value of that label against the target value.
  410. // We don't consider curried values in partial matches.
  411. matches, curried := valueMatchesVariableOrCurriedValue(v, varLabelIndex, values, curry)
  412. if matches && !curried {
  413. continue
  414. }
  415. }
  416. return false
  417. }
  418. return true
  419. }
  420. // getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
  421. // or creates it and returns the new one.
  422. //
  423. // This function holds the mutex.
  424. func (m *metricMap) getOrCreateMetricWithLabelValues(
  425. hash uint64, lvs []string, curry []curriedLabelValue,
  426. ) Metric {
  427. m.mtx.RLock()
  428. metric, ok := m.getMetricWithHashAndLabelValues(hash, lvs, curry)
  429. m.mtx.RUnlock()
  430. if ok {
  431. return metric
  432. }
  433. m.mtx.Lock()
  434. defer m.mtx.Unlock()
  435. metric, ok = m.getMetricWithHashAndLabelValues(hash, lvs, curry)
  436. if !ok {
  437. inlinedLVs := inlineLabelValues(lvs, curry)
  438. metric = m.newMetric(inlinedLVs...)
  439. m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: inlinedLVs, metric: metric})
  440. }
  441. return metric
  442. }
  443. // getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
  444. // or creates it and returns the new one.
  445. //
  446. // This function holds the mutex.
  447. func (m *metricMap) getOrCreateMetricWithLabels(
  448. hash uint64, labels Labels, curry []curriedLabelValue,
  449. ) Metric {
  450. m.mtx.RLock()
  451. metric, ok := m.getMetricWithHashAndLabels(hash, labels, curry)
  452. m.mtx.RUnlock()
  453. if ok {
  454. return metric
  455. }
  456. m.mtx.Lock()
  457. defer m.mtx.Unlock()
  458. metric, ok = m.getMetricWithHashAndLabels(hash, labels, curry)
  459. if !ok {
  460. lvs := extractLabelValues(m.desc, labels, curry)
  461. metric = m.newMetric(lvs...)
  462. m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: lvs, metric: metric})
  463. }
  464. return metric
  465. }
  466. // getMetricWithHashAndLabelValues gets a metric while handling possible
  467. // collisions in the hash space. Must be called while holding the read mutex.
  468. func (m *metricMap) getMetricWithHashAndLabelValues(
  469. h uint64, lvs []string, curry []curriedLabelValue,
  470. ) (Metric, bool) {
  471. metrics, ok := m.metrics[h]
  472. if ok {
  473. if i := findMetricWithLabelValues(metrics, lvs, curry); i < len(metrics) {
  474. return metrics[i].metric, true
  475. }
  476. }
  477. return nil, false
  478. }
  479. // getMetricWithHashAndLabels gets a metric while handling possible collisions in
  480. // the hash space. Must be called while holding read mutex.
  481. func (m *metricMap) getMetricWithHashAndLabels(
  482. h uint64, labels Labels, curry []curriedLabelValue,
  483. ) (Metric, bool) {
  484. metrics, ok := m.metrics[h]
  485. if ok {
  486. if i := findMetricWithLabels(m.desc, metrics, labels, curry); i < len(metrics) {
  487. return metrics[i].metric, true
  488. }
  489. }
  490. return nil, false
  491. }
  492. // findMetricWithLabelValues returns the index of the matching metric or
  493. // len(metrics) if not found.
  494. func findMetricWithLabelValues(
  495. metrics []metricWithLabelValues, lvs []string, curry []curriedLabelValue,
  496. ) int {
  497. for i, metric := range metrics {
  498. if matchLabelValues(metric.values, lvs, curry) {
  499. return i
  500. }
  501. }
  502. return len(metrics)
  503. }
  504. // findMetricWithLabels returns the index of the matching metric or len(metrics)
  505. // if not found.
  506. func findMetricWithLabels(
  507. desc *Desc, metrics []metricWithLabelValues, labels Labels, curry []curriedLabelValue,
  508. ) int {
  509. for i, metric := range metrics {
  510. if matchLabels(desc, metric.values, labels, curry) {
  511. return i
  512. }
  513. }
  514. return len(metrics)
  515. }
  516. func matchLabelValues(values, lvs []string, curry []curriedLabelValue) bool {
  517. if len(values) != len(lvs)+len(curry) {
  518. return false
  519. }
  520. var iLVs, iCurry int
  521. for i, v := range values {
  522. if iCurry < len(curry) && curry[iCurry].index == i {
  523. if v != curry[iCurry].value {
  524. return false
  525. }
  526. iCurry++
  527. continue
  528. }
  529. if v != lvs[iLVs] {
  530. return false
  531. }
  532. iLVs++
  533. }
  534. return true
  535. }
  536. func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool {
  537. if len(values) != len(labels)+len(curry) {
  538. return false
  539. }
  540. iCurry := 0
  541. for i, k := range desc.variableLabels {
  542. if iCurry < len(curry) && curry[iCurry].index == i {
  543. if values[i] != curry[iCurry].value {
  544. return false
  545. }
  546. iCurry++
  547. continue
  548. }
  549. if values[i] != labels[k] {
  550. return false
  551. }
  552. }
  553. return true
  554. }
  555. func extractLabelValues(desc *Desc, labels Labels, curry []curriedLabelValue) []string {
  556. labelValues := make([]string, len(labels)+len(curry))
  557. iCurry := 0
  558. for i, k := range desc.variableLabels {
  559. if iCurry < len(curry) && curry[iCurry].index == i {
  560. labelValues[i] = curry[iCurry].value
  561. iCurry++
  562. continue
  563. }
  564. labelValues[i] = labels[k]
  565. }
  566. return labelValues
  567. }
  568. func inlineLabelValues(lvs []string, curry []curriedLabelValue) []string {
  569. labelValues := make([]string, len(lvs)+len(curry))
  570. var iCurry, iLVs int
  571. for i := range labelValues {
  572. if iCurry < len(curry) && curry[iCurry].index == i {
  573. labelValues[i] = curry[iCurry].value
  574. iCurry++
  575. continue
  576. }
  577. labelValues[i] = lvs[iLVs]
  578. iLVs++
  579. }
  580. return labelValues
  581. }