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.

557 rader
17 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. // Without explicit forwarding of Describe, Collect, Reset, those methods won't
  91. // show up in GoDoc.
  92. // Describe implements Collector.
  93. func (m *MetricVec) Describe(ch chan<- *Desc) { m.metricMap.Describe(ch) }
  94. // Collect implements Collector.
  95. func (m *MetricVec) Collect(ch chan<- Metric) { m.metricMap.Collect(ch) }
  96. // Reset deletes all metrics in this vector.
  97. func (m *MetricVec) Reset() { m.metricMap.Reset() }
  98. // CurryWith returns a vector curried with the provided labels, i.e. the
  99. // returned vector has those labels pre-set for all labeled operations performed
  100. // on it. The cardinality of the curried vector is reduced accordingly. The
  101. // order of the remaining labels stays the same (just with the curried labels
  102. // taken out of the sequence – which is relevant for the
  103. // (GetMetric)WithLabelValues methods). It is possible to curry a curried
  104. // vector, but only with labels not yet used for currying before.
  105. //
  106. // The metrics contained in the MetricVec are shared between the curried and
  107. // uncurried vectors. They are just accessed differently. Curried and uncurried
  108. // vectors behave identically in terms of collection. Only one must be
  109. // registered with a given registry (usually the uncurried version). The Reset
  110. // method deletes all metrics, even if called on a curried vector.
  111. //
  112. // Note that CurryWith is usually not called directly but through a wrapper
  113. // around MetricVec, implementing a vector for a specific Metric
  114. // implementation, for example GaugeVec.
  115. func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) {
  116. var (
  117. newCurry []curriedLabelValue
  118. oldCurry = m.curry
  119. iCurry int
  120. )
  121. for i, label := range m.desc.variableLabels {
  122. val, ok := labels[label]
  123. if iCurry < len(oldCurry) && oldCurry[iCurry].index == i {
  124. if ok {
  125. return nil, fmt.Errorf("label name %q is already curried", label)
  126. }
  127. newCurry = append(newCurry, oldCurry[iCurry])
  128. iCurry++
  129. } else {
  130. if !ok {
  131. continue // Label stays uncurried.
  132. }
  133. newCurry = append(newCurry, curriedLabelValue{i, val})
  134. }
  135. }
  136. if l := len(oldCurry) + len(labels) - len(newCurry); l > 0 {
  137. return nil, fmt.Errorf("%d unknown label(s) found during currying", l)
  138. }
  139. return &MetricVec{
  140. metricMap: m.metricMap,
  141. curry: newCurry,
  142. hashAdd: m.hashAdd,
  143. hashAddByte: m.hashAddByte,
  144. }, nil
  145. }
  146. // GetMetricWithLabelValues returns the Metric for the given slice of label
  147. // values (same order as the variable labels in Desc). If that combination of
  148. // label values is accessed for the first time, a new Metric is created (by
  149. // calling the newMetric function provided during construction of the
  150. // MetricVec).
  151. //
  152. // It is possible to call this method without using the returned Metric to only
  153. // create the new Metric but leave it in its initial state.
  154. //
  155. // Keeping the Metric for later use is possible (and should be considered if
  156. // performance is critical), but keep in mind that Reset, DeleteLabelValues and
  157. // Delete can be used to delete the Metric from the MetricVec. In that case, the
  158. // Metric will still exist, but it will not be exported anymore, even if a
  159. // Metric with the same label values is created later.
  160. //
  161. // An error is returned if the number of label values is not the same as the
  162. // number of variable labels in Desc (minus any curried labels).
  163. //
  164. // Note that for more than one label value, this method is prone to mistakes
  165. // caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
  166. // an alternative to avoid that type of mistake. For higher label numbers, the
  167. // latter has a much more readable (albeit more verbose) syntax, but it comes
  168. // with a performance overhead (for creating and processing the Labels map).
  169. //
  170. // Note that GetMetricWithLabelValues is usually not called directly but through
  171. // a wrapper around MetricVec, implementing a vector for a specific Metric
  172. // implementation, for example GaugeVec.
  173. func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) {
  174. h, err := m.hashLabelValues(lvs)
  175. if err != nil {
  176. return nil, err
  177. }
  178. return m.metricMap.getOrCreateMetricWithLabelValues(h, lvs, m.curry), nil
  179. }
  180. // GetMetricWith returns the Metric for the given Labels map (the label names
  181. // must match those of the variable labels in Desc). If that label map is
  182. // accessed for the first time, a new Metric is created. Implications of
  183. // creating a Metric without using it and keeping the Metric for later use
  184. // are the same as for GetMetricWithLabelValues.
  185. //
  186. // An error is returned if the number and names of the Labels are inconsistent
  187. // with those of the variable labels in Desc (minus any curried labels).
  188. //
  189. // This method is used for the same purpose as
  190. // GetMetricWithLabelValues(...string). See there for pros and cons of the two
  191. // methods.
  192. //
  193. // Note that GetMetricWith is usually not called directly but through a wrapper
  194. // around MetricVec, implementing a vector for a specific Metric implementation,
  195. // for example GaugeVec.
  196. func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
  197. h, err := m.hashLabels(labels)
  198. if err != nil {
  199. return nil, err
  200. }
  201. return m.metricMap.getOrCreateMetricWithLabels(h, labels, m.curry), nil
  202. }
  203. func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
  204. if err := validateLabelValues(vals, len(m.desc.variableLabels)-len(m.curry)); err != nil {
  205. return 0, err
  206. }
  207. var (
  208. h = hashNew()
  209. curry = m.curry
  210. iVals, iCurry int
  211. )
  212. for i := 0; i < len(m.desc.variableLabels); i++ {
  213. if iCurry < len(curry) && curry[iCurry].index == i {
  214. h = m.hashAdd(h, curry[iCurry].value)
  215. iCurry++
  216. } else {
  217. h = m.hashAdd(h, vals[iVals])
  218. iVals++
  219. }
  220. h = m.hashAddByte(h, model.SeparatorByte)
  221. }
  222. return h, nil
  223. }
  224. func (m *MetricVec) hashLabels(labels Labels) (uint64, error) {
  225. if err := validateValuesInLabels(labels, len(m.desc.variableLabels)-len(m.curry)); err != nil {
  226. return 0, err
  227. }
  228. var (
  229. h = hashNew()
  230. curry = m.curry
  231. iCurry int
  232. )
  233. for i, label := range m.desc.variableLabels {
  234. val, ok := labels[label]
  235. if iCurry < len(curry) && curry[iCurry].index == i {
  236. if ok {
  237. return 0, fmt.Errorf("label name %q is already curried", label)
  238. }
  239. h = m.hashAdd(h, curry[iCurry].value)
  240. iCurry++
  241. } else {
  242. if !ok {
  243. return 0, fmt.Errorf("label name %q missing in label map", label)
  244. }
  245. h = m.hashAdd(h, val)
  246. }
  247. h = m.hashAddByte(h, model.SeparatorByte)
  248. }
  249. return h, nil
  250. }
  251. // metricWithLabelValues provides the metric and its label values for
  252. // disambiguation on hash collision.
  253. type metricWithLabelValues struct {
  254. values []string
  255. metric Metric
  256. }
  257. // curriedLabelValue sets the curried value for a label at the given index.
  258. type curriedLabelValue struct {
  259. index int
  260. value string
  261. }
  262. // metricMap is a helper for metricVec and shared between differently curried
  263. // metricVecs.
  264. type metricMap struct {
  265. mtx sync.RWMutex // Protects metrics.
  266. metrics map[uint64][]metricWithLabelValues
  267. desc *Desc
  268. newMetric func(labelValues ...string) Metric
  269. }
  270. // Describe implements Collector. It will send exactly one Desc to the provided
  271. // channel.
  272. func (m *metricMap) Describe(ch chan<- *Desc) {
  273. ch <- m.desc
  274. }
  275. // Collect implements Collector.
  276. func (m *metricMap) Collect(ch chan<- Metric) {
  277. m.mtx.RLock()
  278. defer m.mtx.RUnlock()
  279. for _, metrics := range m.metrics {
  280. for _, metric := range metrics {
  281. ch <- metric.metric
  282. }
  283. }
  284. }
  285. // Reset deletes all metrics in this vector.
  286. func (m *metricMap) Reset() {
  287. m.mtx.Lock()
  288. defer m.mtx.Unlock()
  289. for h := range m.metrics {
  290. delete(m.metrics, h)
  291. }
  292. }
  293. // deleteByHashWithLabelValues removes the metric from the hash bucket h. If
  294. // there are multiple matches in the bucket, use lvs to select a metric and
  295. // remove only that metric.
  296. func (m *metricMap) deleteByHashWithLabelValues(
  297. h uint64, lvs []string, curry []curriedLabelValue,
  298. ) bool {
  299. m.mtx.Lock()
  300. defer m.mtx.Unlock()
  301. metrics, ok := m.metrics[h]
  302. if !ok {
  303. return false
  304. }
  305. i := findMetricWithLabelValues(metrics, lvs, curry)
  306. if i >= len(metrics) {
  307. return false
  308. }
  309. if len(metrics) > 1 {
  310. old := metrics
  311. m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
  312. old[len(old)-1] = metricWithLabelValues{}
  313. } else {
  314. delete(m.metrics, h)
  315. }
  316. return true
  317. }
  318. // deleteByHashWithLabels removes the metric from the hash bucket h. If there
  319. // are multiple matches in the bucket, use lvs to select a metric and remove
  320. // only that metric.
  321. func (m *metricMap) deleteByHashWithLabels(
  322. h uint64, labels Labels, curry []curriedLabelValue,
  323. ) bool {
  324. m.mtx.Lock()
  325. defer m.mtx.Unlock()
  326. metrics, ok := m.metrics[h]
  327. if !ok {
  328. return false
  329. }
  330. i := findMetricWithLabels(m.desc, metrics, labels, curry)
  331. if i >= len(metrics) {
  332. return false
  333. }
  334. if len(metrics) > 1 {
  335. old := metrics
  336. m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
  337. old[len(old)-1] = metricWithLabelValues{}
  338. } else {
  339. delete(m.metrics, h)
  340. }
  341. return true
  342. }
  343. // getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
  344. // or creates it and returns the new one.
  345. //
  346. // This function holds the mutex.
  347. func (m *metricMap) getOrCreateMetricWithLabelValues(
  348. hash uint64, lvs []string, curry []curriedLabelValue,
  349. ) Metric {
  350. m.mtx.RLock()
  351. metric, ok := m.getMetricWithHashAndLabelValues(hash, lvs, curry)
  352. m.mtx.RUnlock()
  353. if ok {
  354. return metric
  355. }
  356. m.mtx.Lock()
  357. defer m.mtx.Unlock()
  358. metric, ok = m.getMetricWithHashAndLabelValues(hash, lvs, curry)
  359. if !ok {
  360. inlinedLVs := inlineLabelValues(lvs, curry)
  361. metric = m.newMetric(inlinedLVs...)
  362. m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: inlinedLVs, metric: metric})
  363. }
  364. return metric
  365. }
  366. // getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
  367. // or creates it and returns the new one.
  368. //
  369. // This function holds the mutex.
  370. func (m *metricMap) getOrCreateMetricWithLabels(
  371. hash uint64, labels Labels, curry []curriedLabelValue,
  372. ) Metric {
  373. m.mtx.RLock()
  374. metric, ok := m.getMetricWithHashAndLabels(hash, labels, curry)
  375. m.mtx.RUnlock()
  376. if ok {
  377. return metric
  378. }
  379. m.mtx.Lock()
  380. defer m.mtx.Unlock()
  381. metric, ok = m.getMetricWithHashAndLabels(hash, labels, curry)
  382. if !ok {
  383. lvs := extractLabelValues(m.desc, labels, curry)
  384. metric = m.newMetric(lvs...)
  385. m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: lvs, metric: metric})
  386. }
  387. return metric
  388. }
  389. // getMetricWithHashAndLabelValues gets a metric while handling possible
  390. // collisions in the hash space. Must be called while holding the read mutex.
  391. func (m *metricMap) getMetricWithHashAndLabelValues(
  392. h uint64, lvs []string, curry []curriedLabelValue,
  393. ) (Metric, bool) {
  394. metrics, ok := m.metrics[h]
  395. if ok {
  396. if i := findMetricWithLabelValues(metrics, lvs, curry); i < len(metrics) {
  397. return metrics[i].metric, true
  398. }
  399. }
  400. return nil, false
  401. }
  402. // getMetricWithHashAndLabels gets a metric while handling possible collisions in
  403. // the hash space. Must be called while holding read mutex.
  404. func (m *metricMap) getMetricWithHashAndLabels(
  405. h uint64, labels Labels, curry []curriedLabelValue,
  406. ) (Metric, bool) {
  407. metrics, ok := m.metrics[h]
  408. if ok {
  409. if i := findMetricWithLabels(m.desc, metrics, labels, curry); i < len(metrics) {
  410. return metrics[i].metric, true
  411. }
  412. }
  413. return nil, false
  414. }
  415. // findMetricWithLabelValues returns the index of the matching metric or
  416. // len(metrics) if not found.
  417. func findMetricWithLabelValues(
  418. metrics []metricWithLabelValues, lvs []string, curry []curriedLabelValue,
  419. ) int {
  420. for i, metric := range metrics {
  421. if matchLabelValues(metric.values, lvs, curry) {
  422. return i
  423. }
  424. }
  425. return len(metrics)
  426. }
  427. // findMetricWithLabels returns the index of the matching metric or len(metrics)
  428. // if not found.
  429. func findMetricWithLabels(
  430. desc *Desc, metrics []metricWithLabelValues, labels Labels, curry []curriedLabelValue,
  431. ) int {
  432. for i, metric := range metrics {
  433. if matchLabels(desc, metric.values, labels, curry) {
  434. return i
  435. }
  436. }
  437. return len(metrics)
  438. }
  439. func matchLabelValues(values []string, lvs []string, curry []curriedLabelValue) bool {
  440. if len(values) != len(lvs)+len(curry) {
  441. return false
  442. }
  443. var iLVs, iCurry int
  444. for i, v := range values {
  445. if iCurry < len(curry) && curry[iCurry].index == i {
  446. if v != curry[iCurry].value {
  447. return false
  448. }
  449. iCurry++
  450. continue
  451. }
  452. if v != lvs[iLVs] {
  453. return false
  454. }
  455. iLVs++
  456. }
  457. return true
  458. }
  459. func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool {
  460. if len(values) != len(labels)+len(curry) {
  461. return false
  462. }
  463. iCurry := 0
  464. for i, k := range desc.variableLabels {
  465. if iCurry < len(curry) && curry[iCurry].index == i {
  466. if values[i] != curry[iCurry].value {
  467. return false
  468. }
  469. iCurry++
  470. continue
  471. }
  472. if values[i] != labels[k] {
  473. return false
  474. }
  475. }
  476. return true
  477. }
  478. func extractLabelValues(desc *Desc, labels Labels, curry []curriedLabelValue) []string {
  479. labelValues := make([]string, len(labels)+len(curry))
  480. iCurry := 0
  481. for i, k := range desc.variableLabels {
  482. if iCurry < len(curry) && curry[iCurry].index == i {
  483. labelValues[i] = curry[iCurry].value
  484. iCurry++
  485. continue
  486. }
  487. labelValues[i] = labels[k]
  488. }
  489. return labelValues
  490. }
  491. func inlineLabelValues(lvs []string, curry []curriedLabelValue) []string {
  492. labelValues := make([]string, len(lvs)+len(curry))
  493. var iCurry, iLVs int
  494. for i := range labelValues {
  495. if iCurry < len(curry) && curry[iCurry].index == i {
  496. labelValues[i] = curry[iCurry].value
  497. iCurry++
  498. continue
  499. }
  500. labelValues[i] = lvs[iLVs]
  501. iLVs++
  502. }
  503. return labelValues
  504. }