Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
65d0922
First draft. One metric - command duration.
elena-kolevska Oct 24, 2025
c17657c
Adds connection state metrics
elena-kolevska Oct 27, 2025
9cf404c
wip
elena-kolevska Nov 3, 2025
fe380b9
Add phase 1 metrics
ofekshenawa Nov 30, 2025
28292b2
Add client api configurations
ofekshenawa Dec 3, 2025
cf9b683
remove redundent code
ofekshenawa Dec 3, 2025
568d889
refactor: move otel-metrics example to separate module
ofekshenawa Dec 3, 2025
d60e2c9
refactor: move otel-metrics example to separate module
ofekshenawa Dec 3, 2025
b4d5dbc
remove internal test
ofekshenawa Dec 3, 2025
2f9ce8b
Resolve merge conflicts with upstream/master
ofekshenawa Dec 3, 2025
31f2dbd
Resolve merge conflicts with upstream/master
ofekshenawa Dec 3, 2025
e9376db
Fix linting issues
ofekshenawa Dec 3, 2025
990e0ec
Fix pool idle connection counter bug
ofekshenawa Dec 3, 2025
5d5dbb8
Add OTel performance benchmarking suite
ofekshenawa Dec 3, 2025
a6ebc87
Improve perf compatison script
ofekshenawa Dec 4, 2025
168f918
Merge remote-tracking branch 'upstream/master' into add-metrics
ofekshenawa Dec 4, 2025
22a92e7
Add phase 2,3 metrics
ofekshenawa Dec 10, 2025
b8afd7f
Merge remote-tracking branch 'upstream/master' into add-metrics
ofekshenawa Dec 10, 2025
89c74a5
Remove local benchmark tests
ofekshenawa Dec 10, 2025
ed99b9d
Add new fields to tests
ofekshenawa Dec 14, 2025
3c09637
Change db.client.connection.count and db.client.connection.pending_re…
ofekshenawa Dec 22, 2025
7ddfbd2
Merge remote-tracking branch 'upstream/master' into add-metrics
ofekshenawa Dec 22, 2025
8d037b4
fix golangci lint
ofekshenawa Dec 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions example/otel-metrics/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# OpenTelemetry Metrics Example

This example demonstrates how to enable OpenTelemetry metrics for Redis operations using the `extra/redisotel-native` package.

## Features

- ✅ OTLP exporter configuration
- ✅ Periodic metric export (every 10 seconds)
- ✅ Concurrent Redis operations
- ✅ Automatic metric collection for:
- Operation duration
- Connection metrics
- Error tracking

## Prerequisites

- Go 1.23.0 or later
- Redis server running on `localhost:6379`
- OTLP collector running on `localhost:4317` (optional)

## Running the Example

```bash
# Start Redis (if not already running)
redis-server

# Optional: Start OTLP collector
# See: https://opentelemetry.io/docs/collector/

# Run the example
go run main.go
```

## What It Does

1. Creates an OTLP exporter that sends metrics to a collector
2. Sets up a meter provider with periodic export (every 10 seconds)
3. Initializes Redis client with OTel instrumentation
4. Executes concurrent Redis operations (SET commands)
5. Waits for metrics to be exported

## Metrics Collected

The example automatically collects:

- **db.client.operation.duration** - Operation latency histogram
- **db.client.connection.create_time** - Connection creation time
- **db.client.connection.count** - Active connection count
- **db.client.errors** - Error counter with error type classification

## Configuration

To use with a production OTLP collector:

```go
exporter, err := otlpmetricgrpc.New(ctx,
otlpmetricgrpc.WithEndpoint("your-collector:4317"),
otlpmetricgrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(certPool, "")),
)
```

## See Also

- [OpenTelemetry Go SDK](https://opentelemetry.io/docs/languages/go/)
- [OTLP Exporter Documentation](https://opentelemetry.io/docs/specs/otlp/)
- [Redis OTel Native Package](../../extra/redisotel-native/)

39 changes: 39 additions & 0 deletions example/otel-metrics/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module github.com/redis/go-redis/example/otel-metrics

go 1.23.0

toolchain go1.24.2

replace github.com/redis/go-redis/v9 => ../..

replace github.com/redis/go-redis/extra/redisotel-native/v9 => ../../extra/redisotel-native

require (
github.com/redis/go-redis/extra/redisotel-native/v9 v9.0.0-00010101000000-000000000000
github.com/redis/go-redis/v9 v9.7.0
go.opentelemetry.io/otel v1.38.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0
go.opentelemetry.io/otel/sdk/metric v1.38.0
)

require (
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/text v0.28.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/grpc v1.75.0 // indirect
google.golang.org/protobuf v1.36.8 // indirect
)
63 changes: 63 additions & 0 deletions example/otel-metrics/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc=
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
121 changes: 121 additions & 0 deletions example/otel-metrics/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// EXAMPLE: otel_metrics
// HIDE_START
package main

import (
"context"
"log"
"math/rand"
"strconv"
"sync"
"time"

redisotel "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"
)

// ExampleClient_otel_metrics demonstrates how to enable OpenTelemetry metrics
// for Redis operations and export them to an OTLP collector.
func main() {
ctx := context.Background()

// HIDE_END

// STEP_START otel_exporter_setup
// 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 {
log.Fatalf("Failed to create OTLP exporter: %v", err)
}
// STEP_END

// STEP_START otel_meter_provider
// Create meter provider with periodic reader
// Metrics are exported every 10 seconds
meterProvider := metric.NewMeterProvider(
metric.WithReader(
metric.NewPeriodicReader(exporter,
metric.WithInterval(10*time.Second),
),
),
)
defer func() {
if err := meterProvider.Shutdown(ctx); err != nil {
log.Printf("Error shutting down meter provider: %v", err)
}
}()

// Set the global meter provider
otel.SetMeterProvider(meterProvider)
// STEP_END

// STEP_START redis_client_setup
// Create Redis client
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
defer rdb.Close()

// Initialize OTel instrumentation (uses global meter provider)
if err := redisotel.Init(rdb); err != nil {
log.Fatalf("Failed to initialize OTel: %v", err)
}
defer redisotel.Shutdown()
// STEP_END

// STEP_START redis_operations
// Execute Redis operations - metrics are automatically collected
log.Println("Executing Redis operations...")
var wg sync.WaitGroup
wg.Add(50)
for i := range 50 {
go func(i int) {
defer wg.Done()

for j := range 10 {
if err := rdb.Set(ctx, "key"+strconv.Itoa(i*10+j), "value", 0).Err(); err != nil {
log.Printf("Error setting key: %v", err)
}
time.Sleep(time.Millisecond * time.Duration(rand.Intn(400)))
}
}(i)
}
wg.Wait()

wg.Add(10)
for i := range 10 {
go func(i int) {
defer wg.Done()

for j := range 10 {
if err := rdb.Set(ctx, "key"+strconv.Itoa(i*10+j), "value", 0).Err(); err != nil {
log.Printf("Error setting key: %v", err)
}
time.Sleep(time.Millisecond * time.Duration(rand.Intn(400)))
}
}(i)
}
wg.Wait()

for j := range 10 {
if err := rdb.Set(ctx, "key"+strconv.Itoa(j), "value", 0).Err(); err != nil {
log.Printf("Error setting key: %v", err)
}
time.Sleep(time.Millisecond * time.Duration(rand.Intn(400)))
}

log.Println("Operations complete. Waiting for metrics to be exported...")

// Wait for metrics to be exported
time.Sleep(15 * time.Second)
// STEP_END
}
Loading
Loading