counter/fixed_precision.go

152 lines
4.3 KiB
Go
Raw Permalink Normal View History

// Package prom exports a collection of prometheus.Metrics that use int64 and
// atomic store and add for maximum speed.
//
// In testing it provides a 10x speedup compared to
// github.com/prometheus/client_golang/prometheus.Metrics.
package prom
import (
"math"
"sync/atomic"
"time"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
)
// NewCounter returns prometheus.Counter backed by a fixed-precision int64 that
// uses atomic operations.
func NewCounter(opts prometheus.CounterOpts, prec uint) prometheus.Counter {
return NewFixedPrecisionCounter(opts, prec)
}
// NewGauge returns prometheus.Gauge backed by a fixed-precision int64 that
// uses atomic operations.
func NewGauge(opts prometheus.GaugeOpts, prec uint) prometheus.Gauge {
return NewFixedPrecisionGauge(opts, prec)
}
// FixedPrecisionGauge implements a prometheus Gauge/Counter metric that uses atomic
// adds and stores for speed.
type FixedPrecisionGauge struct {
val int64
prec uint
desc *prometheus.Desc
}
// NewFixedPrecisionGauge returns a populated fixed-precision counter.
func NewFixedPrecisionGauge(opts prometheus.GaugeOpts, prec uint) *FixedPrecisionGauge {
desc := prometheus.NewDesc(
prometheus.BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
opts.Help,
nil,
opts.ConstLabels,
)
return &FixedPrecisionGauge{
desc: desc,
prec: uint(math.Pow10(int(prec))),
}
}
// Set stores the value in the counter.
func (fpg *FixedPrecisionGauge) Set(val float64) {
atomic.StoreInt64(&fpg.val, int64(val)*int64(fpg.prec))
}
// add maps delta into the appropriate precision and adds it to val.
func (fpg *FixedPrecisionGauge) add(delta int64) {
atomic.AddInt64(&fpg.val, delta*int64(fpg.prec))
}
// Inc adds 1 to the counter.
func (fpg *FixedPrecisionGauge) Inc() {
fpg.add(1)
}
// Dec decrements 1 from the counter.
func (fpg *FixedPrecisionGauge) Dec() {
fpg.add(-1)
}
// Add generically adds delta to the value stored by counter.
func (fpg *FixedPrecisionGauge) Add(delta float64) {
atomic.AddInt64(&fpg.val, int64(delta*float64(fpg.prec)))
}
// Sub is the inverse of Add.
func (fpg *FixedPrecisionGauge) Sub(val float64) {
fpg.Add(val * -1)
}
// Write is implemented to be useful as a prometheus counter.
func (fpg *FixedPrecisionGauge) Write(out *dto.Metric) error {
f := float64(atomic.LoadInt64(&fpg.val)) / float64(fpg.prec)
out.Counter = &dto.Counter{Value: &f}
return nil
}
// Value returns a float64 representation of the current value stored.
func (fpg *FixedPrecisionGauge) Value() float64 {
return float64(atomic.LoadInt64(&fpg.val)) / float64(fpg.prec)
}
// The following three methods exist to make this behave with Prometheus
// Desc returns this FixedPrecisionGauge's prometheus description.
func (fpg *FixedPrecisionGauge) Desc() *prometheus.Desc {
return fpg.desc
}
// Describe sends the counter's description to the chan
func (fpg *FixedPrecisionGauge) Describe(dc chan<- *prometheus.Desc) {
dc <- fpg.desc
}
// Collect sends the counter value to the chan
func (fpg *FixedPrecisionGauge) Collect(mc chan<- prometheus.Metric) {
mc <- fpg
}
// SetToCurrentTime sets the Gauge to the current Unix time in seconds.
//
// Beware that if precision is set too high (greater than 9) it can overflow
// the underlying int64.
func (fpg *FixedPrecisionGauge) SetToCurrentTime() {
fpg.Set(float64(time.Now().Unix()))
}
// FixedPrecisionCounter embeds FixedPrecisionGauge and enforces the same
// guarantees as a prometheus.Counter where negative adds panic.
type FixedPrecisionCounter struct {
FixedPrecisionGauge
}
// NewFixedPrecisionCounter creates a FixedPrecisionCounter based on the
// provided Opts. It matches prometheus.Counter behavior by panicking if adding
// negative numbers.
func NewFixedPrecisionCounter(opts prometheus.CounterOpts, prec uint) *FixedPrecisionCounter {
desc := prometheus.NewDesc(
prometheus.BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
opts.Help,
nil,
opts.ConstLabels,
)
return &FixedPrecisionCounter{
FixedPrecisionGauge: FixedPrecisionGauge{
desc: desc,
prec: uint(math.Pow10(int(prec))),
},
}
}
// Add adds the given value to the counter. It matches prometheus.Counter
// behavior by panicking if the value is < 0.
func (fpc *FixedPrecisionCounter) Add(v float64) {
if v < 0 {
panic("counter cannot decrease in value")
}
fpc.FixedPrecisionGauge.Add(v)
}