From c7c0e8b254a9bf124fab593402c97772d6bff27e Mon Sep 17 00:00:00 2001 From: udhayarajan Date: Mon, 20 Oct 2025 14:51:44 +0530 Subject: [PATCH 1/4] feat(tracing): add process pipeline and dial filtering options for tracing --- extra/redisotel/config.go | 26 ++++++-- extra/redisotel/tracing.go | 12 +++- extra/redisotel/tracing_test.go | 114 ++++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+), 5 deletions(-) diff --git a/extra/redisotel/config.go b/extra/redisotel/config.go index 62b3c9bc28..12cae14d6d 100644 --- a/extra/redisotel/config.go +++ b/extra/redisotel/config.go @@ -22,9 +22,11 @@ type config struct { tp trace.TracerProvider tracer trace.Tracer - dbStmtEnabled bool - callerEnabled bool - filter func(cmd redis.Cmder) bool + dbStmtEnabled bool + callerEnabled bool + filterDial bool + filterProcessPipeline func(cmds []redis.Cmder) bool + filterProcess func(cmd redis.Cmder) bool // Metrics options. @@ -132,7 +134,23 @@ func WithCallerEnabled(on bool) TracingOption { // passwords. func WithCommandFilter(filter func(cmd redis.Cmder) bool) TracingOption { return tracingOption(func(conf *config) { - conf.filter = filter + conf.filterProcess = filter + }) +} + +// WithCommandsFilter allows filtering of pipeline commands +// when tracing to omit commands that may have sensitive details like +// passwords in a pipeline. +func WithCommandsFilter(filter func(cmds []redis.Cmder) bool) TracingOption { + return tracingOption(func(conf *config) { + conf.filterProcessPipeline = filter + }) +} + +// WithDialFilter enables or disables filtering of dial commands. +func WithDialFilter(on bool) TracingOption { + return tracingOption(func(conf *config) { + conf.filterDial = on }) } diff --git a/extra/redisotel/tracing.go b/extra/redisotel/tracing.go index 5c91710c60..a6f361b06e 100644 --- a/extra/redisotel/tracing.go +++ b/extra/redisotel/tracing.go @@ -87,6 +87,11 @@ func newTracingHook(connString string, opts ...TracingOption) *tracingHook { func (th *tracingHook) DialHook(hook redis.DialHook) redis.DialHook { return func(ctx context.Context, network, addr string) (net.Conn, error) { + + if th.conf.filterDial { + return hook(ctx, network, addr) + } + ctx, span := th.conf.tracer.Start(ctx, "redis.dial", th.spanOpts...) defer span.End() @@ -103,7 +108,7 @@ 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 th.conf.filterProcess != nil && th.conf.filterProcess(cmd) { // If so, just call the next hook return hook(ctx, cmd) } @@ -141,6 +146,11 @@ func (th *tracingHook) ProcessPipelineHook( hook redis.ProcessPipelineHook, ) redis.ProcessPipelineHook { return func(ctx context.Context, cmds []redis.Cmder) error { + + if th.conf.filterProcessPipeline != nil && th.conf.filterProcessPipeline(cmds) { + return hook(ctx, cmds) + } + attrs := make([]attribute.KeyValue, 0, 8) attrs = append(attrs, attribute.Int("db.redis.num_cmd", len(cmds)), diff --git a/extra/redisotel/tracing_test.go b/extra/redisotel/tracing_test.go index 0ae70c2d8b..f25b43f8ab 100644 --- a/extra/redisotel/tracing_test.go +++ b/extra/redisotel/tracing_test.go @@ -227,6 +227,120 @@ func TestWithCommandFilter(t *testing.T) { }) } +func TestWithCommandsFilter(t *testing.T) { + t.Run("filter out ping and info commands", func(t *testing.T) { + provider := sdktrace.NewTracerProvider() + hook := newTracingHook( + "", + WithTracerProvider(provider), + WithCommandsFilter(func(cmds []redis.Cmder) bool { + for _, cmd := range cmds { + if cmd.Name() == "ping" || cmd.Name() == "info" { + return true + } + } + return false + }), + ) + + ctx, span := provider.Tracer("redis-test").Start(context.TODO(), "redis-test") + cmds := []redis.Cmder{ + redis.NewCmd(ctx, "ping"), + redis.NewCmd(ctx, "info"), + } + defer span.End() + + processPipelineHook := hook.ProcessPipelineHook(func(ctx context.Context, cmds []redis.Cmder) error { + innerSpan := trace.SpanFromContext(ctx).(sdktrace.ReadOnlySpan) + if innerSpan.Name() != "redis-test" || innerSpan.Name() == "redis.pipeline ping\ninfo" { + t.Fatalf("ping and info commands should not be traced") + } + return nil + }) + err := processPipelineHook(ctx, cmds) + if err != nil { + t.Fatal(err) + } + }) + + t.Run("do not filter ping and info commands", func(t *testing.T) { + provider := sdktrace.NewTracerProvider() + hook := newTracingHook( + "", + WithTracerProvider(provider), + WithCommandsFilter(func(cmds []redis.Cmder) bool { + return false // never filter + }), + ) + ctx, span := provider.Tracer("redis-test").Start(context.TODO(), "redis-test") + cmds := []redis.Cmder{ + redis.NewCmd(ctx, "ping"), + redis.NewCmd(ctx, "info"), + } + defer span.End() + processPipelineHook := hook.ProcessPipelineHook(func(ctx context.Context, cmds []redis.Cmder) error { + innerSpan := trace.SpanFromContext(ctx).(sdktrace.ReadOnlySpan) + if innerSpan.Name() != "redis.pipeline ping info" { + t.Fatalf("ping and info commands should be traced") + } + + return nil + }) + + err := processPipelineHook(ctx, cmds) + if err != nil { + t.Fatal(err) + } + }) +} + +func TestWithDialFilter(t *testing.T) { + t.Run("filter out dial", func(t *testing.T) { + provider := sdktrace.NewTracerProvider() + hook := newTracingHook( + "", + WithTracerProvider(provider), + WithDialFilter(true), + ) + ctx, span := provider.Tracer("redis-test").Start(context.TODO(), "redis-test") + defer span.End() + dialHook := hook.DialHook(func(ctx context.Context, network, addr string) (conn net.Conn, err error) { + innerSpan := trace.SpanFromContext(ctx).(sdktrace.ReadOnlySpan) + if innerSpan.Name() == "redis.dial" { + t.Fatalf("dial should not be traced") + } + return nil, nil + }) + + _, err := dialHook(ctx, "tcp", "localhost:6379") + if err != nil { + t.Fatal(err) + } + }) + + t.Run("do not filter dial", func(t *testing.T) { + provider := sdktrace.NewTracerProvider() + hook := newTracingHook( + "", + WithTracerProvider(provider), + WithDialFilter(false), + ) + ctx, span := provider.Tracer("redis-test").Start(context.TODO(), "redis-test") + defer span.End() + dialHook := hook.DialHook(func(ctx context.Context, network, addr string) (conn net.Conn, err error) { + innerSpan := trace.SpanFromContext(ctx).(sdktrace.ReadOnlySpan) + if innerSpan.Name() != "redis.dial" { + t.Fatalf("dial should be traced") + } + return nil, nil + }) + _, err := dialHook(ctx, "tcp", "localhost:6379") + if err != nil { + t.Fatal(err) + } + }) +} + func TestTracingHook_DialHook(t *testing.T) { imsb := tracetest.NewInMemoryExporter() provider := sdktrace.NewTracerProvider(sdktrace.WithSyncer(imsb)) From 22fe2a448542ddcbcc13cda7046c5d512c5d6d61 Mon Sep 17 00:00:00 2001 From: udhayarajan Date: Tue, 21 Oct 2025 22:44:50 +0530 Subject: [PATCH 2/4] refactor(tracing): implement default command filter and process pipeline options --- extra/redisotel/config.go | 11 ++++++++++- extra/redisotel/tracing_test.go | 6 +++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/extra/redisotel/config.go b/extra/redisotel/config.go index 12cae14d6d..b5250bb73e 100644 --- a/extra/redisotel/config.go +++ b/extra/redisotel/config.go @@ -67,6 +67,15 @@ func newConfig(opts ...baseOption) *config { mp: otel.GetMeterProvider(), dbStmtEnabled: true, callerEnabled: true, + filterProcess: defaultCommandFilter, + filterProcessPipeline: func(cmds []redis.Cmder) bool { + for _, cmd := range cmds { + if defaultCommandFilter(cmd) { + return true + } + } + return false + }, } for _, opt := range opts { @@ -154,7 +163,7 @@ func WithDialFilter(on bool) TracingOption { }) } -func BasicCommandFilter(cmd redis.Cmder) bool { +func defaultCommandFilter(cmd redis.Cmder) bool { if strings.ToLower(cmd.Name()) == "auth" { return true } diff --git a/extra/redisotel/tracing_test.go b/extra/redisotel/tracing_test.go index f25b43f8ab..fc65abc807 100644 --- a/extra/redisotel/tracing_test.go +++ b/extra/redisotel/tracing_test.go @@ -156,7 +156,7 @@ func TestWithCommandFilter(t *testing.T) { hook := newTracingHook( "", WithTracerProvider(provider), - WithCommandFilter(BasicCommandFilter), + WithCommandFilter(defaultCommandFilter), ) ctx, span := provider.Tracer("redis-test").Start(context.TODO(), "redis-test") cmd := redis.NewCmd(ctx, "auth", "test-password") @@ -181,7 +181,7 @@ func TestWithCommandFilter(t *testing.T) { hook := newTracingHook( "", WithTracerProvider(provider), - WithCommandFilter(BasicCommandFilter), + WithCommandFilter(defaultCommandFilter), ) ctx, span := provider.Tracer("redis-test").Start(context.TODO(), "redis-test") cmd := redis.NewCmd(ctx, "hello", 3, "AUTH", "test-user", "test-password") @@ -206,7 +206,7 @@ func TestWithCommandFilter(t *testing.T) { hook := newTracingHook( "", WithTracerProvider(provider), - WithCommandFilter(BasicCommandFilter), + WithCommandFilter(defaultCommandFilter), ) ctx, span := provider.Tracer("redis-test").Start(context.TODO(), "redis-test") cmd := redis.NewCmd(ctx, "hello", 3) From 519e9f164dc008749f22209db118d2a4a4a171bb Mon Sep 17 00:00:00 2001 From: udhayarajan Date: Tue, 21 Oct 2025 23:03:36 +0530 Subject: [PATCH 3/4] refactor(tracing): rename defaultCommandFilter to DefaultCommandFilter --- extra/redisotel/config.go | 7 ++++--- extra/redisotel/tracing_test.go | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/extra/redisotel/config.go b/extra/redisotel/config.go index b5250bb73e..6dc3b78b82 100644 --- a/extra/redisotel/config.go +++ b/extra/redisotel/config.go @@ -67,10 +67,10 @@ func newConfig(opts ...baseOption) *config { mp: otel.GetMeterProvider(), dbStmtEnabled: true, callerEnabled: true, - filterProcess: defaultCommandFilter, + filterProcess: DefaultCommandFilter, filterProcessPipeline: func(cmds []redis.Cmder) bool { for _, cmd := range cmds { - if defaultCommandFilter(cmd) { + if DefaultCommandFilter(cmd) { return true } } @@ -163,7 +163,8 @@ func WithDialFilter(on bool) TracingOption { }) } -func defaultCommandFilter(cmd redis.Cmder) bool { +// DefaultCommandFilter filters out AUTH commands from tracing. +func DefaultCommandFilter(cmd redis.Cmder) bool { if strings.ToLower(cmd.Name()) == "auth" { return true } diff --git a/extra/redisotel/tracing_test.go b/extra/redisotel/tracing_test.go index fc65abc807..96c2aff835 100644 --- a/extra/redisotel/tracing_test.go +++ b/extra/redisotel/tracing_test.go @@ -156,7 +156,7 @@ func TestWithCommandFilter(t *testing.T) { hook := newTracingHook( "", WithTracerProvider(provider), - WithCommandFilter(defaultCommandFilter), + WithCommandFilter(DefaultCommandFilter), ) ctx, span := provider.Tracer("redis-test").Start(context.TODO(), "redis-test") cmd := redis.NewCmd(ctx, "auth", "test-password") @@ -181,7 +181,7 @@ func TestWithCommandFilter(t *testing.T) { hook := newTracingHook( "", WithTracerProvider(provider), - WithCommandFilter(defaultCommandFilter), + WithCommandFilter(DefaultCommandFilter), ) ctx, span := provider.Tracer("redis-test").Start(context.TODO(), "redis-test") cmd := redis.NewCmd(ctx, "hello", 3, "AUTH", "test-user", "test-password") @@ -206,7 +206,7 @@ func TestWithCommandFilter(t *testing.T) { hook := newTracingHook( "", WithTracerProvider(provider), - WithCommandFilter(defaultCommandFilter), + WithCommandFilter(DefaultCommandFilter), ) ctx, span := provider.Tracer("redis-test").Start(context.TODO(), "redis-test") cmd := redis.NewCmd(ctx, "hello", 3) From 7d8103c5b43dce3ed7dd7bd2fbd7f494ae3ef0e9 Mon Sep 17 00:00:00 2001 From: udhayarajan Date: Wed, 22 Oct 2025 14:15:21 +0530 Subject: [PATCH 4/4] refactor(tracing): add BasicCommandFilter as a deprecated alias for DefaultCommandFilter --- extra/redisotel/config.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/extra/redisotel/config.go b/extra/redisotel/config.go index 6dc3b78b82..b9311beafa 100644 --- a/extra/redisotel/config.go +++ b/extra/redisotel/config.go @@ -187,6 +187,12 @@ func DefaultCommandFilter(cmd redis.Cmder) bool { return false } +// BasicCommandFilter filters out AUTH commands from tracing. +// Deprecated: use DefaultCommandFilter instead. +func BasicCommandFilter(cmd redis.Cmder) bool { + return DefaultCommandFilter(cmd) +} + //------------------------------------------------------------------------------ type MetricsOption interface {