Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
36 changes: 36 additions & 0 deletions extra/redisotel/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package redisotel

import (
"strings"

"github.com/redis/go-redis/v9"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
Expand All @@ -21,6 +24,7 @@ type config struct {

dbStmtEnabled bool
callerEnabled bool
filter func(cmd redis.Cmder) bool

// Metrics options.

Expand Down Expand Up @@ -61,6 +65,7 @@ func newConfig(opts ...baseOption) *config {
mp: otel.GetMeterProvider(),
dbStmtEnabled: true,
callerEnabled: true,
filter: defaultCommandFilter,
}

for _, opt := range opts {
Expand Down Expand Up @@ -124,6 +129,37 @@ func WithCallerEnabled(on bool) TracingOption {
})
}

// WithCommandFilter allows filtering of commands when tracing to omit commands that may have sensitive details like
// passwords.
func WithCommandFilter(filter func(cmd redis.Cmder) bool) TracingOption {
return tracingOption(func(conf *config) {
conf.filter = filter
})
}

func defaultCommandFilter(cmd redis.Cmder) bool {
if strings.ToLower(cmd.Name()) == "auth" {
return true
}

if strings.ToLower(cmd.Name()) == "hello" {
if len(cmd.Args()) < 3 {
return false
}

arg, exists := cmd.Args()[2].(string)
if !exists {
return false
}

if strings.ToLower(arg) == "auth" {
return true
}
}

return false
}

//------------------------------------------------------------------------------

type MetricsOption interface {
Expand Down
6 changes: 6 additions & 0 deletions extra/redisotel/tracing.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ func (th *tracingHook) DialHook(hook redis.DialHook) redis.DialHook {
func (th *tracingHook) ProcessHook(hook redis.ProcessHook) redis.ProcessHook {
return func(ctx context.Context, cmd redis.Cmder) error {

// Check if the command should be filtered out
if th.conf.filter != nil && th.conf.filter(cmd) {
// If so, just call the next hook
return hook(ctx, cmd)
}

attrs := make([]attribute.KeyValue, 0, 8)
if th.conf.callerEnabled {
fn, file, line := funcFileLine("github.com/redis/go-redis")
Expand Down
129 changes: 129 additions & 0 deletions extra/redisotel/tracing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,135 @@ func TestWithoutCaller(t *testing.T) {
}
}

func TestWithCommandFilter(t *testing.T) {

t.Run("filter out ping command", func(t *testing.T) {
provider := sdktrace.NewTracerProvider()
hook := newTracingHook(
"",
WithTracerProvider(provider),
WithCommandFilter(func(cmd redis.Cmder) bool {
return cmd.Name() == "ping"
}),
)
ctx, span := provider.Tracer("redis-test").Start(context.TODO(), "redis-test")
cmd := redis.NewCmd(ctx, "ping")
defer span.End()

processHook := hook.ProcessHook(func(ctx context.Context, cmd redis.Cmder) error {
innerSpan := trace.SpanFromContext(ctx).(sdktrace.ReadOnlySpan)
if innerSpan.Name() != "redis-test" || innerSpan.Name() == "ping" {
t.Fatalf("ping command should not be traced")
}

return nil
})
err := processHook(ctx, cmd)
if err != nil {
t.Fatal(err)
}
})

t.Run("do not filter ping command", func(t *testing.T) {
provider := sdktrace.NewTracerProvider()
hook := newTracingHook(
"",
WithTracerProvider(provider),
WithCommandFilter(func(cmd redis.Cmder) bool {
return false // never filter
}),
)
ctx, span := provider.Tracer("redis-test").Start(context.TODO(), "redis-test")
cmd := redis.NewCmd(ctx, "ping")
defer span.End()

processHook := hook.ProcessHook(func(ctx context.Context, cmd redis.Cmder) error {
innerSpan := trace.SpanFromContext(ctx).(sdktrace.ReadOnlySpan)
if innerSpan.Name() != "ping" {
t.Fatalf("ping command should be traced")
}

return nil
})
err := processHook(ctx, cmd)
if err != nil {
t.Fatal(err)
}
})

t.Run("auth command filtered by default", func(t *testing.T) {
provider := sdktrace.NewTracerProvider()
hook := newTracingHook(
"",
WithTracerProvider(provider),
)
ctx, span := provider.Tracer("redis-test").Start(context.TODO(), "redis-test")
cmd := redis.NewCmd(ctx, "auth", "test-password")
defer span.End()

processHook := hook.ProcessHook(func(ctx context.Context, cmd redis.Cmder) error {
innerSpan := trace.SpanFromContext(ctx).(sdktrace.ReadOnlySpan)
if innerSpan.Name() != "redis-test" || innerSpan.Name() == "auth" {
t.Fatalf("auth command should not be traced by default")
}

return nil
})
err := processHook(ctx, cmd)
if err != nil {
t.Fatal(err)
}
})

t.Run("hello command filtered by default when sensitive", func(t *testing.T) {
provider := sdktrace.NewTracerProvider()
hook := newTracingHook(
"",
WithTracerProvider(provider),
)
ctx, span := provider.Tracer("redis-test").Start(context.TODO(), "redis-test")
cmd := redis.NewCmd(ctx, "hello", 3, "AUTH", "test-user", "test-password")
defer span.End()

processHook := hook.ProcessHook(func(ctx context.Context, cmd redis.Cmder) error {
innerSpan := trace.SpanFromContext(ctx).(sdktrace.ReadOnlySpan)
if innerSpan.Name() != "redis-test" || innerSpan.Name() == "hello" {
t.Fatalf("auth command should not be traced by default")
}

return nil
})
err := processHook(ctx, cmd)
if err != nil {
t.Fatal(err)
}
})

t.Run("hello command not filtered by default when not sensitive", func(t *testing.T) {
provider := sdktrace.NewTracerProvider()
hook := newTracingHook(
"",
WithTracerProvider(provider),
)
ctx, span := provider.Tracer("redis-test").Start(context.TODO(), "redis-test")
cmd := redis.NewCmd(ctx, "hello", 3)
defer span.End()

processHook := hook.ProcessHook(func(ctx context.Context, cmd redis.Cmder) error {
innerSpan := trace.SpanFromContext(ctx).(sdktrace.ReadOnlySpan)
if innerSpan.Name() != "hello" {
t.Fatalf("hello command should be traced")
}

return nil
})
err := processHook(ctx, cmd)
if err != nil {
t.Fatal(err)
}
})
}

func TestTracingHook_DialHook(t *testing.T) {
imsb := tracetest.NewInMemoryExporter()
provider := sdktrace.NewTracerProvider(sdktrace.WithSyncer(imsb))
Expand Down
Loading