Observability
Monitor your client's activity for optimization and debugging.
go-redis has built-in support for OpenTelemetry (OTel)
instrumentation to collect metrics. This can be very helpful for
diagnosing problems and improving the performance and connection resiliency of
your application. See the
Observability overview
for an introduction to Redis client observability and a reference guide for the
available metrics.
This page explains how to enable and use OTel instrumentation
in go-redis using an example configuration for a local Grafana
instance. See our
observability demonstration repository
on GitHub to learn how to set up a suitable Grafana dashboard.
Installation
Install OTel support for go-redis with the following commands:
go get github.com/redis/go-redis/extra/redisotel-native/v9
go get go.opentelemetry.io/otel
Import
Start by importing the required OTel and Redis modules:
import (
"context"
"fmt"
"time"
"github.com/redis/go-redis/extra/redisotel-native/v9"
"github.com/redis/go-redis/v9"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
)
func main() {
ctx := context.Background()
// Create OTLP exporter that sends metrics to the collector
// Default endpoint is localhost:4317 (gRPC)
exporter, err := otlpmetricgrpc.New(ctx,
otlpmetricgrpc.WithInsecure(), // Use insecure for local development
// For production, configure TLS and authentication:
// otlpmetricgrpc.WithEndpoint("your-collector:4317"),
// otlpmetricgrpc.WithTLSCredentials(...),
)
if err != nil {
panic(err)
}
// Create resource with service name
res, err := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName(
fmt.Sprintf("go-redis-examples:%d", time.Now().Unix()),
),
),
)
if err != nil {
panic(err)
}
// Create meter provider with periodic reader
// Metrics are exported every 10 seconds
meterProvider := metric.NewMeterProvider(
metric.WithResource(res),
metric.WithReader(
metric.NewPeriodicReader(exporter,
metric.WithInterval(10*time.Second),
),
),
)
// Set the global meter provider
otel.SetMeterProvider(meterProvider)
// Initialize OTel instrumentation BEFORE creating Redis clients
otelInstance := redisotel.GetObservabilityInstance()
config := redisotel.NewConfig().
// You must enable OTel explicitly
WithEnabled(true).
// Enable the metric groups you want to collect. Use bitwise OR
// to combine multiple groups. The default is `MetricGroupAll`
// which includes all groups.
WithMetricGroups(redisotel.MetricGroupFlagCommand |
redisotel.MetricGroupFlagConnectionBasic |
redisotel.MetricGroupFlagResiliency |
redisotel.MetricGroupFlagConnectionAdvanced).
// Filter which commands to track
WithIncludeCommands([]string{"GET", "SET"}).
WithExcludeCommands([]string{"DEBUG", "SLOWLOG"}).
// Privacy controls
WithHidePubSubChannelNames(true).
WithHideStreamNames(true).
// Custom histogram buckets
WithHistogramBuckets([]float64{
0.0001, // 0.1ms
0.0005, // 0.5ms
0.001, // 1ms
0.005, // 5ms
0.01, // 10ms
0.05, // 50ms
0.1, // 100ms
0.5, // 500ms
1.0, // 1s
5.0, // 5s
10.0, // 10s
})
if err := otelInstance.Init(config); err != nil {
panic(err)
}
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
rdb.Set(ctx, "key", "value", 0)
v, err := rdb.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println(v)
rdb.Close()
otelInstance.Shutdown()
meterProvider.Shutdown(context.Background())
}
Configure the meter provider
Otel uses a Meter provider to create the objects that collect the metric information. The example below configures a meter provider to export metrics to a local Otel collector every 10 seconds, but see the OpenTelemetry Go docs to learn more about other export options.
import (
"context"
"fmt"
"time"
"github.com/redis/go-redis/extra/redisotel-native/v9"
"github.com/redis/go-redis/v9"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
)
func main() {
ctx := context.Background()
// Create OTLP exporter that sends metrics to the collector
// Default endpoint is localhost:4317 (gRPC)
exporter, err := otlpmetricgrpc.New(ctx,
otlpmetricgrpc.WithInsecure(), // Use insecure for local development
// For production, configure TLS and authentication:
// otlpmetricgrpc.WithEndpoint("your-collector:4317"),
// otlpmetricgrpc.WithTLSCredentials(...),
)
if err != nil {
panic(err)
}
// Create resource with service name
res, err := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName(
fmt.Sprintf("go-redis-examples:%d", time.Now().Unix()),
),
),
)
if err != nil {
panic(err)
}
// Create meter provider with periodic reader
// Metrics are exported every 10 seconds
meterProvider := metric.NewMeterProvider(
metric.WithResource(res),
metric.WithReader(
metric.NewPeriodicReader(exporter,
metric.WithInterval(10*time.Second),
),
),
)
// Set the global meter provider
otel.SetMeterProvider(meterProvider)
// Initialize OTel instrumentation BEFORE creating Redis clients
otelInstance := redisotel.GetObservabilityInstance()
config := redisotel.NewConfig().
// You must enable OTel explicitly
WithEnabled(true).
// Enable the metric groups you want to collect. Use bitwise OR
// to combine multiple groups. The default is `MetricGroupAll`
// which includes all groups.
WithMetricGroups(redisotel.MetricGroupFlagCommand |
redisotel.MetricGroupFlagConnectionBasic |
redisotel.MetricGroupFlagResiliency |
redisotel.MetricGroupFlagConnectionAdvanced).
// Filter which commands to track
WithIncludeCommands([]string{"GET", "SET"}).
WithExcludeCommands([]string{"DEBUG", "SLOWLOG"}).
// Privacy controls
WithHidePubSubChannelNames(true).
WithHideStreamNames(true).
// Custom histogram buckets
WithHistogramBuckets([]float64{
0.0001, // 0.1ms
0.0005, // 0.5ms
0.001, // 1ms
0.005, // 5ms
0.01, // 10ms
0.05, // 50ms
0.1, // 100ms
0.5, // 500ms
1.0, // 1s
5.0, // 5s
10.0, // 10s
})
if err := otelInstance.Init(config); err != nil {
panic(err)
}
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
rdb.Set(ctx, "key", "value", 0)
v, err := rdb.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println(v)
rdb.Close()
otelInstance.Shutdown()
meterProvider.Shutdown(context.Background())
}
Configure the Redis client
You configure the client library for OTel only once per application. This will
enable OTel for all Redis connections you create. The example below shows the
options you can pass to the observability instance via the redisotel.Config object
during initialization.
import (
"context"
"fmt"
"time"
"github.com/redis/go-redis/extra/redisotel-native/v9"
"github.com/redis/go-redis/v9"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
)
func main() {
ctx := context.Background()
// Create OTLP exporter that sends metrics to the collector
// Default endpoint is localhost:4317 (gRPC)
exporter, err := otlpmetricgrpc.New(ctx,
otlpmetricgrpc.WithInsecure(), // Use insecure for local development
// For production, configure TLS and authentication:
// otlpmetricgrpc.WithEndpoint("your-collector:4317"),
// otlpmetricgrpc.WithTLSCredentials(...),
)
if err != nil {
panic(err)
}
// Create resource with service name
res, err := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName(
fmt.Sprintf("go-redis-examples:%d", time.Now().Unix()),
),
),
)
if err != nil {
panic(err)
}
// Create meter provider with periodic reader
// Metrics are exported every 10 seconds
meterProvider := metric.NewMeterProvider(
metric.WithResource(res),
metric.WithReader(
metric.NewPeriodicReader(exporter,
metric.WithInterval(10*time.Second),
),
),
)
// Set the global meter provider
otel.SetMeterProvider(meterProvider)
// Initialize OTel instrumentation BEFORE creating Redis clients
otelInstance := redisotel.GetObservabilityInstance()
config := redisotel.NewConfig().
// You must enable OTel explicitly
WithEnabled(true).
// Enable the metric groups you want to collect. Use bitwise OR
// to combine multiple groups. The default is `MetricGroupAll`
// which includes all groups.
WithMetricGroups(redisotel.MetricGroupFlagCommand |
redisotel.MetricGroupFlagConnectionBasic |
redisotel.MetricGroupFlagResiliency |
redisotel.MetricGroupFlagConnectionAdvanced).
// Filter which commands to track
WithIncludeCommands([]string{"GET", "SET"}).
WithExcludeCommands([]string{"DEBUG", "SLOWLOG"}).
// Privacy controls
WithHidePubSubChannelNames(true).
WithHideStreamNames(true).
// Custom histogram buckets
WithHistogramBuckets([]float64{
0.0001, // 0.1ms
0.0005, // 0.5ms
0.001, // 1ms
0.005, // 5ms
0.01, // 10ms
0.05, // 50ms
0.1, // 100ms
0.5, // 500ms
1.0, // 1s
5.0, // 5s
10.0, // 10s
})
if err := otelInstance.Init(config); err != nil {
panic(err)
}
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
rdb.Set(ctx, "key", "value", 0)
v, err := rdb.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println(v)
rdb.Close()
otelInstance.Shutdown()
meterProvider.Shutdown(context.Background())
}
The available option methods for redisotel.Config are described in the table below.
| Method | Type | Description |
|---|---|---|
WithEnabled |
bool |
Enable or disable OTel instrumentation. Default is false, so you must enable it explicitly. |
WithMetricGroups |
MetricGroupFlag |
Bitmap of metric groups to enable. By default, all metrics are enabled (equivalent to MetricGroupAll). See Redis metric groups for a list of available groups. |
WithIncludeCommands |
[]string |
List of Redis commands to track. If set, only these commands will be tracked. Note that you should use the Redis command name rather than the Go method name (for example LPOP rather than LPopCount). |
WithExcludeCommands |
[]string |
List of Redis commands to exclude from tracking. If set, all commands except these will be tracked. Note that you should use the Redis command name rather than the Go method name (for example LPOP rather than LPopCount). |
WithHidePubSubChannelNames |
bool |
If true, channel names in pub/sub metrics will be hidden. |
WithHideStreamNames |
bool |
If true, stream names in streaming metrics will be hidden. |
WithHistogramBuckets |
[]float64 |
List of bucket boundaries for the histogram metrics. See Custom histogram buckets below for more information. |
Custom histogram buckets
For the histogram metrics, a reasonable default set of buckets is defined, but
you can customize the bucket boundaries to suit your needs (the buckets are the
ranges of data values counted for each bar of the histogram). Pass an increasing
list of float values to the WithHistogramBuckets option when you create the redisotel.Config
object. The first and last values in the list are the lower and upper bounds of the
histogram, respectively, and the values in between define the bucket boundaries.
Use Redis
Once you have configured the client, all Redis connections you create will be automatically instrumented and the collected metrics will be exported to your configured destination.
The example below shows the simplest Redis connection and a few commands, but see the observability demonstration repository for an example that calls a variety of commands in a more realistic way.
import (
"context"
"fmt"
"time"
"github.com/redis/go-redis/extra/redisotel-native/v9"
"github.com/redis/go-redis/v9"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
)
func main() {
ctx := context.Background()
// Create OTLP exporter that sends metrics to the collector
// Default endpoint is localhost:4317 (gRPC)
exporter, err := otlpmetricgrpc.New(ctx,
otlpmetricgrpc.WithInsecure(), // Use insecure for local development
// For production, configure TLS and authentication:
// otlpmetricgrpc.WithEndpoint("your-collector:4317"),
// otlpmetricgrpc.WithTLSCredentials(...),
)
if err != nil {
panic(err)
}
// Create resource with service name
res, err := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName(
fmt.Sprintf("go-redis-examples:%d", time.Now().Unix()),
),
),
)
if err != nil {
panic(err)
}
// Create meter provider with periodic reader
// Metrics are exported every 10 seconds
meterProvider := metric.NewMeterProvider(
metric.WithResource(res),
metric.WithReader(
metric.NewPeriodicReader(exporter,
metric.WithInterval(10*time.Second),
),
),
)
// Set the global meter provider
otel.SetMeterProvider(meterProvider)
// Initialize OTel instrumentation BEFORE creating Redis clients
otelInstance := redisotel.GetObservabilityInstance()
config := redisotel.NewConfig().
// You must enable OTel explicitly
WithEnabled(true).
// Enable the metric groups you want to collect. Use bitwise OR
// to combine multiple groups. The default is `MetricGroupAll`
// which includes all groups.
WithMetricGroups(redisotel.MetricGroupFlagCommand |
redisotel.MetricGroupFlagConnectionBasic |
redisotel.MetricGroupFlagResiliency |
redisotel.MetricGroupFlagConnectionAdvanced).
// Filter which commands to track
WithIncludeCommands([]string{"GET", "SET"}).
WithExcludeCommands([]string{"DEBUG", "SLOWLOG"}).
// Privacy controls
WithHidePubSubChannelNames(true).
WithHideStreamNames(true).
// Custom histogram buckets
WithHistogramBuckets([]float64{
0.0001, // 0.1ms
0.0005, // 0.5ms
0.001, // 1ms
0.005, // 5ms
0.01, // 10ms
0.05, // 50ms
0.1, // 100ms
0.5, // 500ms
1.0, // 1s
5.0, // 5s
10.0, // 10s
})
if err := otelInstance.Init(config); err != nil {
panic(err)
}
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
rdb.Set(ctx, "key", "value", 0)
v, err := rdb.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println(v)
rdb.Close()
otelInstance.Shutdown()
meterProvider.Shutdown(context.Background())
}
Shutdown
When your application exits, you should close the Redis connection and then
you should call the Shutdown() method on the
ObservabilityInstance and MeterProvider instances to ensure that all pending
metrics are exported. You may find it useful to put the shutdown code in a
defer statement to ensure that it is called even if the main function
exits early due to an error.
import (
"context"
"fmt"
"time"
"github.com/redis/go-redis/extra/redisotel-native/v9"
"github.com/redis/go-redis/v9"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
)
func main() {
ctx := context.Background()
// Create OTLP exporter that sends metrics to the collector
// Default endpoint is localhost:4317 (gRPC)
exporter, err := otlpmetricgrpc.New(ctx,
otlpmetricgrpc.WithInsecure(), // Use insecure for local development
// For production, configure TLS and authentication:
// otlpmetricgrpc.WithEndpoint("your-collector:4317"),
// otlpmetricgrpc.WithTLSCredentials(...),
)
if err != nil {
panic(err)
}
// Create resource with service name
res, err := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName(
fmt.Sprintf("go-redis-examples:%d", time.Now().Unix()),
),
),
)
if err != nil {
panic(err)
}
// Create meter provider with periodic reader
// Metrics are exported every 10 seconds
meterProvider := metric.NewMeterProvider(
metric.WithResource(res),
metric.WithReader(
metric.NewPeriodicReader(exporter,
metric.WithInterval(10*time.Second),
),
),
)
// Set the global meter provider
otel.SetMeterProvider(meterProvider)
// Initialize OTel instrumentation BEFORE creating Redis clients
otelInstance := redisotel.GetObservabilityInstance()
config := redisotel.NewConfig().
// You must enable OTel explicitly
WithEnabled(true).
// Enable the metric groups you want to collect. Use bitwise OR
// to combine multiple groups. The default is `MetricGroupAll`
// which includes all groups.
WithMetricGroups(redisotel.MetricGroupFlagCommand |
redisotel.MetricGroupFlagConnectionBasic |
redisotel.MetricGroupFlagResiliency |
redisotel.MetricGroupFlagConnectionAdvanced).
// Filter which commands to track
WithIncludeCommands([]string{"GET", "SET"}).
WithExcludeCommands([]string{"DEBUG", "SLOWLOG"}).
// Privacy controls
WithHidePubSubChannelNames(true).
WithHideStreamNames(true).
// Custom histogram buckets
WithHistogramBuckets([]float64{
0.0001, // 0.1ms
0.0005, // 0.5ms
0.001, // 1ms
0.005, // 5ms
0.01, // 10ms
0.05, // 50ms
0.1, // 100ms
0.5, // 500ms
1.0, // 1s
5.0, // 5s
10.0, // 10s
})
if err := otelInstance.Init(config); err != nil {
panic(err)
}
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
rdb.Set(ctx, "key", "value", 0)
v, err := rdb.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println(v)
rdb.Close()
otelInstance.Shutdown()
meterProvider.Shutdown(context.Background())
}