namespace.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. package metrics
  2. import (
  3. "fmt"
  4. "sync"
  5. "github.com/prometheus/client_golang/prometheus"
  6. )
  7. type Labels map[string]string
  8. // NewNamespace returns a namespaces that is responsible for managing a collection of
  9. // metrics for a particual namespace and subsystem
  10. //
  11. // labels allows const labels to be added to all metrics created in this namespace
  12. // and are commonly used for data like application version and git commit
  13. func NewNamespace(name, subsystem string, labels Labels) *Namespace {
  14. if labels == nil {
  15. labels = make(map[string]string)
  16. }
  17. return &Namespace{
  18. name: name,
  19. subsystem: subsystem,
  20. labels: labels,
  21. }
  22. }
  23. // Namespace describes a set of metrics that share a namespace and subsystem.
  24. type Namespace struct {
  25. name string
  26. subsystem string
  27. labels Labels
  28. mu sync.Mutex
  29. metrics []prometheus.Collector
  30. }
  31. // WithConstLabels returns a namespace with the provided set of labels merged
  32. // with the existing constant labels on the namespace.
  33. //
  34. // Only metrics created with the returned namespace will get the new constant
  35. // labels. The returned namespace must be registered separately.
  36. func (n *Namespace) WithConstLabels(labels Labels) *Namespace {
  37. n.mu.Lock()
  38. ns := &Namespace{
  39. name: n.name,
  40. subsystem: n.subsystem,
  41. labels: mergeLabels(n.labels, labels),
  42. }
  43. n.mu.Unlock()
  44. return ns
  45. }
  46. func (n *Namespace) NewCounter(name, help string) Counter {
  47. c := &counter{pc: prometheus.NewCounter(n.newCounterOpts(name, help))}
  48. n.Add(c)
  49. return c
  50. }
  51. func (n *Namespace) NewLabeledCounter(name, help string, labels ...string) LabeledCounter {
  52. c := &labeledCounter{pc: prometheus.NewCounterVec(n.newCounterOpts(name, help), labels)}
  53. n.Add(c)
  54. return c
  55. }
  56. func (n *Namespace) newCounterOpts(name, help string) prometheus.CounterOpts {
  57. return prometheus.CounterOpts{
  58. Namespace: n.name,
  59. Subsystem: n.subsystem,
  60. Name: makeName(name, Total),
  61. Help: help,
  62. ConstLabels: prometheus.Labels(n.labels),
  63. }
  64. }
  65. func (n *Namespace) NewTimer(name, help string) Timer {
  66. t := &timer{
  67. m: prometheus.NewHistogram(n.newTimerOpts(name, help)),
  68. }
  69. n.Add(t)
  70. return t
  71. }
  72. func (n *Namespace) NewLabeledTimer(name, help string, labels ...string) LabeledTimer {
  73. t := &labeledTimer{
  74. m: prometheus.NewHistogramVec(n.newTimerOpts(name, help), labels),
  75. }
  76. n.Add(t)
  77. return t
  78. }
  79. func (n *Namespace) newTimerOpts(name, help string) prometheus.HistogramOpts {
  80. return prometheus.HistogramOpts{
  81. Namespace: n.name,
  82. Subsystem: n.subsystem,
  83. Name: makeName(name, Seconds),
  84. Help: help,
  85. ConstLabels: prometheus.Labels(n.labels),
  86. }
  87. }
  88. func (n *Namespace) NewGauge(name, help string, unit Unit) Gauge {
  89. g := &gauge{
  90. pg: prometheus.NewGauge(n.newGaugeOpts(name, help, unit)),
  91. }
  92. n.Add(g)
  93. return g
  94. }
  95. func (n *Namespace) NewLabeledGauge(name, help string, unit Unit, labels ...string) LabeledGauge {
  96. g := &labeledGauge{
  97. pg: prometheus.NewGaugeVec(n.newGaugeOpts(name, help, unit), labels),
  98. }
  99. n.Add(g)
  100. return g
  101. }
  102. func (n *Namespace) newGaugeOpts(name, help string, unit Unit) prometheus.GaugeOpts {
  103. return prometheus.GaugeOpts{
  104. Namespace: n.name,
  105. Subsystem: n.subsystem,
  106. Name: makeName(name, unit),
  107. Help: help,
  108. ConstLabels: prometheus.Labels(n.labels),
  109. }
  110. }
  111. func (n *Namespace) Describe(ch chan<- *prometheus.Desc) {
  112. n.mu.Lock()
  113. defer n.mu.Unlock()
  114. for _, metric := range n.metrics {
  115. metric.Describe(ch)
  116. }
  117. }
  118. func (n *Namespace) Collect(ch chan<- prometheus.Metric) {
  119. n.mu.Lock()
  120. defer n.mu.Unlock()
  121. for _, metric := range n.metrics {
  122. metric.Collect(ch)
  123. }
  124. }
  125. func (n *Namespace) Add(collector prometheus.Collector) {
  126. n.mu.Lock()
  127. n.metrics = append(n.metrics, collector)
  128. n.mu.Unlock()
  129. }
  130. func (n *Namespace) NewDesc(name, help string, unit Unit, labels ...string) *prometheus.Desc {
  131. name = makeName(name, unit)
  132. namespace := n.name
  133. if n.subsystem != "" {
  134. namespace = fmt.Sprintf("%s_%s", namespace, n.subsystem)
  135. }
  136. name = fmt.Sprintf("%s_%s", namespace, name)
  137. return prometheus.NewDesc(name, help, labels, prometheus.Labels(n.labels))
  138. }
  139. // mergeLabels merges two or more labels objects into a single map, favoring
  140. // the later labels.
  141. func mergeLabels(lbs ...Labels) Labels {
  142. merged := make(Labels)
  143. for _, target := range lbs {
  144. for k, v := range target {
  145. merged[k] = v
  146. }
  147. }
  148. return merged
  149. }
  150. func makeName(name string, unit Unit) string {
  151. if unit == "" {
  152. return name
  153. }
  154. return fmt.Sprintf("%s_%s", name, unit)
  155. }