expvar_collector.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  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. "encoding/json"
  16. "expvar"
  17. )
  18. type expvarCollector struct {
  19. exports map[string]*Desc
  20. }
  21. // NewExpvarCollector returns a newly allocated expvar Collector that still has
  22. // to be registered with a Prometheus registry.
  23. //
  24. // An expvar Collector collects metrics from the expvar interface. It provides a
  25. // quick way to expose numeric values that are already exported via expvar as
  26. // Prometheus metrics. Note that the data models of expvar and Prometheus are
  27. // fundamentally different, and that the expvar Collector is inherently slower
  28. // than native Prometheus metrics. Thus, the expvar Collector is probably great
  29. // for experiments and prototying, but you should seriously consider a more
  30. // direct implementation of Prometheus metrics for monitoring production
  31. // systems.
  32. //
  33. // The exports map has the following meaning:
  34. //
  35. // The keys in the map correspond to expvar keys, i.e. for every expvar key you
  36. // want to export as Prometheus metric, you need an entry in the exports
  37. // map. The descriptor mapped to each key describes how to export the expvar
  38. // value. It defines the name and the help string of the Prometheus metric
  39. // proxying the expvar value. The type will always be Untyped.
  40. //
  41. // For descriptors without variable labels, the expvar value must be a number or
  42. // a bool. The number is then directly exported as the Prometheus sample
  43. // value. (For a bool, 'false' translates to 0 and 'true' to 1). Expvar values
  44. // that are not numbers or bools are silently ignored.
  45. //
  46. // If the descriptor has one variable label, the expvar value must be an expvar
  47. // map. The keys in the expvar map become the various values of the one
  48. // Prometheus label. The values in the expvar map must be numbers or bools again
  49. // as above.
  50. //
  51. // For descriptors with more than one variable label, the expvar must be a
  52. // nested expvar map, i.e. where the values of the topmost map are maps again
  53. // etc. until a depth is reached that corresponds to the number of labels. The
  54. // leaves of that structure must be numbers or bools as above to serve as the
  55. // sample values.
  56. //
  57. // Anything that does not fit into the scheme above is silently ignored.
  58. func NewExpvarCollector(exports map[string]*Desc) Collector {
  59. return &expvarCollector{
  60. exports: exports,
  61. }
  62. }
  63. // Describe implements Collector.
  64. func (e *expvarCollector) Describe(ch chan<- *Desc) {
  65. for _, desc := range e.exports {
  66. ch <- desc
  67. }
  68. }
  69. // Collect implements Collector.
  70. func (e *expvarCollector) Collect(ch chan<- Metric) {
  71. for name, desc := range e.exports {
  72. var m Metric
  73. expVar := expvar.Get(name)
  74. if expVar == nil {
  75. continue
  76. }
  77. var v interface{}
  78. labels := make([]string, len(desc.variableLabels))
  79. if err := json.Unmarshal([]byte(expVar.String()), &v); err != nil {
  80. ch <- NewInvalidMetric(desc, err)
  81. continue
  82. }
  83. var processValue func(v interface{}, i int)
  84. processValue = func(v interface{}, i int) {
  85. if i >= len(labels) {
  86. copiedLabels := append(make([]string, 0, len(labels)), labels...)
  87. switch v := v.(type) {
  88. case float64:
  89. m = MustNewConstMetric(desc, UntypedValue, v, copiedLabels...)
  90. case bool:
  91. if v {
  92. m = MustNewConstMetric(desc, UntypedValue, 1, copiedLabels...)
  93. } else {
  94. m = MustNewConstMetric(desc, UntypedValue, 0, copiedLabels...)
  95. }
  96. default:
  97. return
  98. }
  99. ch <- m
  100. return
  101. }
  102. vm, ok := v.(map[string]interface{})
  103. if !ok {
  104. return
  105. }
  106. for lv, val := range vm {
  107. labels[i] = lv
  108. processValue(val, i+1)
  109. }
  110. }
  111. processValue(v, 0)
  112. }
  113. }