collection of high-performance prometheus counters
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

152 lines
4.3 KiB

  1. // Package prom exports a collection of prometheus.Metrics that use int64 and
  2. // atomic store and add for maximum speed.
  3. //
  4. // In testing it provides a 10x speedup compared to
  5. // github.com/prometheus/client_golang/prometheus.Metrics.
  6. package prom
  7. import (
  8. "math"
  9. "sync/atomic"
  10. "time"
  11. "github.com/prometheus/client_golang/prometheus"
  12. dto "github.com/prometheus/client_model/go"
  13. )
  14. // NewCounter returns prometheus.Counter backed by a fixed-precision int64 that
  15. // uses atomic operations.
  16. func NewCounter(opts prometheus.CounterOpts, prec uint) prometheus.Counter {
  17. return NewFixedPrecisionCounter(opts, prec)
  18. }
  19. // NewGauge returns prometheus.Gauge backed by a fixed-precision int64 that
  20. // uses atomic operations.
  21. func NewGauge(opts prometheus.GaugeOpts, prec uint) prometheus.Gauge {
  22. return NewFixedPrecisionGauge(opts, prec)
  23. }
  24. // FixedPrecisionGauge implements a prometheus Gauge/Counter metric that uses atomic
  25. // adds and stores for speed.
  26. type FixedPrecisionGauge struct {
  27. val int64
  28. prec uint
  29. desc *prometheus.Desc
  30. }
  31. // NewFixedPrecisionGauge returns a populated fixed-precision counter.
  32. func NewFixedPrecisionGauge(opts prometheus.GaugeOpts, prec uint) *FixedPrecisionGauge {
  33. desc := prometheus.NewDesc(
  34. prometheus.BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
  35. opts.Help,
  36. nil,
  37. opts.ConstLabels,
  38. )
  39. return &FixedPrecisionGauge{
  40. desc: desc,
  41. prec: uint(math.Pow10(int(prec))),
  42. }
  43. }
  44. // Set stores the value in the counter.
  45. func (fpg *FixedPrecisionGauge) Set(val float64) {
  46. atomic.StoreInt64(&fpg.val, int64(val)*int64(fpg.prec))
  47. }
  48. // add maps delta into the appropriate precision and adds it to val.
  49. func (fpg *FixedPrecisionGauge) add(delta int64) {
  50. atomic.AddInt64(&fpg.val, delta*int64(fpg.prec))
  51. }
  52. // Inc adds 1 to the counter.
  53. func (fpg *FixedPrecisionGauge) Inc() {
  54. fpg.add(1)
  55. }
  56. // Dec decrements 1 from the counter.
  57. func (fpg *FixedPrecisionGauge) Dec() {
  58. fpg.add(-1)
  59. }
  60. // Add generically adds delta to the value stored by counter.
  61. func (fpg *FixedPrecisionGauge) Add(delta float64) {
  62. atomic.AddInt64(&fpg.val, int64(delta*float64(fpg.prec)))
  63. }
  64. // Sub is the inverse of Add.
  65. func (fpg *FixedPrecisionGauge) Sub(val float64) {
  66. fpg.Add(val * -1)
  67. }
  68. // Write is implemented to be useful as a prometheus counter.
  69. func (fpg *FixedPrecisionGauge) Write(out *dto.Metric) error {
  70. f := float64(atomic.LoadInt64(&fpg.val)) / float64(fpg.prec)
  71. out.Counter = &dto.Counter{Value: &f}
  72. return nil
  73. }
  74. // Value returns a float64 representation of the current value stored.
  75. func (fpg *FixedPrecisionGauge) Value() float64 {
  76. return float64(atomic.LoadInt64(&fpg.val)) / float64(fpg.prec)
  77. }
  78. // The following three methods exist to make this behave with Prometheus
  79. // Desc returns this FixedPrecisionGauge's prometheus description.
  80. func (fpg *FixedPrecisionGauge) Desc() *prometheus.Desc {
  81. return fpg.desc
  82. }
  83. // Describe sends the counter's description to the chan
  84. func (fpg *FixedPrecisionGauge) Describe(dc chan<- *prometheus.Desc) {
  85. dc <- fpg.desc
  86. }
  87. // Collect sends the counter value to the chan
  88. func (fpg *FixedPrecisionGauge) Collect(mc chan<- prometheus.Metric) {
  89. mc <- fpg
  90. }
  91. // SetToCurrentTime sets the Gauge to the current Unix time in seconds.
  92. //
  93. // Beware that if precision is set too high (greater than 9) it can overflow
  94. // the underlying int64.
  95. func (fpg *FixedPrecisionGauge) SetToCurrentTime() {
  96. fpg.Set(float64(time.Now().Unix()))
  97. }
  98. // FixedPrecisionCounter embeds FixedPrecisionGauge and enforces the same
  99. // guarantees as a prometheus.Counter where negative adds panic.
  100. type FixedPrecisionCounter struct {
  101. FixedPrecisionGauge
  102. }
  103. // NewFixedPrecisionCounter creates a FixedPrecisionCounter based on the
  104. // provided Opts. It matches prometheus.Counter behavior by panicking if adding
  105. // negative numbers.
  106. func NewFixedPrecisionCounter(opts prometheus.CounterOpts, prec uint) *FixedPrecisionCounter {
  107. desc := prometheus.NewDesc(
  108. prometheus.BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
  109. opts.Help,
  110. nil,
  111. opts.ConstLabels,
  112. )
  113. return &FixedPrecisionCounter{
  114. FixedPrecisionGauge: FixedPrecisionGauge{
  115. desc: desc,
  116. prec: uint(math.Pow10(int(prec))),
  117. },
  118. }
  119. }
  120. // Add adds the given value to the counter. It matches prometheus.Counter
  121. // behavior by panicking if the value is < 0.
  122. func (fpc *FixedPrecisionCounter) Add(v float64) {
  123. if v < 0 {
  124. panic("counter cannot decrease in value")
  125. }
  126. fpc.FixedPrecisionGauge.Add(v)
  127. }