Search⌘ K

Exposing the Desired Metrics

Explore how to expose Go runtime metrics such as goroutines count and memory usage to Prometheus for effective monitoring. Learn to implement HTTP handlers that serve these metrics, use Go modules and external packages, and run the application with Docker. This lesson guides you through running and testing the metrics endpoint locally and preparing the environment for Prometheus integration.

This lesson illustrates how to expose metrics from the runtime/metrics package to Prometheus. In our case, we use /sched/goroutines:goroutines and /memory/classes/total:bytes. We already know about the former, which is the total number of goroutines. The latter metric is the amount of memory mapped by the Go runtime into the current process as read-write.

Note: Because the presented code uses an external package, it should be put inside ~/go/src and Go modules should be enabled using go mod init.

Coding example

The Go code of prometheus.go is as follows:

Go (1.19.0)
package main
import (
"log"
"math/rand"
"net/http"
"runtime"
"runtime/metrics"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

The first external package is the Go client library for Prometheus and the second package is for using the default handler function (promhttp.Handler()).

Go (1.19.0)
var PORT = ":1234"
var n_goroutines = prometheus.NewGauge(
prometheus.GaugeOpts{
Namespace: "packt",
Name: "n_goroutines",
Help: "Number of goroutines"})
var n_memory = prometheus.NewGauge(
prometheus.GaugeOpts{
Namespace: "packt",
Name: "n_memory",
Help: "Memory usage"})

Here, we define the two Prometheus metrics.

Go (1.19.0)
func main() {
rand.Seed(time.Now().Unix())
prometheus.MustRegister(n_goroutines)
prometheus.MustRegister(n_memory)
const nGo = "/sched/goroutines:goroutines"
const nMem = "/memory/classes/heap/free:bytes"
getMetric := make([]metrics.Sample, 2)
getMetric[0].Name = nGo
getMetric[1].Name = nMem
http.Handle("/metrics", promhttp.Handler())
go func() {
for {
for i := 1; i < 4; i++ {
go func() {
_ = make([]int, 1000000)
time.Sleep(time.Duration(rand.Intn(10)) * time.Second)
}()
}
runtime.GC()
metrics.Read(getMetric)
goVal := getMetric[0].Value.Uint64()
memVal := getMetric[1].Value.Uint64()
time.Sleep(time.Duration(rand.Intn(15)) * time.Second)
n_goroutines.Set(float64(goVal))
n_memory.Set(float64(memVal))
}
}()
log.Println("Listening to port", PORT)
log.Println(http.ListenAndServe(PORT, nil))
}
...