'Prometheus counters: How to get current value with golang client?

I am using counters to count the number of requests. Is there any way to get current value of a prometheus counter?

My aim is to reuse existing counter without allocating another variable.

Golang prometheus client version is 1.1.0.



Solution 1:[1]

Currently there is no way to get the value of a counter in the official Golang implementation.

You can also avoid double counting by incrementing your own counter and use an CounterFunc to collect it.

Note: use integral type and atomic to avoid concurrent access issues

// declare the counter as unsigned int
var requestsCounter uint64 = 0

// register counter in Prometheus collector
prometheus.MustRegister(prometheus.NewCounterFunc(
    prometheus.CounterOpts{
        Name: "requests_total",
        Help: "Counts number of requests",
    },
    func() float64 {
        return float64(atomic.LoadUint64(&requestsCounter))
    }))

// somewhere in your code
atomic.AddUint64(&requestsCounter, 1)

Solution 2:[2]

It's easy, have a function to fetch Prometheus counter value


import (
    "github.com/prometheus/client_golang/prometheus"
    dto "github.com/prometheus/client_model/go"
    "github.com/prometheus/common/log"
)

func GetCounterValue(metric *prometheus.CounterVec) float64 {
    var m = &dto.Metric{}
    if err := metric.WithLabelValues("label1", "label2").Write(m); err != nil {
        log.Error(err)
        return 0
    }
    return m.Counter.GetValue()
}

Solution 3:[3]

It is possible to read the value of a counter (or any metric) in the official Golang implementation. I'm not sure when it was added.

This works for me for a simple metric with no vector:

func getMetricValue(col prometheus.Collector) float64 {
    c := make(chan prometheus.Metric, 1) // 1 for metric with no vector
    col.Collect(c)      // collect current metric value into the channel
    m := dto.Metric{}
    _ = (<-c).Write(&m) // read metric value from the channel
    return *m.Counter.Value
}

Update: here's a more general version that works with vectors and on histograms...

// GetMetricValue returns the sum of the Counter metrics associated with the Collector
// e.g. the metric for a non-vector, or the sum of the metrics for vector labels.
// If the metric is a Histogram then number of samples is used.
func GetMetricValue(col prometheus.Collector) float64 {
    var total float64
    collect(col, func(m dto.Metric) {
        if h := m.GetHistogram(); h != nil {
                total += float64(h.GetSampleCount())
        } else {
                total += m.GetCounter().GetValue()
        }
    })
    return total
}

// collect calls the function for each metric associated with the Collector
func collect(col prometheus.Collector, do func(dto.Metric)) {
    c := make(chan prometheus.Metric)
    go func(c chan prometheus.Metric) {
        col.Collect(c)
        close(c)
    }(c)
    for x := range c { // eg range across distinct label vector values
            m := dto.Metric{}
            _ = x.Write(&m)
            do(m)
    }
}

Solution 4:[4]

While it is possible to obtain counter values in github.com/prometheus/client_golang as pointed at this answer, this looks too complicated. This can be greatly simplified by using an alternative library for exporing Prometheus metrics - github.com/VictoriaMetrics/metrics:

import (
  "github.com/VictoriaMetrics/metrics"
)

var requestsTotal = metrics.NewCounter(`http_requests_total`)
//...

func getRequestsTotal() uint64 {
  return requestsTotal.Get()
}

E.g. just call Get() function on the needed counter.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Michael Doubez
Solution 2 Deep Nirmal
Solution 3
Solution 4 valyala