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:
- https://opentelemetry.io/docs/instrumentation/go/libraries/
- https://prometheus.io/docs/tutorials/instrumenting_http_server_in_go/
- https://opentelemetry.io/docs/specs/semconv/http/http-metrics/
- https://grafana.com/ecosystem/registry/
- https://grafana.com/docs/opentelemetry/instrumentation/java/metrics-from-traces/
- https://sweetcode.io/distributed-tracing-what-why-how/
- https://grafana.com/sitemap/