Instrumenting HTTP Handlers - docker/go-metrics

Instrumenting HTTP Handlers with the “github.com/docker/go-metrics” module in Go can be achieved using various options. Here, we will cover some of the possible options with examples.

Option 1: Using Prometheus Go Client

The Prometheus Go Client provides a simple and effective way to instrument HTTP handlers. Here’s an example of how to use it:

import (
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
httpRequestDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name:    "http_request_duration_seconds",
Help:    "HTTP request duration in seconds.",
Buckets: prometheus.DefBuckets,
},
[]string{"handler", "code"},
)
)

func ping(w http.ResponseWriter, req *http.Request) {
timer := prometheus.NewTimer(httpRequestDuration.MustCurryWith(prometheus.Labels{"handler": "/ping"}))
defer timer.ObserveDuration()

fmt.Fprintf(w, "pong")
}

func main() {
http.Handle("/metrics", promhttp.Handler())
http.HandleFunc("/ping", ping)

http.ListenAndServe(":8080", nil)
}

In this example, we create a histogram metric for HTTP request duration and use it to measure the time taken by the /ping handler.

Option 2: Using Manual Instrumentation

Manual instrumentation involves adding custom code to instrument HTTP handlers. Here’s an example of how to do it:

import (
"net/http"
"time"
)

type requestMetrics struct {
handler string
start   time.Time
}

func (m *requestMetrics) observe() {
duration := time.Since(m.start)
// Report the duration to a metrics system here
}

func ping(w http.ResponseWriter, req *http.Request) {
m := &requestMetrics{
handler: "/ping",
start:   time.Now(),
}
defer m.observe()

fmt.Fprintf(w, "pong")
}

func main() {
http.HandleFunc("/ping", ping)

http.ListenAndServe(":8080", nil)
}

In this example, we create a custom requestMetrics struct to measure the time taken by the /ping handler. The observe method can be used to report the duration to a metrics system.

Option 3: Using OpenTelemetry

OpenTelemetry provides a powerful and flexible way to instrument HTTP handlers. Here’s an example of how to use it:

import (
"context"
"net/http"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/trace"
)

func ping(w http.ResponseWriter, req *http.Request) {
tracer := otel.Tracer("my-service")
ctx, span := tracer.Start(req.Context(), "ping")
defer span.End()

req = req.WithContext(ctx)
propagation.Set(req, propagation.HeaderCarrier(req.Header))

fmt.Fprintf(w, "pong")
}

func main() {
http.HandleFunc("/ping", ping)

http.ListenAndServe(":8080", nil)
}

In this example, we use the OpenTelemetry Go SDK to create a trace span for the /ping handler. The span can be used to measure the duration of the request and report it to a tracing system.

Sources: