Skip to content

Prometheus Go Client

ObservabilityMonitoring / MetricsGo

What it is

The official Go instrumentation library for Prometheus, providing counters, gauges, histograms and summaries plus an HTTP handler to expose them.

Declare metrics, register them, update them in your code, and serve /metrics with promhttp. Labels turn one metric into a dimension you can slice.

Installation

go get github.com/prometheus/client_golang/prometheus

Getting started

The smallest useful thing you can do with it, and what each part means.

Counter and histogram with labels
var (
    requests = promauto.NewCounterVec(prometheus.CounterOpts{
        Name: "http_requests_total",
        Help: "Total HTTP requests.",
    }, []string{"method", "route", "status"})

    duration = promauto.NewHistogramVec(prometheus.HistogramOpts{
        Name:    "http_request_duration_seconds",
        Help:    "Request latency.",
        Buckets: prometheus.DefBuckets,
    }, []string{"route"})
)

func instrument(route string, next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        rec := &statusRecorder{ResponseWriter: w, status: 200}
        next(rec, r)

        requests.WithLabelValues(r.Method, route, strconv.Itoa(rec.status)).Inc()
        duration.WithLabelValues(route).Observe(time.Since(start).Seconds())
    }
}

http.Handle("/metrics", promhttp.Handler())
Note the route label is the pattern (/books/{id}), not the actual path. Using the raw URL would create a new time series per book id and eventually take Prometheus down — this is called a cardinality explosion.

Advanced usage

Where the library earns its place over a simpler alternative.

Gauges for current state
queueDepth := promauto.NewGauge(prometheus.GaugeOpts{
    Name: "job_queue_depth",
    Help: "Jobs currently waiting.",
})

queueDepth.Set(float64(len(queue)))
queueDepth.Inc()
queueDepth.Dec()

// Derive a value only when scraped, rather than tracking it continuously.
promauto.NewGaugeFunc(prometheus.GaugeOpts{
    Name: "db_open_connections",
}, func() float64 {
    return float64(db.Stats().OpenConnections)
})
GaugeFunc is evaluated at scrape time, which suits values you can already query cheaply and would otherwise have to mirror by hand.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

duplicate metrics collector registration attempted
The same metric name was registered twice. Declare metrics once as package-level variables using promauto.
Prometheus becomes slow or runs out of memory
Cardinality explosion from a high-variance label. Replace raw paths and IDs with bounded values.

Best practices

  • Never use unbounded values such as user IDs, URLs or error messages as label values.
  • Follow the naming convention: units in the name, _total suffix for counters.
  • Use histograms for latency so you can compute percentiles, not averages.
  • Register metrics once at package level; re-registering the same name panics.

Background

Why it exists, and what it was reacting to.

Prometheus itself is written in Go, so its client library is the reference implementation and the default way Go services expose metrics.