From 6aa8d693a960a5308dc5889437b8eef073a05b21 Mon Sep 17 00:00:00 2001 From: keisku Date: Sun, 12 Jan 2025 06:51:26 +0000 Subject: [PATCH 01/24] Support valkey-go tracing --- .github/workflows/unit-integration-tests.yml | 6 + contrib/valkey-go/example_test.go | 40 +++ contrib/valkey-go/option.go | 83 +++++ contrib/valkey-go/valkey.go | 347 +++++++++++++++++++ contrib/valkey-go/valkey_test.go | 311 +++++++++++++++++ ddtrace/ext/app_types.go | 3 + ddtrace/ext/db.go | 40 +++ docker-compose.yaml | 6 + go.mod | 3 +- go.sum | 10 +- internal/exectracetest/go.mod | 2 +- internal/exectracetest/go.sum | 4 +- internal/namingschema/op.go | 5 + 13 files changed, 852 insertions(+), 8 deletions(-) create mode 100644 contrib/valkey-go/example_test.go create mode 100644 contrib/valkey-go/option.go create mode 100644 contrib/valkey-go/valkey.go create mode 100644 contrib/valkey-go/valkey_test.go diff --git a/.github/workflows/unit-integration-tests.yml b/.github/workflows/unit-integration-tests.yml index 760d9ef40e..3bd7454759 100644 --- a/.github/workflows/unit-integration-tests.yml +++ b/.github/workflows/unit-integration-tests.yml @@ -134,6 +134,12 @@ jobs: image: redis:3.2 ports: - 6379:6379 + valkey: + image: valkey/valkey:8 + ports: + - "6380:6380" + # https://valkey.io/topics/acl/ + command: [ "valkey-server", "--port", "6380", "--requirepass", "password-for-default" ] elasticsearch2: image: elasticsearch:2 env: diff --git a/contrib/valkey-go/example_test.go b/contrib/valkey-go/example_test.go new file mode 100644 index 0000000000..f9105ea894 --- /dev/null +++ b/contrib/valkey-go/example_test.go @@ -0,0 +1,40 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016 Datadog, Inc. + +package valkey_test + +import ( + "context" + "log/slog" + + "github.com/valkey-io/valkey-go" + valkeytrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/valkey-go" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" +) + +// To start tracing Valkey, simply create a new client using the library and continue +// using as you normally would. +func Example() { + vk, err := valkeytrace.NewClient(valkey.ClientOption{ + InitAddress: []string{"localhost:6379"}, + }) + if err != nil { + slog.Error(err.Error()) + return + } + + span, ctx := tracer.StartSpanFromContext(context.Background(), "parent.request", + tracer.SpanType(ext.SpanTypeValkey), + tracer.ServiceName("web"), + tracer.ResourceName("/home"), + ) + + if err := vk.Do(ctx, vk.B().Set().Key("key").Value("value").Build()).Error(); err != nil { + slog.ErrorContext(ctx, "set a value", slog.Any("error", err)) + } + + span.Finish() +} diff --git a/contrib/valkey-go/option.go b/contrib/valkey-go/option.go new file mode 100644 index 0000000000..21b16498ae --- /dev/null +++ b/contrib/valkey-go/option.go @@ -0,0 +1,83 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016 Datadog, Inc. + +// Package redis provides tracing functions for tracing the go-redis/redis package (https://github.com/go-redis/redis). +// This package supports versions up to go-redis 6.15. +package valkey + +import ( + "math" + "os" + + "gopkg.in/DataDog/dd-trace-go.v1/internal" + "gopkg.in/DataDog/dd-trace-go.v1/internal/namingschema" +) + +const defaultServiceName = "valkey.client" + +type clientConfig struct { + serviceName string + spanName string + analyticsRate float64 + skipRaw bool +} + +// ClientOption represents an option that can be used to create or wrap a client. +type ClientOption func(*clientConfig) + +func defaults(cfg *clientConfig) { + cfg.serviceName = namingschema.ServiceNameOverrideV0(defaultServiceName, defaultServiceName) + cfg.spanName = namingschema.OpName(namingschema.ValkeyOutbound) + if internal.BoolEnv("DD_TRACE_VALKEY_ANALYTICS_ENABLED", false) { + cfg.analyticsRate = 1.0 + } else { + cfg.analyticsRate = math.NaN() + } + if v := os.Getenv("DD_TRACE_VALKEY_SERVICE_NAME"); v == "" { + cfg.serviceName = defaultServiceName + } else { + cfg.serviceName = v + } + cfg.skipRaw = internal.BoolEnv("DD_TRACE_VALKEY_SKIP_RAW_COMMAND", false) +} + +// WithSkipRawCommand reports whether to skip setting the raw command value +// on instrumenation spans. This may be useful if the Datadog Agent is not +// set up to obfuscate this value and it could contain sensitive information. +func WithSkipRawCommand(skip bool) ClientOption { + return func(cfg *clientConfig) { + cfg.skipRaw = skip + } +} + +// WithServiceName sets the given service name for the client. +func WithServiceName(name string) ClientOption { + return func(cfg *clientConfig) { + cfg.serviceName = name + } +} + +// WithAnalytics enables Trace Analytics for all started spans. +func WithAnalytics(on bool) ClientOption { + return func(cfg *clientConfig) { + if on { + cfg.analyticsRate = 1.0 + } else { + cfg.analyticsRate = math.NaN() + } + } +} + +// WithAnalyticsRate sets the sampling rate for Trace Analytics events +// correlated to started spans. +func WithAnalyticsRate(rate float64) ClientOption { + return func(cfg *clientConfig) { + if rate >= 0.0 && rate <= 1.0 { + cfg.analyticsRate = rate + } else { + cfg.analyticsRate = math.NaN() + } + } +} diff --git a/contrib/valkey-go/valkey.go b/contrib/valkey-go/valkey.go new file mode 100644 index 0000000000..f470c01a32 --- /dev/null +++ b/contrib/valkey-go/valkey.go @@ -0,0 +1,347 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016 Datadog, Inc. + +// Package redis provides tracing functions for tracing the go-redis/redis package (https://github.com/go-redis/redis). +// This package supports versions up to go-redis 6.15. +package valkey + +import ( + "context" + "math" + "net" + "strconv" + "strings" + "time" + + "github.com/valkey-io/valkey-go" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" + "gopkg.in/DataDog/dd-trace-go.v1/internal/log" + "gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry" +) + +const componentName = "valkey-go/valkey" + +func init() { + telemetry.LoadIntegration(componentName) + tracer.MarkIntegrationImported("github.com/valkey/valkey-go") +} + +var ( + _ valkey.CoreClient = (*coreClient)(nil) + _ valkey.Client = (*client)(nil) + _ valkey.DedicatedClient = (*dedicatedClient)(nil) +) + +type coreClient struct { + valkey.Client + option valkey.ClientOption + clientConfig clientConfig + host string + port int +} + +type client struct { + coreClient +} + +type dedicatedClient struct { + coreClient + dedicatedClient valkey.DedicatedClient +} + +func NewClient(option valkey.ClientOption, opts ...ClientOption) (valkey.Client, error) { + valkeyClient, err := valkey.NewClient(option) + if err != nil { + return nil, err + } + var cfg clientConfig + defaults(&cfg) + for _, fn := range opts { + fn(&cfg) + } + var host, portStr string + var port int + if len(option.InitAddress) == 1 { + host, portStr, err = net.SplitHostPort(option.InitAddress[0]) + if err != nil { + log.Error("valkey.ClientOption.InitAddress contains invalid address: %s", err) + } + port, _ = strconv.Atoi(portStr) + } + core := coreClient{ + Client: valkeyClient, + option: option, + clientConfig: cfg, + host: host, + port: port, + } + return &client{ + coreClient: core, + }, nil +} + +type commander interface { + Commands() []string +} + +func processCmd(commander commander) (command, statement string, size int) { + commands := commander.Commands() + if len(commands) == 0 { + return "", "", 0 + } + command = commands[0] + statement = strings.Join(commands, "\n") + return command, statement, len(statement) +} + +func processMultiCmds(multi []commander) (command, statement string, size int) { + var commands []string + var statements []string + for _, cmd := range multi { + cmdStr, stmt, cmdSize := processCmd(cmd) + size += cmdSize + commands = append(commands, cmdStr) + statements = append(statements, stmt) + } + command = strings.Join(commands, " ") + statement = strings.Join(statements, "\n") + return command, statement, size +} + +func processMultiCompleted(multi ...valkey.Completed) (command, statement string, size int) { + cmds := make([]commander, len(multi)) + for i, cmd := range multi { + cmds[i] = &cmd + } + return processMultiCmds(cmds) +} + +func processMultiCacheableTTL(multi ...valkey.CacheableTTL) (command, statement string, size int) { + cmds := make([]commander, len(multi)) + for i, cmd := range multi { + cmds[i] = &cmd.Cmd + } + return processMultiCmds(cmds) +} + +func firstError(s []valkey.ValkeyResult) error { + for _, result := range s { + if err := result.Error(); err != nil && !valkey.IsValkeyNil(err) { + return err + } + } + return nil +} + +func setClientCacheTags(s tracer.Span, result valkey.ValkeyResult) { + s.SetTag(ext.ValkeyClientCacheHit, result.IsCacheHit()) + s.SetTag(ext.ValkeyClientCacheTTL, result.CacheTTL()) + s.SetTag(ext.ValkeyClientCachePTTL, result.CachePTTL()) + s.SetTag(ext.ValkeyClientCachePXAT, result.CachePXAT()) +} + +type buildStartSpanOptionsInput struct { + command string + statement string + size int + isWrite bool + isBlock bool + isMulti bool + isStream bool + skipRawCommand bool +} + +func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []tracer.StartSpanOption { + opts := []tracer.StartSpanOption{ + tracer.SpanType(ext.SpanTypeValkey), + tracer.ServiceName(c.clientConfig.serviceName), + tracer.Tag(ext.TargetHost, c.host), + tracer.Tag(ext.TargetPort, c.port), + tracer.Tag(ext.ValkeyClientVersion, valkey.LibVer), + tracer.Tag(ext.ValkeyClientName, valkey.LibName), + tracer.Tag(ext.Component, componentName), + tracer.Tag(ext.SpanKind, ext.SpanKindClient), + tracer.Tag(ext.DBType, ext.DBSystemValkey), + tracer.Tag(ext.DBSystem, ext.DBSystemValkey), + tracer.Tag(ext.ValkeyDatabaseIndex, c.option.SelectDB), + tracer.Tag(ext.ValkeyClientCommandWrite, input.isWrite), + tracer.Tag(ext.ValkeyClientCommandBlock, input.isBlock), + tracer.Tag(ext.ValkeyClientCommandMulti, input.isMulti), + tracer.Tag(ext.ValkeyClientCommandStream, input.isStream), + tracer.Tag(ext.ValkeyClientCommandWithPassword, c.option.Password != ""), + } + if input.command != "" { + opts = append(opts, []tracer.StartSpanOption{ + // valkeyotel tags + tracer.Tag("db.stmt_size", input.size), + tracer.Tag("db.operation", input.command), + }...) + if input.skipRawCommand { + opts = append(opts, tracer.ResourceName(input.command)) + opts = append(opts, tracer.Tag(ext.DBStatement, input.command)) + } else { + opts = append(opts, tracer.ResourceName(input.statement)) + opts = append(opts, tracer.Tag(ext.DBStatement, input.statement)) + } + } + if c.option.ClientName != "" { + opts = append(opts, tracer.Tag(ext.DBApplication, c.option.ClientName)) + } + if c.option.Username != "" { + opts = append(opts, tracer.Tag(ext.DBUser, c.option.Username)) + } + if !math.IsNaN(c.clientConfig.analyticsRate) { + opts = append(opts, tracer.Tag(ext.EventSampleRate, c.clientConfig.analyticsRate)) + } + return opts +} + +func (c *coreClient) Do(ctx context.Context, cmd valkey.Completed) (resp valkey.ValkeyResult) { + command, statement, size := processCmd(&cmd) + span, ctx := tracer.StartSpanFromContext(ctx, c.clientConfig.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ + command: command, + statement: statement, + size: size, + isWrite: cmd.IsWrite(), + isBlock: cmd.IsBlock(), + skipRawCommand: c.clientConfig.skipRaw, + })...) + resp = c.Client.Do(ctx, cmd) + setClientCacheTags(span, resp) + defer span.Finish(tracer.WithError(resp.Error())) + return resp +} + +func (c *coreClient) DoMulti(ctx context.Context, multi ...valkey.Completed) (resp []valkey.ValkeyResult) { + command, statement, size := processMultiCompleted(multi...) + span, ctx := tracer.StartSpanFromContext(ctx, c.clientConfig.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ + command: command, + statement: statement, + size: size, + isMulti: true, + skipRawCommand: c.clientConfig.skipRaw, + })...) + resp = c.Client.DoMulti(ctx, multi...) + defer span.Finish(tracer.WithError(firstError(resp))) + return resp +} + +func (c *coreClient) Receive(ctx context.Context, subscribe valkey.Completed, fn func(msg valkey.PubSubMessage)) (err error) { + command, statement, size := processCmd(&subscribe) + span, ctx := tracer.StartSpanFromContext(ctx, c.clientConfig.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ + command: command, + statement: statement, + size: size, + isWrite: subscribe.IsWrite(), + isBlock: subscribe.IsBlock(), + skipRawCommand: c.clientConfig.skipRaw, + })...) + err = c.Client.Receive(ctx, subscribe, fn) + defer span.Finish(tracer.WithError(err)) + return err +} + +func (c *client) DoCache(ctx context.Context, cmd valkey.Cacheable, ttl time.Duration) (resp valkey.ValkeyResult) { + command, statement, size := processCmd(&cmd) + span, ctx := tracer.StartSpanFromContext(ctx, c.clientConfig.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ + command: command, + statement: statement, + size: size, + isMulti: cmd.IsMGet(), + skipRawCommand: c.clientConfig.skipRaw, + })...) + resp = c.Client.DoCache(ctx, cmd, ttl) + setClientCacheTags(span, resp) + defer span.Finish(tracer.WithError(resp.Error())) + return resp +} + +func (c *client) DoMultiCache(ctx context.Context, multi ...valkey.CacheableTTL) (resp []valkey.ValkeyResult) { + command, statement, size := processMultiCacheableTTL(multi...) + span, ctx := tracer.StartSpanFromContext(ctx, c.clientConfig.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ + command: command, + statement: statement, + size: size, + isWrite: true, + skipRawCommand: c.clientConfig.skipRaw, + })...) + resp = c.Client.DoMultiCache(ctx, multi...) + defer span.Finish(tracer.WithError(firstError(resp))) + return resp +} + +func (c *client) DoStream(ctx context.Context, cmd valkey.Completed) (resp valkey.ValkeyResultStream) { + command, statement, size := processCmd(&cmd) + span, ctx := tracer.StartSpanFromContext(ctx, c.clientConfig.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ + command: command, + statement: statement, + size: size, + isWrite: cmd.IsWrite(), + isBlock: cmd.IsBlock(), + isStream: true, + skipRawCommand: c.clientConfig.skipRaw, + })...) + resp = c.Client.DoStream(ctx, cmd) + defer span.Finish(tracer.WithError(resp.Error())) + return resp +} + +func (c *client) DoMultiStream(ctx context.Context, multi ...valkey.Completed) (resp valkey.MultiValkeyResultStream) { + command, statement, size := processMultiCompleted(multi...) + span, ctx := tracer.StartSpanFromContext(ctx, c.clientConfig.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ + command: command, + statement: statement, + size: size, + isMulti: true, + isStream: true, + skipRawCommand: c.clientConfig.skipRaw, + })...) + resp = c.Client.DoMultiStream(ctx, multi...) + defer span.Finish(tracer.WithError(resp.Error())) + return resp +} + +func (c *client) Dedicated(fn func(valkey.DedicatedClient) error) (err error) { + return c.Client.Dedicated(func(dc valkey.DedicatedClient) error { + return fn(&dedicatedClient{ + coreClient: c.coreClient, + dedicatedClient: dc, + }) + }) +} + +func (c *client) Dedicate() (client valkey.DedicatedClient, cancel func()) { + dedicated, cancel := c.coreClient.Client.Dedicate() + return &dedicatedClient{ + coreClient: c.coreClient, + dedicatedClient: dedicated, + }, cancel +} + +func (c *client) Nodes() map[string]valkey.Client { + nodes := c.Client.Nodes() + for addr, valkeyClient := range nodes { + host, portStr, err := net.SplitHostPort(addr) + if err != nil { + log.Error("invalid address is set to valkey client: %s", err) + } + port, _ := strconv.Atoi(portStr) + nodes[addr] = &client{ + coreClient: coreClient{ + Client: valkeyClient, + option: c.option, + clientConfig: c.clientConfig, + host: host, + port: port, + }, + } + } + return nodes +} + +func (c *dedicatedClient) SetPubSubHooks(hooks valkey.PubSubHooks) <-chan error { + return c.dedicatedClient.SetPubSubHooks(hooks) +} diff --git a/contrib/valkey-go/valkey_test.go b/contrib/valkey-go/valkey_test.go new file mode 100644 index 0000000000..10329a4e92 --- /dev/null +++ b/contrib/valkey-go/valkey_test.go @@ -0,0 +1,311 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016 Datadog, Inc. +package valkey_test + +import ( + "context" + "fmt" + "os" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/valkey-io/valkey-go" + valkeytrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/valkey-go" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/mocktracer" +) + +var ( + // See docker-compose.yaml + valkeyPort = 6380 + valkeyUsername = "default" + valkeyPassword = "password-for-default" +) + +func TestMain(m *testing.M) { + _, ok := os.LookupEnv("INTEGRATION") + if !ok { + fmt.Println("--- SKIP: to enable integration test, set the INTEGRATION environment variable") + os.Exit(0) + } + os.Exit(m.Run()) +} + +func TestNewClient(t *testing.T) { + tests := []struct { + name string + valkeyClientOptions valkey.ClientOption + valkeytraceClientOptions []valkeytrace.ClientOption + valkeytraceClientEnvVars map[string]string + createSpans func(*testing.T, context.Context, valkey.Client) + assertNewClientError func(*testing.T, error) + assertSpans []func(*testing.T, mocktracer.Span) + }{ + { + name: "Test invalid username", + valkeyClientOptions: valkey.ClientOption{ + InitAddress: []string{fmt.Sprintf("127.0.0.1:%d", valkeyPort)}, + Username: "invalid-username", + Password: valkeyPassword, + }, + assertNewClientError: func(t *testing.T, err error) { + assert.EqualError(t, err, "WRONGPASS invalid username-password pair or user is disabled.") + }, + }, + { + name: "Test invalid password", + valkeyClientOptions: valkey.ClientOption{ + InitAddress: []string{fmt.Sprintf("127.0.0.1:%d", valkeyPort)}, + Username: valkeyUsername, + Password: "invalid", + }, + assertNewClientError: func(t *testing.T, err error) { + assert.EqualError(t, err, "WRONGPASS invalid username-password pair or user is disabled.") + }, + }, + { + name: "Test SET command with custom options", + valkeyClientOptions: valkey.ClientOption{ + InitAddress: []string{fmt.Sprintf("127.0.0.1:%d", valkeyPort)}, + Username: valkeyUsername, + Password: valkeyPassword, + }, + valkeytraceClientOptions: []valkeytrace.ClientOption{ + valkeytrace.WithServiceName("my-valkey-client"), + valkeytrace.WithAnalytics(true), + valkeytrace.WithSkipRawCommand(true), + }, + createSpans: func(t *testing.T, ctx context.Context, client valkey.Client) { + assert.NoError(t, client.Do(ctx, client.B().Set().Key("test_key").Value("test_value").Build()).Error()) + }, + assertSpans: []func(t *testing.T, span mocktracer.Span){ + func(t *testing.T, span mocktracer.Span) { + assert.Equal(t, "my-valkey-client", span.Tag(ext.ServiceName)) + assert.Equal(t, "127.0.0.1", span.Tag(ext.TargetHost)) + assert.Equal(t, valkeyPort, span.Tag(ext.TargetPort)) + assert.Equal(t, "SET", span.Tag(ext.DBStatement)) + assert.Equal(t, "SET", span.Tag(ext.ResourceName)) + assert.Greater(t, span.Tag("db.stmt_size"), 0) + assert.Equal(t, "SET", span.Tag("db.operation")) + assert.True(t, span.Tag(ext.ValkeyClientCommandWrite).(bool)) + assert.False(t, span.Tag(ext.ValkeyClientCommandStream).(bool)) + assert.False(t, span.Tag(ext.ValkeyClientCommandBlock).(bool)) + assert.False(t, span.Tag(ext.ValkeyClientCommandMulti).(bool)) + assert.False(t, span.Tag(ext.ValkeyClientCacheHit).(bool)) + assert.Less(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) + assert.Less(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) + assert.Less(t, span.Tag(ext.ValkeyClientCachePTTL), int64(0)) + assert.Nil(t, span.Tag(ext.DBApplication)) + assert.Equal(t, 1.0, span.Tag(ext.EventSampleRate)) + assert.Nil(t, span.Tag(ext.Error)) + }, + }, + }, + { + name: "Test SET/GET commands", + valkeyClientOptions: valkey.ClientOption{ + InitAddress: []string{fmt.Sprintf("127.0.0.1:%d", valkeyPort)}, + Username: valkeyUsername, + Password: valkeyPassword, + ClientName: "my-valkey-client", + }, + createSpans: func(t *testing.T, ctx context.Context, client valkey.Client) { + resp := client.DoMulti(ctx, client.B().Set().Key("test_key").Value("test_value").Build(), client.B().Get().Key("test_key").Build()) + assert.Len(t, resp, 2) + }, + assertSpans: []func(t *testing.T, span mocktracer.Span){ + func(t *testing.T, span mocktracer.Span) { + assert.Equal(t, "valkey.client", span.Tag(ext.ServiceName)) + assert.Equal(t, "127.0.0.1", span.Tag(ext.TargetHost)) + assert.Equal(t, valkeyPort, span.Tag(ext.TargetPort)) + assert.Equal(t, "SET\ntest_key\ntest_value\nGET\ntest_key", span.Tag(ext.DBStatement)) + assert.Equal(t, "SET\ntest_key\ntest_value\nGET\ntest_key", span.Tag(ext.ResourceName)) + assert.Greater(t, span.Tag("db.stmt_size"), 0) + assert.Equal(t, "SET GET", span.Tag("db.operation")) + assert.False(t, span.Tag(ext.ValkeyClientCommandWrite).(bool)) + assert.False(t, span.Tag(ext.ValkeyClientCommandStream).(bool)) + assert.False(t, span.Tag(ext.ValkeyClientCommandBlock).(bool)) + assert.True(t, span.Tag(ext.ValkeyClientCommandMulti).(bool)) + assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) + assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) + assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) + assert.Nil(t, span.Tag(ext.ValkeyClientCachePTTL)) + assert.Equal(t, "my-valkey-client", span.Tag(ext.DBApplication)) + assert.Nil(t, span.Tag(ext.EventSampleRate)) + assert.Nil(t, span.Tag(ext.Error)) + }, + }, + }, + { + name: "Test HMGET command with cache", + valkeyClientOptions: valkey.ClientOption{ + InitAddress: []string{fmt.Sprintf("127.0.0.1:%d", valkeyPort)}, + Username: valkeyUsername, + Password: valkeyPassword, + }, + createSpans: func(t *testing.T, ctx context.Context, client valkey.Client) { + assert.NoError(t, client.DoCache(ctx, client.B().Hmget().Key("mk").Field("1", "2").Cache(), time.Minute).Error()) + resp, err := client.DoCache(ctx, client.B().Hmget().Key("mk").Field("1", "2").Cache(), time.Minute).ToArray() + assert.Len(t, resp, 2) + assert.NoError(t, err) + }, + assertSpans: []func(t *testing.T, span mocktracer.Span){ + func(t *testing.T, span mocktracer.Span) { + assert.Equal(t, "valkey.client", span.Tag(ext.ServiceName)) + assert.Equal(t, "127.0.0.1", span.Tag(ext.TargetHost)) + assert.Equal(t, valkeyPort, span.Tag(ext.TargetPort)) + assert.Greater(t, span.Tag("db.stmt_size"), 0) + assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.DBStatement)) + assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.ResourceName)) + assert.Equal(t, "HMGET", span.Tag("db.operation")) + assert.False(t, span.Tag(ext.ValkeyClientCommandWrite).(bool)) + assert.False(t, span.Tag(ext.ValkeyClientCacheHit).(bool)) + assert.Greater(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) + assert.Greater(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) + assert.Greater(t, span.Tag(ext.ValkeyClientCachePTTL), int64(0)) + assert.False(t, span.Tag(ext.ValkeyClientCommandStream).(bool)) + assert.False(t, span.Tag(ext.ValkeyClientCommandBlock).(bool)) + assert.False(t, span.Tag(ext.ValkeyClientCommandMulti).(bool)) + assert.Nil(t, span.Tag(ext.DBApplication)) + assert.Nil(t, span.Tag(ext.EventSampleRate)) + assert.Nil(t, span.Tag(ext.Error)) + }, + func(t *testing.T, span mocktracer.Span) { + assert.Equal(t, "valkey.client", span.Tag(ext.ServiceName)) + assert.Equal(t, "127.0.0.1", span.Tag(ext.TargetHost)) + assert.Equal(t, valkeyPort, span.Tag(ext.TargetPort)) + assert.Greater(t, span.Tag("db.stmt_size"), 0) + assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.DBStatement)) + assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.ResourceName)) + assert.Equal(t, "HMGET", span.Tag("db.operation")) + assert.False(t, span.Tag(ext.ValkeyClientCommandWrite).(bool)) + assert.True(t, span.Tag(ext.ValkeyClientCacheHit).(bool)) + assert.Greater(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) + assert.Greater(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) + assert.Greater(t, span.Tag(ext.ValkeyClientCachePTTL), int64(0)) + assert.False(t, span.Tag(ext.ValkeyClientCommandStream).(bool)) + assert.False(t, span.Tag(ext.ValkeyClientCommandBlock).(bool)) + assert.False(t, span.Tag(ext.ValkeyClientCommandMulti).(bool)) + assert.Nil(t, span.Tag(ext.DBApplication)) + assert.Nil(t, span.Tag(ext.EventSampleRate)) + assert.Nil(t, span.Tag(ext.Error)) + }, + }, + }, + { + name: "Test GET command with stream with env vars", + valkeyClientOptions: valkey.ClientOption{ + InitAddress: []string{fmt.Sprintf("127.0.0.1:%d", valkeyPort)}, + Username: valkeyUsername, + Password: valkeyPassword, + }, + valkeytraceClientEnvVars: map[string]string{ + "DD_TRACE_VALKEY_SERVICE_NAME": "my-valkey-client", + "DD_TRACE_VALKEY_ANALYTICS_ENABLED": "true", + "DD_TRACE_VALKEY_SKIP_RAW_COMMAND": "true", + }, + createSpans: func(t *testing.T, ctx context.Context, client valkey.Client) { + resp := client.DoStream(ctx, client.B().Get().Key("test_key").Build()) + assert.NoError(t, resp.Error()) + }, + assertSpans: []func(t *testing.T, span mocktracer.Span){ + func(t *testing.T, span mocktracer.Span) { + assert.Equal(t, "my-valkey-client", span.Tag(ext.ServiceName)) + assert.Equal(t, "127.0.0.1", span.Tag(ext.TargetHost)) + assert.Equal(t, valkeyPort, span.Tag(ext.TargetPort)) + assert.Equal(t, "GET", span.Tag(ext.DBStatement)) + assert.Equal(t, "GET", span.Tag(ext.ResourceName)) + assert.Greater(t, span.Tag("db.stmt_size"), 0) + assert.Equal(t, "GET", span.Tag("db.operation")) + assert.False(t, span.Tag(ext.ValkeyClientCommandWrite).(bool)) + assert.True(t, span.Tag(ext.ValkeyClientCommandStream).(bool)) + assert.False(t, span.Tag(ext.ValkeyClientCommandBlock).(bool)) + assert.False(t, span.Tag(ext.ValkeyClientCommandMulti).(bool)) + assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) + assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) + assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) + assert.Nil(t, span.Tag(ext.ValkeyClientCachePTTL)) + assert.Nil(t, span.Tag(ext.DBApplication)) + assert.Equal(t, 1.0, span.Tag(ext.EventSampleRate)) + assert.Nil(t, span.Tag(ext.Error)) + }, + }, + }, + { + name: "Test SUBSCRIBE command with timeout", + valkeyClientOptions: valkey.ClientOption{ + InitAddress: []string{fmt.Sprintf("127.0.0.1:%d", valkeyPort)}, + Username: valkeyUsername, + Password: valkeyPassword, + }, + createSpans: func(t *testing.T, ctx context.Context, client valkey.Client) { + ctxWithTimeout, cancel := context.WithTimeout(ctx, time.Millisecond) + assert.Equal(t, + context.DeadlineExceeded, + client.Receive(ctxWithTimeout, client.B().Subscribe().Channel("test_channel").Build(), func(msg valkey.PubSubMessage) {}), + ) + cancel() + }, + assertSpans: []func(t *testing.T, span mocktracer.Span){ + func(t *testing.T, span mocktracer.Span) { + assert.Equal(t, "valkey.client", span.Tag(ext.ServiceName)) + assert.Equal(t, "127.0.0.1", span.Tag(ext.TargetHost)) + assert.Equal(t, valkeyPort, span.Tag(ext.TargetPort)) + assert.Greater(t, span.Tag("db.stmt_size"), 0) + assert.Equal(t, "SUBSCRIBE\ntest_channel", span.Tag(ext.DBStatement)) + assert.Equal(t, "SUBSCRIBE\ntest_channel", span.Tag(ext.ResourceName)) + assert.Equal(t, "SUBSCRIBE", span.Tag("db.operation")) + assert.False(t, span.Tag(ext.ValkeyClientCommandWrite).(bool)) + assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) + assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) + assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) + assert.Nil(t, span.Tag(ext.ValkeyClientCachePTTL)) + assert.False(t, span.Tag(ext.ValkeyClientCommandStream).(bool)) + assert.False(t, span.Tag(ext.ValkeyClientCommandBlock).(bool)) + assert.False(t, span.Tag(ext.ValkeyClientCommandMulti).(bool)) + assert.Nil(t, span.Tag(ext.DBApplication)) + assert.Nil(t, span.Tag(ext.EventSampleRate)) + assert.Equal(t, context.DeadlineExceeded, span.Tag(ext.Error).(error)) + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + mt := mocktracer.Start() + defer mt.Stop() + for k, v := range tt.valkeytraceClientEnvVars { + t.Setenv(k, v) + } + client, err := valkeytrace.NewClient(tt.valkeyClientOptions, tt.valkeytraceClientOptions...) + if tt.assertNewClientError == nil { + require.NoErrorf(t, err, tt.name) + } else { + tt.assertNewClientError(t, err) + return + } + tt.createSpans(t, ctx, client) + spans := mt.FinishedSpans() + require.Len(t, spans, len(tt.assertSpans)) + for i, span := range spans { + tt.assertSpans[i](t, span) + // Following assertions are common to all spans + assert.NotNil(t, span) + assert.True(t, span.Tag(ext.ValkeyClientCommandWithPassword).(bool)) + assert.Equal(t, tt.valkeyClientOptions.Username, span.Tag(ext.DBUser)) + assert.Equal(t, "valkey.command", span.OperationName()) + assert.Equal(t, "client", span.Tag(ext.SpanKind)) + assert.Equal(t, ext.SpanTypeValkey, span.Tag(ext.SpanType)) + assert.Equal(t, "valkey-go/valkey", span.Tag(ext.Component)) + assert.Equal(t, "valkey", span.Tag(ext.DBType)) + assert.Equal(t, "valkey", span.Tag(ext.DBSystem)) + } + }) + } + +} diff --git a/ddtrace/ext/app_types.go b/ddtrace/ext/app_types.go index eb6ded8f60..1313c78a76 100644 --- a/ddtrace/ext/app_types.go +++ b/ddtrace/ext/app_types.go @@ -51,6 +51,9 @@ const ( // also have a "redis.raw_command" tag. SpanTypeRedis = "redis" + // SpanTypeRedis marks a span as a Valkey operation. + SpanTypeValkey = "valkey" + // SpanTypeMemcached marks a span as a memcached operation. SpanTypeMemcached = "memcached" diff --git a/ddtrace/ext/db.go b/ddtrace/ext/db.go index c9a046f86d..9ad7bd01c5 100644 --- a/ddtrace/ext/db.go +++ b/ddtrace/ext/db.go @@ -32,6 +32,7 @@ const ( DBSystemOtherSQL = "other_sql" DBSystemElasticsearch = "elasticsearch" DBSystemRedis = "redis" + DBSystemValkey = "valkey" DBSystemMongoDB = "mongodb" DBSystemCassandra = "cassandra" DBSystemConsulKV = "consul" @@ -57,6 +58,45 @@ const ( RedisDatabaseIndex = "db.redis.database_index" ) +// Valkey tags. +const ( + // ValkeyDatabaseIndex specifies the index of the database being connected to. + ValkeyDatabaseIndex = "db.valkey.database_index" + + // ValkeyClientVersion denotes the version of the Valkey client in use. + ValkeyClientVersion = "db.valkey.client.version" + + // ValkeyClientName indicates the name of the Valkey client being used. + ValkeyClientName = "db.valkey.client.name" + + // ValkeyClientCacheHit is the remaining TTL in seconds of client side cache. + ValkeyClientCacheHit = "db.valkey.client.cache.hit" + + // ValkeyClientCacheTTL captures the Time-To-Live (TTL) of a cached entry in the client. + ValkeyClientCacheTTL = "db.valkey.client.cache.ttl" + + // ValkeyClientCachePTTL is the remaining PTTL in seconds of client side cache. + ValkeyClientCachePTTL = "db.valkey.client.cache.pttl" + + // ValkeyClientCachePXAT is the remaining PXAT in seconds of client side cache. + ValkeyClientCachePXAT = "db.valkey.client.cache.pxat" + + // ValkeyClientCommandWrite indicates whether a command involves a write operation. + ValkeyClientCommandWrite = "db.valkey.client.command.write" + + // ValkeyClientCommandBlock indicates whether a command is blocking. + ValkeyClientCommandBlock = "db.valkey.client.command.block" + + // ValkeyClientCommandMulti specifies whether multiple Valkey commands are sent together. + ValkeyClientCommandMulti = "db.valkey.client.command.multi" + + // ValkeyClientCommandStream indicates whether a command uses a dedicated connection to stream responses directly. + ValkeyClientCommandStream = "db.valkey.client.command.stream" + + // ValkeyClientCommandWithPassword indicates whether a command was executed with password authentication. + ValkeyClientCommandWithPassword = "db.valkey.client.command.with_password" +) + // Cassandra tags. const ( // CassandraQuery is the tag name used for cassandra queries. diff --git a/docker-compose.yaml b/docker-compose.yaml index 76680be932..c397a91758 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -41,6 +41,12 @@ services: image: redis:3.2 ports: - "6379:6379" + valkey: + image: valkey/valkey:8 + ports: + - "6380:6380" + # https://valkey.io/topics/acl/ + command: [ "valkey-server", "--port", "6380", "--requirepass", "password-for-default" ] elasticsearch2: image: elasticsearch:2 environment: diff --git a/go.mod b/go.mod index f78b187447..c78d2c02dc 100644 --- a/go.mod +++ b/go.mod @@ -90,6 +90,7 @@ require ( github.com/uptrace/bun v1.1.17 github.com/uptrace/bun/dialect/sqlitedialect v1.1.17 github.com/urfave/negroni v1.0.0 + github.com/valkey-io/valkey-go v1.0.52 github.com/valyala/fasthttp v1.51.0 github.com/vektah/gqlparser/v2 v2.5.16 github.com/zenazn/goji v1.0.1 @@ -294,7 +295,7 @@ require ( go.uber.org/zap v1.27.0 // indirect golang.org/x/arch v0.4.0 // indirect golang.org/x/crypto v0.31.0 // indirect - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/net v0.33.0 // indirect golang.org/x/term v0.27.0 // indirect golang.org/x/text v0.21.0 // indirect diff --git a/go.sum b/go.sum index aed16d7b16..4165930177 100644 --- a/go.sum +++ b/go.sum @@ -1819,8 +1819,8 @@ github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+t github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= -github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -2120,6 +2120,8 @@ github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +github.com/valkey-io/valkey-go v1.0.52 h1:ojrR736satGucqpllYzal8fUrNNROc11V10zokAyIYg= +github.com/valkey-io/valkey-go v1.0.52/go.mod h1:BXlVAPIL9rFQinSFM+N32JfWzfCaUAqBpZkc4vPY6fM= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= @@ -2329,8 +2331,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= diff --git a/internal/exectracetest/go.mod b/internal/exectracetest/go.mod index 653487ca6f..1a7fbec509 100644 --- a/internal/exectracetest/go.mod +++ b/internal/exectracetest/go.mod @@ -5,7 +5,7 @@ go 1.22.0 require ( github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b github.com/mattn/go-sqlite3 v1.14.18 - golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 gopkg.in/DataDog/dd-trace-go.v1 v1.64.0 ) diff --git a/internal/exectracetest/go.sum b/internal/exectracetest/go.sum index db01821d72..3d73ca60cf 100644 --- a/internal/exectracetest/go.sum +++ b/internal/exectracetest/go.sum @@ -231,8 +231,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= diff --git a/internal/namingschema/op.go b/internal/namingschema/op.go index 01c90726c1..2d3059facf 100644 --- a/internal/namingschema/op.go +++ b/internal/namingschema/op.go @@ -31,6 +31,7 @@ const ( // cache MemcachedOutbound RedisOutbound + ValkeyOutbound // db ElasticSearchOutbound @@ -75,6 +76,8 @@ func opV1(t IntegrationType) string { return "memcached.command" case RedisOutbound: return "redis.command" + case ValkeyOutbound: + return "valkey.command" // Database case ElasticSearchOutbound: @@ -121,6 +124,8 @@ func opV0(t IntegrationType) string { return "memcached.query" case RedisOutbound: return "redis.command" + case ValkeyOutbound: + return "valkey.command" case ElasticSearchOutbound: return "elasticsearch.query" case MongoDBOutbound: From fe59e6d14442bd3742caa4ce2e88da62dda8f685 Mon Sep 17 00:00:00 2001 From: keisku Date: Thu, 16 Jan 2025 02:58:00 +0000 Subject: [PATCH 02/24] as per DD_APM_PEER_TAGS_AGGREGATION in agent --- contrib/valkey-go/option.go | 17 ---- contrib/valkey-go/valkey.go | 44 +++++++--- contrib/valkey-go/valkey_test.go | 135 ++++++++++++++++++++++++------- 3 files changed, 137 insertions(+), 59 deletions(-) diff --git a/contrib/valkey-go/option.go b/contrib/valkey-go/option.go index 21b16498ae..406ca5a753 100644 --- a/contrib/valkey-go/option.go +++ b/contrib/valkey-go/option.go @@ -9,16 +9,12 @@ package valkey import ( "math" - "os" "gopkg.in/DataDog/dd-trace-go.v1/internal" "gopkg.in/DataDog/dd-trace-go.v1/internal/namingschema" ) -const defaultServiceName = "valkey.client" - type clientConfig struct { - serviceName string spanName string analyticsRate float64 skipRaw bool @@ -28,18 +24,12 @@ type clientConfig struct { type ClientOption func(*clientConfig) func defaults(cfg *clientConfig) { - cfg.serviceName = namingschema.ServiceNameOverrideV0(defaultServiceName, defaultServiceName) cfg.spanName = namingschema.OpName(namingschema.ValkeyOutbound) if internal.BoolEnv("DD_TRACE_VALKEY_ANALYTICS_ENABLED", false) { cfg.analyticsRate = 1.0 } else { cfg.analyticsRate = math.NaN() } - if v := os.Getenv("DD_TRACE_VALKEY_SERVICE_NAME"); v == "" { - cfg.serviceName = defaultServiceName - } else { - cfg.serviceName = v - } cfg.skipRaw = internal.BoolEnv("DD_TRACE_VALKEY_SKIP_RAW_COMMAND", false) } @@ -52,13 +42,6 @@ func WithSkipRawCommand(skip bool) ClientOption { } } -// WithServiceName sets the given service name for the client. -func WithServiceName(name string) ClientOption { - return func(cfg *clientConfig) { - cfg.serviceName = name - } -} - // WithAnalytics enables Trace Analytics for all started spans. func WithAnalytics(on bool) ClientOption { return func(cfg *clientConfig) { diff --git a/contrib/valkey-go/valkey.go b/contrib/valkey-go/valkey.go index f470c01a32..1bdf183463 100644 --- a/contrib/valkey-go/valkey.go +++ b/contrib/valkey-go/valkey.go @@ -62,14 +62,10 @@ func NewClient(option valkey.ClientOption, opts ...ClientOption) (valkey.Client, for _, fn := range opts { fn(&cfg) } - var host, portStr string + var host string var port int if len(option.InitAddress) == 1 { - host, portStr, err = net.SplitHostPort(option.InitAddress[0]) - if err != nil { - log.Error("valkey.ClientOption.InitAddress contains invalid address: %s", err) - } - port, _ = strconv.Atoi(portStr) + host, port = splitHostPort(option.InitAddress[0]) } core := coreClient{ Client: valkeyClient, @@ -83,6 +79,16 @@ func NewClient(option valkey.ClientOption, opts ...ClientOption) (valkey.Client, }, nil } +func splitHostPort(addr string) (string, int) { + host, portStr, err := net.SplitHostPort(addr) + if err != nil { + log.Error("%q cannot be split: %s", addr, err) + return "", 0 + } + port, _ := strconv.Atoi(portStr) + return host, port +} + type commander interface { Commands() []string } @@ -154,10 +160,26 @@ type buildStartSpanOptionsInput struct { skipRawCommand bool } +func (c *coreClient) peerTags() []tracer.StartSpanOption { + ipAddr := net.ParseIP(c.host) + var peerHostKey string + if ipAddr == nil { + peerHostKey = ext.PeerHostname + } else if ipAddr.To4() != nil { + peerHostKey = ext.PeerHostIPV4 + } else { + peerHostKey = ext.PeerHostIPV6 + } + return []tracer.StartSpanOption{ + tracer.Tag(ext.PeerService, ext.DBSystemValkey), + tracer.Tag(peerHostKey, c.host), + tracer.Tag(ext.PeerPort, c.port), + } +} + func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []tracer.StartSpanOption { opts := []tracer.StartSpanOption{ tracer.SpanType(ext.SpanTypeValkey), - tracer.ServiceName(c.clientConfig.serviceName), tracer.Tag(ext.TargetHost, c.host), tracer.Tag(ext.TargetPort, c.port), tracer.Tag(ext.ValkeyClientVersion, valkey.LibVer), @@ -166,6 +188,7 @@ func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []t tracer.Tag(ext.SpanKind, ext.SpanKindClient), tracer.Tag(ext.DBType, ext.DBSystemValkey), tracer.Tag(ext.DBSystem, ext.DBSystemValkey), + tracer.Tag(ext.DBInstance, ext.DBSystemValkey), tracer.Tag(ext.ValkeyDatabaseIndex, c.option.SelectDB), tracer.Tag(ext.ValkeyClientCommandWrite, input.isWrite), tracer.Tag(ext.ValkeyClientCommandBlock, input.isBlock), @@ -173,6 +196,7 @@ func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []t tracer.Tag(ext.ValkeyClientCommandStream, input.isStream), tracer.Tag(ext.ValkeyClientCommandWithPassword, c.option.Password != ""), } + opts = append(opts, c.peerTags()...) if input.command != "" { opts = append(opts, []tracer.StartSpanOption{ // valkeyotel tags @@ -324,11 +348,7 @@ func (c *client) Dedicate() (client valkey.DedicatedClient, cancel func()) { func (c *client) Nodes() map[string]valkey.Client { nodes := c.Client.Nodes() for addr, valkeyClient := range nodes { - host, portStr, err := net.SplitHostPort(addr) - if err != nil { - log.Error("invalid address is set to valkey client: %s", err) - } - port, _ := strconv.Atoi(portStr) + host, port := splitHostPort(addr) nodes[addr] = &client{ coreClient: coreClient{ Client: valkeyClient, diff --git a/contrib/valkey-go/valkey_test.go b/contrib/valkey-go/valkey_test.go index 10329a4e92..8badb15f96 100644 --- a/contrib/valkey-go/valkey_test.go +++ b/contrib/valkey-go/valkey_test.go @@ -2,7 +2,7 @@ // under the Apache License Version 2.0. // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016 Datadog, Inc. -package valkey_test +package valkey import ( "context" @@ -14,9 +14,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/valkey-io/valkey-go" - valkeytrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/valkey-go" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/mocktracer" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) var ( @@ -35,11 +36,89 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } +func TestPeerTags(t *testing.T) { + tests := []struct { + initAddress string + expectedTags map[string]interface{} + }{ + { + initAddress: "127.0.0.1:6379", + expectedTags: map[string]interface{}{ + ext.PeerService: "valkey", + ext.PeerHostIPV4: "127.0.0.1", + ext.PeerPort: 6379, + }, + }, + { + initAddress: "[::1]:6379", + expectedTags: map[string]interface{}{ + ext.PeerService: "valkey", + ext.PeerHostIPV6: "::1", + ext.PeerPort: 6379, + }, + }, + { + initAddress: "[2001:db8::2]:6379", + expectedTags: map[string]interface{}{ + ext.PeerService: "valkey", + ext.PeerHostIPV6: "2001:db8::2", + ext.PeerPort: 6379, + }, + }, + { + initAddress: "[2001:db8::2%lo]:6379", + expectedTags: map[string]interface{}{ + ext.PeerService: "valkey", + ext.PeerHostname: "2001:db8::2%lo", + ext.PeerPort: 6379, + }, + }, + { + initAddress: "::1:7777", + expectedTags: map[string]interface{}{ + ext.PeerService: "valkey", + ext.PeerHostname: "", + ext.PeerPort: 0, + }, + }, + { + initAddress: ":::7777", + expectedTags: map[string]interface{}{ + ext.PeerService: "valkey", + ext.PeerHostname: "", + ext.PeerPort: 0, + }, + }, + { + initAddress: "localhost:7777", + expectedTags: map[string]interface{}{ + ext.PeerService: "valkey", + ext.PeerHostname: "localhost", + ext.PeerPort: 7777, + }, + }, + } + for _, tt := range tests { + t.Run(tt.initAddress, func(t *testing.T) { + host, port := splitHostPort(tt.initAddress) + client := coreClient{ + host: host, + port: port, + } + var startSpanConfig ddtrace.StartSpanConfig + for _, tag := range client.peerTags() { + tag(&startSpanConfig) + } + require.Equal(t, tt.expectedTags, startSpanConfig.Tags) + }) + } +} + func TestNewClient(t *testing.T) { tests := []struct { name string valkeyClientOptions valkey.ClientOption - valkeytraceClientOptions []valkeytrace.ClientOption + valkeytraceClientOptions []ClientOption valkeytraceClientEnvVars map[string]string createSpans func(*testing.T, context.Context, valkey.Client) assertNewClientError func(*testing.T, error) @@ -74,19 +153,15 @@ func TestNewClient(t *testing.T) { Username: valkeyUsername, Password: valkeyPassword, }, - valkeytraceClientOptions: []valkeytrace.ClientOption{ - valkeytrace.WithServiceName("my-valkey-client"), - valkeytrace.WithAnalytics(true), - valkeytrace.WithSkipRawCommand(true), + valkeytraceClientOptions: []ClientOption{ + WithAnalytics(true), + WithSkipRawCommand(true), }, createSpans: func(t *testing.T, ctx context.Context, client valkey.Client) { assert.NoError(t, client.Do(ctx, client.B().Set().Key("test_key").Value("test_value").Build()).Error()) }, assertSpans: []func(t *testing.T, span mocktracer.Span){ func(t *testing.T, span mocktracer.Span) { - assert.Equal(t, "my-valkey-client", span.Tag(ext.ServiceName)) - assert.Equal(t, "127.0.0.1", span.Tag(ext.TargetHost)) - assert.Equal(t, valkeyPort, span.Tag(ext.TargetPort)) assert.Equal(t, "SET", span.Tag(ext.DBStatement)) assert.Equal(t, "SET", span.Tag(ext.ResourceName)) assert.Greater(t, span.Tag("db.stmt_size"), 0) @@ -119,9 +194,6 @@ func TestNewClient(t *testing.T) { }, assertSpans: []func(t *testing.T, span mocktracer.Span){ func(t *testing.T, span mocktracer.Span) { - assert.Equal(t, "valkey.client", span.Tag(ext.ServiceName)) - assert.Equal(t, "127.0.0.1", span.Tag(ext.TargetHost)) - assert.Equal(t, valkeyPort, span.Tag(ext.TargetPort)) assert.Equal(t, "SET\ntest_key\ntest_value\nGET\ntest_key", span.Tag(ext.DBStatement)) assert.Equal(t, "SET\ntest_key\ntest_value\nGET\ntest_key", span.Tag(ext.ResourceName)) assert.Greater(t, span.Tag("db.stmt_size"), 0) @@ -155,9 +227,6 @@ func TestNewClient(t *testing.T) { }, assertSpans: []func(t *testing.T, span mocktracer.Span){ func(t *testing.T, span mocktracer.Span) { - assert.Equal(t, "valkey.client", span.Tag(ext.ServiceName)) - assert.Equal(t, "127.0.0.1", span.Tag(ext.TargetHost)) - assert.Equal(t, valkeyPort, span.Tag(ext.TargetPort)) assert.Greater(t, span.Tag("db.stmt_size"), 0) assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.DBStatement)) assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.ResourceName)) @@ -175,9 +244,6 @@ func TestNewClient(t *testing.T) { assert.Nil(t, span.Tag(ext.Error)) }, func(t *testing.T, span mocktracer.Span) { - assert.Equal(t, "valkey.client", span.Tag(ext.ServiceName)) - assert.Equal(t, "127.0.0.1", span.Tag(ext.TargetHost)) - assert.Equal(t, valkeyPort, span.Tag(ext.TargetPort)) assert.Greater(t, span.Tag("db.stmt_size"), 0) assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.DBStatement)) assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.ResourceName)) @@ -204,7 +270,6 @@ func TestNewClient(t *testing.T) { Password: valkeyPassword, }, valkeytraceClientEnvVars: map[string]string{ - "DD_TRACE_VALKEY_SERVICE_NAME": "my-valkey-client", "DD_TRACE_VALKEY_ANALYTICS_ENABLED": "true", "DD_TRACE_VALKEY_SKIP_RAW_COMMAND": "true", }, @@ -214,9 +279,6 @@ func TestNewClient(t *testing.T) { }, assertSpans: []func(t *testing.T, span mocktracer.Span){ func(t *testing.T, span mocktracer.Span) { - assert.Equal(t, "my-valkey-client", span.Tag(ext.ServiceName)) - assert.Equal(t, "127.0.0.1", span.Tag(ext.TargetHost)) - assert.Equal(t, valkeyPort, span.Tag(ext.TargetPort)) assert.Equal(t, "GET", span.Tag(ext.DBStatement)) assert.Equal(t, "GET", span.Tag(ext.ResourceName)) assert.Greater(t, span.Tag("db.stmt_size"), 0) @@ -252,9 +314,6 @@ func TestNewClient(t *testing.T) { }, assertSpans: []func(t *testing.T, span mocktracer.Span){ func(t *testing.T, span mocktracer.Span) { - assert.Equal(t, "valkey.client", span.Tag(ext.ServiceName)) - assert.Equal(t, "127.0.0.1", span.Tag(ext.TargetHost)) - assert.Equal(t, valkeyPort, span.Tag(ext.TargetPort)) assert.Greater(t, span.Tag("db.stmt_size"), 0) assert.Equal(t, "SUBSCRIBE\ntest_channel", span.Tag(ext.DBStatement)) assert.Equal(t, "SUBSCRIBE\ntest_channel", span.Tag(ext.ResourceName)) @@ -276,25 +335,40 @@ func TestNewClient(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ctx := context.Background() mt := mocktracer.Start() defer mt.Stop() for k, v := range tt.valkeytraceClientEnvVars { t.Setenv(k, v) } - client, err := valkeytrace.NewClient(tt.valkeyClientOptions, tt.valkeytraceClientOptions...) + span, ctx := tracer.StartSpanFromContext(context.Background(), "test.root", tracer.ServiceName("test-service")) + client, err := NewClient(tt.valkeyClientOptions, tt.valkeytraceClientOptions...) if tt.assertNewClientError == nil { require.NoErrorf(t, err, tt.name) } else { tt.assertNewClientError(t, err) + span.Finish() return } tt.createSpans(t, ctx, client) + span.Finish() // test.root exists in the last span. spans := mt.FinishedSpans() - require.Len(t, spans, len(tt.assertSpans)) + require.Len(t, spans, len(tt.assertSpans)+1) // +1 for test.root for i, span := range spans { + if span.OperationName() == "test.root" { + continue + } tt.assertSpans[i](t, span) - // Following assertions are common to all spans + t.Log("Following assertions are common to all spans") + assert.Equalf(t, + "test-service", + span.Tag(ext.ServiceName), + "service name should not be overwritten as per DD_APM_PEER_TAGS_AGGREGATION in trace-agent", + ) + assert.Equal(t, "valkey", span.Tag(ext.PeerService)) + assert.Equal(t, "127.0.0.1", span.Tag(ext.PeerHostIPV4)) + assert.Equal(t, "127.0.0.1", span.Tag(ext.TargetHost)) + assert.Equal(t, valkeyPort, span.Tag(ext.PeerPort)) + assert.Equal(t, valkeyPort, span.Tag(ext.TargetPort)) assert.NotNil(t, span) assert.True(t, span.Tag(ext.ValkeyClientCommandWithPassword).(bool)) assert.Equal(t, tt.valkeyClientOptions.Username, span.Tag(ext.DBUser)) @@ -304,6 +378,7 @@ func TestNewClient(t *testing.T) { assert.Equal(t, "valkey-go/valkey", span.Tag(ext.Component)) assert.Equal(t, "valkey", span.Tag(ext.DBType)) assert.Equal(t, "valkey", span.Tag(ext.DBSystem)) + assert.Equal(t, "valkey", span.Tag(ext.DBInstance)) } }) } From 49ebeed82d148188a46506e7fb38e4cba623ed9e Mon Sep 17 00:00:00 2001 From: keisku Date: Sat, 18 Jan 2025 01:07:24 +0000 Subject: [PATCH 03/24] update example as per best practice Signed-off-by: keisku --- contrib/valkey-go/example_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/valkey-go/example_test.go b/contrib/valkey-go/example_test.go index f9105ea894..03796a485f 100644 --- a/contrib/valkey-go/example_test.go +++ b/contrib/valkey-go/example_test.go @@ -18,6 +18,8 @@ import ( // To start tracing Valkey, simply create a new client using the library and continue // using as you normally would. func Example() { + tracer.Start() + defer tracer.Stop() vk, err := valkeytrace.NewClient(valkey.ClientOption{ InitAddress: []string{"localhost:6379"}, }) From 90f6c43a92f634a1f6197a3ad8954344a469fc68 Mon Sep 17 00:00:00 2001 From: keisku Date: Sat, 18 Jan 2025 01:10:33 +0000 Subject: [PATCH 04/24] fix doc Signed-off-by: keisku --- contrib/valkey-go/option.go | 2 -- contrib/valkey-go/valkey.go | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/contrib/valkey-go/option.go b/contrib/valkey-go/option.go index 406ca5a753..da817b908a 100644 --- a/contrib/valkey-go/option.go +++ b/contrib/valkey-go/option.go @@ -3,8 +3,6 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016 Datadog, Inc. -// Package redis provides tracing functions for tracing the go-redis/redis package (https://github.com/go-redis/redis). -// This package supports versions up to go-redis 6.15. package valkey import ( diff --git a/contrib/valkey-go/valkey.go b/contrib/valkey-go/valkey.go index 1bdf183463..e00261364b 100644 --- a/contrib/valkey-go/valkey.go +++ b/contrib/valkey-go/valkey.go @@ -3,8 +3,7 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016 Datadog, Inc. -// Package redis provides tracing functions for tracing the go-redis/redis package (https://github.com/go-redis/redis). -// This package supports versions up to go-redis 6.15. +// Package valkey provides tracing functions for tracing the valkey/valkey-go package (https://github.com/valkey/valkey-go). package valkey import ( From 46cacc30befdf1d813c7dcc431ce1fee87a517f5 Mon Sep 17 00:00:00 2001 From: keisku Date: Sat, 18 Jan 2025 01:13:39 +0000 Subject: [PATCH 05/24] fix spanName Signed-off-by: keisku --- contrib/valkey-go/option.go | 3 --- contrib/valkey-go/valkey.go | 17 ++++++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/contrib/valkey-go/option.go b/contrib/valkey-go/option.go index da817b908a..58ef3f1402 100644 --- a/contrib/valkey-go/option.go +++ b/contrib/valkey-go/option.go @@ -9,11 +9,9 @@ import ( "math" "gopkg.in/DataDog/dd-trace-go.v1/internal" - "gopkg.in/DataDog/dd-trace-go.v1/internal/namingschema" ) type clientConfig struct { - spanName string analyticsRate float64 skipRaw bool } @@ -22,7 +20,6 @@ type clientConfig struct { type ClientOption func(*clientConfig) func defaults(cfg *clientConfig) { - cfg.spanName = namingschema.OpName(namingschema.ValkeyOutbound) if internal.BoolEnv("DD_TRACE_VALKEY_ANALYTICS_ENABLED", false) { cfg.analyticsRate = 1.0 } else { diff --git a/contrib/valkey-go/valkey.go b/contrib/valkey-go/valkey.go index e00261364b..e89cf2f656 100644 --- a/contrib/valkey-go/valkey.go +++ b/contrib/valkey-go/valkey.go @@ -18,6 +18,7 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" + "gopkg.in/DataDog/dd-trace-go.v1/internal/namingschema" "gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry" ) @@ -38,6 +39,7 @@ type coreClient struct { valkey.Client option valkey.ClientOption clientConfig clientConfig + spanName string host string port int } @@ -70,6 +72,7 @@ func NewClient(option valkey.ClientOption, opts ...ClientOption) (valkey.Client, Client: valkeyClient, option: option, clientConfig: cfg, + spanName: namingschema.OpName(namingschema.ValkeyOutbound), host: host, port: port, } @@ -224,7 +227,7 @@ func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []t func (c *coreClient) Do(ctx context.Context, cmd valkey.Completed) (resp valkey.ValkeyResult) { command, statement, size := processCmd(&cmd) - span, ctx := tracer.StartSpanFromContext(ctx, c.clientConfig.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ + span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ command: command, statement: statement, size: size, @@ -240,7 +243,7 @@ func (c *coreClient) Do(ctx context.Context, cmd valkey.Completed) (resp valkey. func (c *coreClient) DoMulti(ctx context.Context, multi ...valkey.Completed) (resp []valkey.ValkeyResult) { command, statement, size := processMultiCompleted(multi...) - span, ctx := tracer.StartSpanFromContext(ctx, c.clientConfig.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ + span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ command: command, statement: statement, size: size, @@ -254,7 +257,7 @@ func (c *coreClient) DoMulti(ctx context.Context, multi ...valkey.Completed) (re func (c *coreClient) Receive(ctx context.Context, subscribe valkey.Completed, fn func(msg valkey.PubSubMessage)) (err error) { command, statement, size := processCmd(&subscribe) - span, ctx := tracer.StartSpanFromContext(ctx, c.clientConfig.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ + span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ command: command, statement: statement, size: size, @@ -269,7 +272,7 @@ func (c *coreClient) Receive(ctx context.Context, subscribe valkey.Completed, fn func (c *client) DoCache(ctx context.Context, cmd valkey.Cacheable, ttl time.Duration) (resp valkey.ValkeyResult) { command, statement, size := processCmd(&cmd) - span, ctx := tracer.StartSpanFromContext(ctx, c.clientConfig.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ + span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ command: command, statement: statement, size: size, @@ -284,7 +287,7 @@ func (c *client) DoCache(ctx context.Context, cmd valkey.Cacheable, ttl time.Dur func (c *client) DoMultiCache(ctx context.Context, multi ...valkey.CacheableTTL) (resp []valkey.ValkeyResult) { command, statement, size := processMultiCacheableTTL(multi...) - span, ctx := tracer.StartSpanFromContext(ctx, c.clientConfig.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ + span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ command: command, statement: statement, size: size, @@ -298,7 +301,7 @@ func (c *client) DoMultiCache(ctx context.Context, multi ...valkey.CacheableTTL) func (c *client) DoStream(ctx context.Context, cmd valkey.Completed) (resp valkey.ValkeyResultStream) { command, statement, size := processCmd(&cmd) - span, ctx := tracer.StartSpanFromContext(ctx, c.clientConfig.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ + span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ command: command, statement: statement, size: size, @@ -314,7 +317,7 @@ func (c *client) DoStream(ctx context.Context, cmd valkey.Completed) (resp valke func (c *client) DoMultiStream(ctx context.Context, multi ...valkey.Completed) (resp valkey.MultiValkeyResultStream) { command, statement, size := processMultiCompleted(multi...) - span, ctx := tracer.StartSpanFromContext(ctx, c.clientConfig.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ + span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ command: command, statement: statement, size: size, From 6d5b6f6e08481a9e9bc3a9a0b87660bef4a392b3 Mon Sep 17 00:00:00 2001 From: keisku Date: Sat, 18 Jan 2025 01:18:38 +0000 Subject: [PATCH 06/24] update contribIntegrations Signed-off-by: keisku --- ddtrace/tracer/option.go | 1 + ddtrace/tracer/option_test.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ddtrace/tracer/option.go b/ddtrace/tracer/option.go index 1efdcd2359..8d6f0dd25e 100644 --- a/ddtrace/tracer/option.go +++ b/ddtrace/tracer/option.go @@ -99,6 +99,7 @@ var contribIntegrations = map[string]struct { "github.com/zenazn/goji": {"Goji", false}, "log/slog": {"log/slog", false}, "github.com/uptrace/bun": {"Bun", false}, + "github.com/valkey/valkey-go": {"Valkey", false}, } var ( diff --git a/ddtrace/tracer/option_test.go b/ddtrace/tracer/option_test.go index 7c842c9102..5a90b7ffdb 100644 --- a/ddtrace/tracer/option_test.go +++ b/ddtrace/tracer/option_test.go @@ -271,7 +271,7 @@ func TestAgentIntegration(t *testing.T) { defer clearIntegrationsForTests() cfg.loadContribIntegrations(nil) - assert.Equal(t, 56, len(cfg.integrations)) + assert.Equal(t, 57, len(cfg.integrations)) for integrationName, v := range cfg.integrations { assert.False(t, v.Instrumented, "integrationName=%s", integrationName) } From e99f4d7826d13e58b8b3e932de29684742106855 Mon Sep 17 00:00:00 2001 From: keisku Date: Tue, 21 Jan 2025 00:34:06 +0000 Subject: [PATCH 07/24] rename to github.com/valkey-io/valkey-go --- contrib/valkey-go/valkey.go | 4 ++-- ddtrace/tracer/option.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/valkey-go/valkey.go b/contrib/valkey-go/valkey.go index e89cf2f656..52d4bbeca6 100644 --- a/contrib/valkey-go/valkey.go +++ b/contrib/valkey-go/valkey.go @@ -3,7 +3,7 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016 Datadog, Inc. -// Package valkey provides tracing functions for tracing the valkey/valkey-go package (https://github.com/valkey/valkey-go). +// Package valkey provides tracing functions for tracing the valkey/valkey-go package (https://github.com/valkey-io/valkey-go). package valkey import ( @@ -26,7 +26,7 @@ const componentName = "valkey-go/valkey" func init() { telemetry.LoadIntegration(componentName) - tracer.MarkIntegrationImported("github.com/valkey/valkey-go") + tracer.MarkIntegrationImported("github.com/valkey-io/valkey-go") } var ( diff --git a/ddtrace/tracer/option.go b/ddtrace/tracer/option.go index 8d6f0dd25e..4982e5cdaf 100644 --- a/ddtrace/tracer/option.go +++ b/ddtrace/tracer/option.go @@ -99,7 +99,7 @@ var contribIntegrations = map[string]struct { "github.com/zenazn/goji": {"Goji", false}, "log/slog": {"log/slog", false}, "github.com/uptrace/bun": {"Bun", false}, - "github.com/valkey/valkey-go": {"Valkey", false}, + "github.com/valkey-io/valkey-go": {"Valkey", false}, } var ( From 4031cd7f0f6d7acc08fa4acae73ba233625d30bc Mon Sep 17 00:00:00 2001 From: keisku Date: Tue, 21 Jan 2025 00:38:21 +0000 Subject: [PATCH 08/24] remove App Analytics which was deprecated --- contrib/valkey-go/option.go | 33 +------------------------------- contrib/valkey-go/valkey.go | 4 ---- contrib/valkey-go/valkey_test.go | 10 +--------- 3 files changed, 2 insertions(+), 45 deletions(-) diff --git a/contrib/valkey-go/option.go b/contrib/valkey-go/option.go index 58ef3f1402..9c1d6e1e6b 100644 --- a/contrib/valkey-go/option.go +++ b/contrib/valkey-go/option.go @@ -6,25 +6,17 @@ package valkey import ( - "math" - "gopkg.in/DataDog/dd-trace-go.v1/internal" ) type clientConfig struct { - analyticsRate float64 - skipRaw bool + skipRaw bool } // ClientOption represents an option that can be used to create or wrap a client. type ClientOption func(*clientConfig) func defaults(cfg *clientConfig) { - if internal.BoolEnv("DD_TRACE_VALKEY_ANALYTICS_ENABLED", false) { - cfg.analyticsRate = 1.0 - } else { - cfg.analyticsRate = math.NaN() - } cfg.skipRaw = internal.BoolEnv("DD_TRACE_VALKEY_SKIP_RAW_COMMAND", false) } @@ -36,26 +28,3 @@ func WithSkipRawCommand(skip bool) ClientOption { cfg.skipRaw = skip } } - -// WithAnalytics enables Trace Analytics for all started spans. -func WithAnalytics(on bool) ClientOption { - return func(cfg *clientConfig) { - if on { - cfg.analyticsRate = 1.0 - } else { - cfg.analyticsRate = math.NaN() - } - } -} - -// WithAnalyticsRate sets the sampling rate for Trace Analytics events -// correlated to started spans. -func WithAnalyticsRate(rate float64) ClientOption { - return func(cfg *clientConfig) { - if rate >= 0.0 && rate <= 1.0 { - cfg.analyticsRate = rate - } else { - cfg.analyticsRate = math.NaN() - } - } -} diff --git a/contrib/valkey-go/valkey.go b/contrib/valkey-go/valkey.go index 52d4bbeca6..3c1e3f98e3 100644 --- a/contrib/valkey-go/valkey.go +++ b/contrib/valkey-go/valkey.go @@ -8,7 +8,6 @@ package valkey import ( "context" - "math" "net" "strconv" "strings" @@ -219,9 +218,6 @@ func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []t if c.option.Username != "" { opts = append(opts, tracer.Tag(ext.DBUser, c.option.Username)) } - if !math.IsNaN(c.clientConfig.analyticsRate) { - opts = append(opts, tracer.Tag(ext.EventSampleRate, c.clientConfig.analyticsRate)) - } return opts } diff --git a/contrib/valkey-go/valkey_test.go b/contrib/valkey-go/valkey_test.go index 8badb15f96..8d2c5d1fbc 100644 --- a/contrib/valkey-go/valkey_test.go +++ b/contrib/valkey-go/valkey_test.go @@ -154,7 +154,6 @@ func TestNewClient(t *testing.T) { Password: valkeyPassword, }, valkeytraceClientOptions: []ClientOption{ - WithAnalytics(true), WithSkipRawCommand(true), }, createSpans: func(t *testing.T, ctx context.Context, client valkey.Client) { @@ -175,7 +174,6 @@ func TestNewClient(t *testing.T) { assert.Less(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) assert.Less(t, span.Tag(ext.ValkeyClientCachePTTL), int64(0)) assert.Nil(t, span.Tag(ext.DBApplication)) - assert.Equal(t, 1.0, span.Tag(ext.EventSampleRate)) assert.Nil(t, span.Tag(ext.Error)) }, }, @@ -207,7 +205,6 @@ func TestNewClient(t *testing.T) { assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) assert.Nil(t, span.Tag(ext.ValkeyClientCachePTTL)) assert.Equal(t, "my-valkey-client", span.Tag(ext.DBApplication)) - assert.Nil(t, span.Tag(ext.EventSampleRate)) assert.Nil(t, span.Tag(ext.Error)) }, }, @@ -240,7 +237,6 @@ func TestNewClient(t *testing.T) { assert.False(t, span.Tag(ext.ValkeyClientCommandBlock).(bool)) assert.False(t, span.Tag(ext.ValkeyClientCommandMulti).(bool)) assert.Nil(t, span.Tag(ext.DBApplication)) - assert.Nil(t, span.Tag(ext.EventSampleRate)) assert.Nil(t, span.Tag(ext.Error)) }, func(t *testing.T, span mocktracer.Span) { @@ -257,7 +253,6 @@ func TestNewClient(t *testing.T) { assert.False(t, span.Tag(ext.ValkeyClientCommandBlock).(bool)) assert.False(t, span.Tag(ext.ValkeyClientCommandMulti).(bool)) assert.Nil(t, span.Tag(ext.DBApplication)) - assert.Nil(t, span.Tag(ext.EventSampleRate)) assert.Nil(t, span.Tag(ext.Error)) }, }, @@ -270,8 +265,7 @@ func TestNewClient(t *testing.T) { Password: valkeyPassword, }, valkeytraceClientEnvVars: map[string]string{ - "DD_TRACE_VALKEY_ANALYTICS_ENABLED": "true", - "DD_TRACE_VALKEY_SKIP_RAW_COMMAND": "true", + "DD_TRACE_VALKEY_SKIP_RAW_COMMAND": "true", }, createSpans: func(t *testing.T, ctx context.Context, client valkey.Client) { resp := client.DoStream(ctx, client.B().Get().Key("test_key").Build()) @@ -292,7 +286,6 @@ func TestNewClient(t *testing.T) { assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) assert.Nil(t, span.Tag(ext.ValkeyClientCachePTTL)) assert.Nil(t, span.Tag(ext.DBApplication)) - assert.Equal(t, 1.0, span.Tag(ext.EventSampleRate)) assert.Nil(t, span.Tag(ext.Error)) }, }, @@ -327,7 +320,6 @@ func TestNewClient(t *testing.T) { assert.False(t, span.Tag(ext.ValkeyClientCommandBlock).(bool)) assert.False(t, span.Tag(ext.ValkeyClientCommandMulti).(bool)) assert.Nil(t, span.Tag(ext.DBApplication)) - assert.Nil(t, span.Tag(ext.EventSampleRate)) assert.Equal(t, context.DeadlineExceeded, span.Tag(ext.Error).(error)) }, }, From aae0a854033187cd28042272a6ae6a487459f51d Mon Sep 17 00:00:00 2001 From: keisku Date: Tue, 21 Jan 2025 00:41:53 +0000 Subject: [PATCH 09/24] for consistency with the other examples --- contrib/valkey-go/example_test.go | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/contrib/valkey-go/example_test.go b/contrib/valkey-go/example_test.go index 03796a485f..236a35c91c 100644 --- a/contrib/valkey-go/example_test.go +++ b/contrib/valkey-go/example_test.go @@ -7,11 +7,10 @@ package valkey_test import ( "context" - "log/slog" + "log" "github.com/valkey-io/valkey-go" valkeytrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/valkey-go" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) @@ -20,23 +19,17 @@ import ( func Example() { tracer.Start() defer tracer.Stop() + vk, err := valkeytrace.NewClient(valkey.ClientOption{ InitAddress: []string{"localhost:6379"}, }) if err != nil { - slog.Error(err.Error()) + log.Fatal(err) return } - span, ctx := tracer.StartSpanFromContext(context.Background(), "parent.request", - tracer.SpanType(ext.SpanTypeValkey), - tracer.ServiceName("web"), - tracer.ResourceName("/home"), - ) - - if err := vk.Do(ctx, vk.B().Set().Key("key").Value("value").Build()).Error(); err != nil { - slog.ErrorContext(ctx, "set a value", slog.Any("error", err)) + if err := vk.Do(context.Background(), vk.B().Set().Key("key").Value("value").Build()).Error(); err != nil { + log.Fatal(err) + return } - - span.Finish() } From 14b4d21944995b7edba86e35ae8a84a602f2d013 Mon Sep 17 00:00:00 2001 From: keisku Date: Tue, 21 Jan 2025 00:49:31 +0000 Subject: [PATCH 10/24] remove ValkeyClientCommand* tags --- contrib/valkey-go/valkey.go | 21 --------------------- contrib/valkey-go/valkey_test.go | 25 ------------------------- ddtrace/ext/db.go | 15 --------------- 3 files changed, 61 deletions(-) diff --git a/contrib/valkey-go/valkey.go b/contrib/valkey-go/valkey.go index 3c1e3f98e3..1a63a45132 100644 --- a/contrib/valkey-go/valkey.go +++ b/contrib/valkey-go/valkey.go @@ -154,10 +154,6 @@ type buildStartSpanOptionsInput struct { command string statement string size int - isWrite bool - isBlock bool - isMulti bool - isStream bool skipRawCommand bool } @@ -191,11 +187,6 @@ func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []t tracer.Tag(ext.DBSystem, ext.DBSystemValkey), tracer.Tag(ext.DBInstance, ext.DBSystemValkey), tracer.Tag(ext.ValkeyDatabaseIndex, c.option.SelectDB), - tracer.Tag(ext.ValkeyClientCommandWrite, input.isWrite), - tracer.Tag(ext.ValkeyClientCommandBlock, input.isBlock), - tracer.Tag(ext.ValkeyClientCommandMulti, input.isMulti), - tracer.Tag(ext.ValkeyClientCommandStream, input.isStream), - tracer.Tag(ext.ValkeyClientCommandWithPassword, c.option.Password != ""), } opts = append(opts, c.peerTags()...) if input.command != "" { @@ -227,8 +218,6 @@ func (c *coreClient) Do(ctx context.Context, cmd valkey.Completed) (resp valkey. command: command, statement: statement, size: size, - isWrite: cmd.IsWrite(), - isBlock: cmd.IsBlock(), skipRawCommand: c.clientConfig.skipRaw, })...) resp = c.Client.Do(ctx, cmd) @@ -243,7 +232,6 @@ func (c *coreClient) DoMulti(ctx context.Context, multi ...valkey.Completed) (re command: command, statement: statement, size: size, - isMulti: true, skipRawCommand: c.clientConfig.skipRaw, })...) resp = c.Client.DoMulti(ctx, multi...) @@ -257,8 +245,6 @@ func (c *coreClient) Receive(ctx context.Context, subscribe valkey.Completed, fn command: command, statement: statement, size: size, - isWrite: subscribe.IsWrite(), - isBlock: subscribe.IsBlock(), skipRawCommand: c.clientConfig.skipRaw, })...) err = c.Client.Receive(ctx, subscribe, fn) @@ -272,7 +258,6 @@ func (c *client) DoCache(ctx context.Context, cmd valkey.Cacheable, ttl time.Dur command: command, statement: statement, size: size, - isMulti: cmd.IsMGet(), skipRawCommand: c.clientConfig.skipRaw, })...) resp = c.Client.DoCache(ctx, cmd, ttl) @@ -287,7 +272,6 @@ func (c *client) DoMultiCache(ctx context.Context, multi ...valkey.CacheableTTL) command: command, statement: statement, size: size, - isWrite: true, skipRawCommand: c.clientConfig.skipRaw, })...) resp = c.Client.DoMultiCache(ctx, multi...) @@ -301,9 +285,6 @@ func (c *client) DoStream(ctx context.Context, cmd valkey.Completed) (resp valke command: command, statement: statement, size: size, - isWrite: cmd.IsWrite(), - isBlock: cmd.IsBlock(), - isStream: true, skipRawCommand: c.clientConfig.skipRaw, })...) resp = c.Client.DoStream(ctx, cmd) @@ -317,8 +298,6 @@ func (c *client) DoMultiStream(ctx context.Context, multi ...valkey.Completed) ( command: command, statement: statement, size: size, - isMulti: true, - isStream: true, skipRawCommand: c.clientConfig.skipRaw, })...) resp = c.Client.DoMultiStream(ctx, multi...) diff --git a/contrib/valkey-go/valkey_test.go b/contrib/valkey-go/valkey_test.go index 8d2c5d1fbc..5b73955732 100644 --- a/contrib/valkey-go/valkey_test.go +++ b/contrib/valkey-go/valkey_test.go @@ -165,10 +165,6 @@ func TestNewClient(t *testing.T) { assert.Equal(t, "SET", span.Tag(ext.ResourceName)) assert.Greater(t, span.Tag("db.stmt_size"), 0) assert.Equal(t, "SET", span.Tag("db.operation")) - assert.True(t, span.Tag(ext.ValkeyClientCommandWrite).(bool)) - assert.False(t, span.Tag(ext.ValkeyClientCommandStream).(bool)) - assert.False(t, span.Tag(ext.ValkeyClientCommandBlock).(bool)) - assert.False(t, span.Tag(ext.ValkeyClientCommandMulti).(bool)) assert.False(t, span.Tag(ext.ValkeyClientCacheHit).(bool)) assert.Less(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) assert.Less(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) @@ -196,10 +192,6 @@ func TestNewClient(t *testing.T) { assert.Equal(t, "SET\ntest_key\ntest_value\nGET\ntest_key", span.Tag(ext.ResourceName)) assert.Greater(t, span.Tag("db.stmt_size"), 0) assert.Equal(t, "SET GET", span.Tag("db.operation")) - assert.False(t, span.Tag(ext.ValkeyClientCommandWrite).(bool)) - assert.False(t, span.Tag(ext.ValkeyClientCommandStream).(bool)) - assert.False(t, span.Tag(ext.ValkeyClientCommandBlock).(bool)) - assert.True(t, span.Tag(ext.ValkeyClientCommandMulti).(bool)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) @@ -228,14 +220,10 @@ func TestNewClient(t *testing.T) { assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.DBStatement)) assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.ResourceName)) assert.Equal(t, "HMGET", span.Tag("db.operation")) - assert.False(t, span.Tag(ext.ValkeyClientCommandWrite).(bool)) assert.False(t, span.Tag(ext.ValkeyClientCacheHit).(bool)) assert.Greater(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) assert.Greater(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) assert.Greater(t, span.Tag(ext.ValkeyClientCachePTTL), int64(0)) - assert.False(t, span.Tag(ext.ValkeyClientCommandStream).(bool)) - assert.False(t, span.Tag(ext.ValkeyClientCommandBlock).(bool)) - assert.False(t, span.Tag(ext.ValkeyClientCommandMulti).(bool)) assert.Nil(t, span.Tag(ext.DBApplication)) assert.Nil(t, span.Tag(ext.Error)) }, @@ -244,14 +232,10 @@ func TestNewClient(t *testing.T) { assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.DBStatement)) assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.ResourceName)) assert.Equal(t, "HMGET", span.Tag("db.operation")) - assert.False(t, span.Tag(ext.ValkeyClientCommandWrite).(bool)) assert.True(t, span.Tag(ext.ValkeyClientCacheHit).(bool)) assert.Greater(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) assert.Greater(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) assert.Greater(t, span.Tag(ext.ValkeyClientCachePTTL), int64(0)) - assert.False(t, span.Tag(ext.ValkeyClientCommandStream).(bool)) - assert.False(t, span.Tag(ext.ValkeyClientCommandBlock).(bool)) - assert.False(t, span.Tag(ext.ValkeyClientCommandMulti).(bool)) assert.Nil(t, span.Tag(ext.DBApplication)) assert.Nil(t, span.Tag(ext.Error)) }, @@ -277,10 +261,6 @@ func TestNewClient(t *testing.T) { assert.Equal(t, "GET", span.Tag(ext.ResourceName)) assert.Greater(t, span.Tag("db.stmt_size"), 0) assert.Equal(t, "GET", span.Tag("db.operation")) - assert.False(t, span.Tag(ext.ValkeyClientCommandWrite).(bool)) - assert.True(t, span.Tag(ext.ValkeyClientCommandStream).(bool)) - assert.False(t, span.Tag(ext.ValkeyClientCommandBlock).(bool)) - assert.False(t, span.Tag(ext.ValkeyClientCommandMulti).(bool)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) @@ -311,14 +291,10 @@ func TestNewClient(t *testing.T) { assert.Equal(t, "SUBSCRIBE\ntest_channel", span.Tag(ext.DBStatement)) assert.Equal(t, "SUBSCRIBE\ntest_channel", span.Tag(ext.ResourceName)) assert.Equal(t, "SUBSCRIBE", span.Tag("db.operation")) - assert.False(t, span.Tag(ext.ValkeyClientCommandWrite).(bool)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) assert.Nil(t, span.Tag(ext.ValkeyClientCachePTTL)) - assert.False(t, span.Tag(ext.ValkeyClientCommandStream).(bool)) - assert.False(t, span.Tag(ext.ValkeyClientCommandBlock).(bool)) - assert.False(t, span.Tag(ext.ValkeyClientCommandMulti).(bool)) assert.Nil(t, span.Tag(ext.DBApplication)) assert.Equal(t, context.DeadlineExceeded, span.Tag(ext.Error).(error)) }, @@ -362,7 +338,6 @@ func TestNewClient(t *testing.T) { assert.Equal(t, valkeyPort, span.Tag(ext.PeerPort)) assert.Equal(t, valkeyPort, span.Tag(ext.TargetPort)) assert.NotNil(t, span) - assert.True(t, span.Tag(ext.ValkeyClientCommandWithPassword).(bool)) assert.Equal(t, tt.valkeyClientOptions.Username, span.Tag(ext.DBUser)) assert.Equal(t, "valkey.command", span.OperationName()) assert.Equal(t, "client", span.Tag(ext.SpanKind)) diff --git a/ddtrace/ext/db.go b/ddtrace/ext/db.go index 9ad7bd01c5..0b6915b361 100644 --- a/ddtrace/ext/db.go +++ b/ddtrace/ext/db.go @@ -80,21 +80,6 @@ const ( // ValkeyClientCachePXAT is the remaining PXAT in seconds of client side cache. ValkeyClientCachePXAT = "db.valkey.client.cache.pxat" - - // ValkeyClientCommandWrite indicates whether a command involves a write operation. - ValkeyClientCommandWrite = "db.valkey.client.command.write" - - // ValkeyClientCommandBlock indicates whether a command is blocking. - ValkeyClientCommandBlock = "db.valkey.client.command.block" - - // ValkeyClientCommandMulti specifies whether multiple Valkey commands are sent together. - ValkeyClientCommandMulti = "db.valkey.client.command.multi" - - // ValkeyClientCommandStream indicates whether a command uses a dedicated connection to stream responses directly. - ValkeyClientCommandStream = "db.valkey.client.command.stream" - - // ValkeyClientCommandWithPassword indicates whether a command was executed with password authentication. - ValkeyClientCommandWithPassword = "db.valkey.client.command.with_password" ) // Cassandra tags. From 6197ac16f2b26c36ea8fda05ed259c4cacf9b35c Mon Sep 17 00:00:00 2001 From: keisku Date: Tue, 21 Jan 2025 00:51:01 +0000 Subject: [PATCH 11/24] there is not concept of db.instance in valkey / redis --- contrib/valkey-go/valkey.go | 1 - contrib/valkey-go/valkey_test.go | 1 - 2 files changed, 2 deletions(-) diff --git a/contrib/valkey-go/valkey.go b/contrib/valkey-go/valkey.go index 1a63a45132..98fdc9f2ad 100644 --- a/contrib/valkey-go/valkey.go +++ b/contrib/valkey-go/valkey.go @@ -185,7 +185,6 @@ func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []t tracer.Tag(ext.SpanKind, ext.SpanKindClient), tracer.Tag(ext.DBType, ext.DBSystemValkey), tracer.Tag(ext.DBSystem, ext.DBSystemValkey), - tracer.Tag(ext.DBInstance, ext.DBSystemValkey), tracer.Tag(ext.ValkeyDatabaseIndex, c.option.SelectDB), } opts = append(opts, c.peerTags()...) diff --git a/contrib/valkey-go/valkey_test.go b/contrib/valkey-go/valkey_test.go index 5b73955732..294dcf4f96 100644 --- a/contrib/valkey-go/valkey_test.go +++ b/contrib/valkey-go/valkey_test.go @@ -345,7 +345,6 @@ func TestNewClient(t *testing.T) { assert.Equal(t, "valkey-go/valkey", span.Tag(ext.Component)) assert.Equal(t, "valkey", span.Tag(ext.DBType)) assert.Equal(t, "valkey", span.Tag(ext.DBSystem)) - assert.Equal(t, "valkey", span.Tag(ext.DBInstance)) } }) } From 7b7329f77ae764f6b6b9accf70da4a43ce04008c Mon Sep 17 00:00:00 2001 From: keisku Date: Tue, 21 Jan 2025 01:26:46 +0000 Subject: [PATCH 12/24] remove db.type:valkey --- contrib/valkey-go/valkey.go | 1 - contrib/valkey-go/valkey_test.go | 1 - 2 files changed, 2 deletions(-) diff --git a/contrib/valkey-go/valkey.go b/contrib/valkey-go/valkey.go index 98fdc9f2ad..eeeccc01b6 100644 --- a/contrib/valkey-go/valkey.go +++ b/contrib/valkey-go/valkey.go @@ -183,7 +183,6 @@ func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []t tracer.Tag(ext.ValkeyClientName, valkey.LibName), tracer.Tag(ext.Component, componentName), tracer.Tag(ext.SpanKind, ext.SpanKindClient), - tracer.Tag(ext.DBType, ext.DBSystemValkey), tracer.Tag(ext.DBSystem, ext.DBSystemValkey), tracer.Tag(ext.ValkeyDatabaseIndex, c.option.SelectDB), } diff --git a/contrib/valkey-go/valkey_test.go b/contrib/valkey-go/valkey_test.go index 294dcf4f96..57bf1d5b91 100644 --- a/contrib/valkey-go/valkey_test.go +++ b/contrib/valkey-go/valkey_test.go @@ -343,7 +343,6 @@ func TestNewClient(t *testing.T) { assert.Equal(t, "client", span.Tag(ext.SpanKind)) assert.Equal(t, ext.SpanTypeValkey, span.Tag(ext.SpanType)) assert.Equal(t, "valkey-go/valkey", span.Tag(ext.Component)) - assert.Equal(t, "valkey", span.Tag(ext.DBType)) assert.Equal(t, "valkey", span.Tag(ext.DBSystem)) } }) From fca4a18628bba08d42ace104e97e4b894ac90b77 Mon Sep 17 00:00:00 2001 From: keisku Date: Tue, 21 Jan 2025 01:33:20 +0000 Subject: [PATCH 13/24] remove db.application --- contrib/valkey-go/valkey.go | 3 --- contrib/valkey-go/valkey_test.go | 6 ------ 2 files changed, 9 deletions(-) diff --git a/contrib/valkey-go/valkey.go b/contrib/valkey-go/valkey.go index eeeccc01b6..5830a786b3 100644 --- a/contrib/valkey-go/valkey.go +++ b/contrib/valkey-go/valkey.go @@ -201,9 +201,6 @@ func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []t opts = append(opts, tracer.Tag(ext.DBStatement, input.statement)) } } - if c.option.ClientName != "" { - opts = append(opts, tracer.Tag(ext.DBApplication, c.option.ClientName)) - } if c.option.Username != "" { opts = append(opts, tracer.Tag(ext.DBUser, c.option.Username)) } diff --git a/contrib/valkey-go/valkey_test.go b/contrib/valkey-go/valkey_test.go index 57bf1d5b91..c375feb047 100644 --- a/contrib/valkey-go/valkey_test.go +++ b/contrib/valkey-go/valkey_test.go @@ -169,7 +169,6 @@ func TestNewClient(t *testing.T) { assert.Less(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) assert.Less(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) assert.Less(t, span.Tag(ext.ValkeyClientCachePTTL), int64(0)) - assert.Nil(t, span.Tag(ext.DBApplication)) assert.Nil(t, span.Tag(ext.Error)) }, }, @@ -196,7 +195,6 @@ func TestNewClient(t *testing.T) { assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) assert.Nil(t, span.Tag(ext.ValkeyClientCachePTTL)) - assert.Equal(t, "my-valkey-client", span.Tag(ext.DBApplication)) assert.Nil(t, span.Tag(ext.Error)) }, }, @@ -224,7 +222,6 @@ func TestNewClient(t *testing.T) { assert.Greater(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) assert.Greater(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) assert.Greater(t, span.Tag(ext.ValkeyClientCachePTTL), int64(0)) - assert.Nil(t, span.Tag(ext.DBApplication)) assert.Nil(t, span.Tag(ext.Error)) }, func(t *testing.T, span mocktracer.Span) { @@ -236,7 +233,6 @@ func TestNewClient(t *testing.T) { assert.Greater(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) assert.Greater(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) assert.Greater(t, span.Tag(ext.ValkeyClientCachePTTL), int64(0)) - assert.Nil(t, span.Tag(ext.DBApplication)) assert.Nil(t, span.Tag(ext.Error)) }, }, @@ -265,7 +261,6 @@ func TestNewClient(t *testing.T) { assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) assert.Nil(t, span.Tag(ext.ValkeyClientCachePTTL)) - assert.Nil(t, span.Tag(ext.DBApplication)) assert.Nil(t, span.Tag(ext.Error)) }, }, @@ -295,7 +290,6 @@ func TestNewClient(t *testing.T) { assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) assert.Nil(t, span.Tag(ext.ValkeyClientCachePTTL)) - assert.Nil(t, span.Tag(ext.DBApplication)) assert.Equal(t, context.DeadlineExceeded, span.Tag(ext.Error).(error)) }, }, From e7758a3aa759a9e32a91b2fdc9e6e1ac90c142cd Mon Sep 17 00:00:00 2001 From: keisku Date: Tue, 21 Jan 2025 01:41:49 +0000 Subject: [PATCH 14/24] fix valkey.raw_command behavior --- contrib/valkey-go/valkey.go | 12 +++++------- contrib/valkey-go/valkey_test.go | 13 +++++++++---- ddtrace/ext/db.go | 3 +++ 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/contrib/valkey-go/valkey.go b/contrib/valkey-go/valkey.go index 5830a786b3..3e5d603f2e 100644 --- a/contrib/valkey-go/valkey.go +++ b/contrib/valkey-go/valkey.go @@ -176,6 +176,8 @@ func (c *coreClient) peerTags() []tracer.StartSpanOption { func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []tracer.StartSpanOption { opts := []tracer.StartSpanOption{ + tracer.ResourceName(input.statement), + tracer.Tag(ext.DBStatement, input.statement), tracer.SpanType(ext.SpanTypeValkey), tracer.Tag(ext.TargetHost, c.host), tracer.Tag(ext.TargetPort, c.port), @@ -193,13 +195,9 @@ func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []t tracer.Tag("db.stmt_size", input.size), tracer.Tag("db.operation", input.command), }...) - if input.skipRawCommand { - opts = append(opts, tracer.ResourceName(input.command)) - opts = append(opts, tracer.Tag(ext.DBStatement, input.command)) - } else { - opts = append(opts, tracer.ResourceName(input.statement)) - opts = append(opts, tracer.Tag(ext.DBStatement, input.statement)) - } + } + if input.skipRawCommand { + opts = append(opts, tracer.Tag(ext.ValkeyRawCommand, input.skipRawCommand)) } if c.option.Username != "" { opts = append(opts, tracer.Tag(ext.DBUser, c.option.Username)) diff --git a/contrib/valkey-go/valkey_test.go b/contrib/valkey-go/valkey_test.go index c375feb047..717fcefb3a 100644 --- a/contrib/valkey-go/valkey_test.go +++ b/contrib/valkey-go/valkey_test.go @@ -161,10 +161,11 @@ func TestNewClient(t *testing.T) { }, assertSpans: []func(t *testing.T, span mocktracer.Span){ func(t *testing.T, span mocktracer.Span) { - assert.Equal(t, "SET", span.Tag(ext.DBStatement)) - assert.Equal(t, "SET", span.Tag(ext.ResourceName)) + assert.Equal(t, "SET\ntest_key\ntest_value", span.Tag(ext.DBStatement)) + assert.Equal(t, "SET\ntest_key\ntest_value", span.Tag(ext.ResourceName)) assert.Greater(t, span.Tag("db.stmt_size"), 0) assert.Equal(t, "SET", span.Tag("db.operation")) + assert.True(t, span.Tag(ext.ValkeyRawCommand).(bool)) assert.False(t, span.Tag(ext.ValkeyClientCacheHit).(bool)) assert.Less(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) assert.Less(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) @@ -191,6 +192,7 @@ func TestNewClient(t *testing.T) { assert.Equal(t, "SET\ntest_key\ntest_value\nGET\ntest_key", span.Tag(ext.ResourceName)) assert.Greater(t, span.Tag("db.stmt_size"), 0) assert.Equal(t, "SET GET", span.Tag("db.operation")) + assert.Nil(t, span.Tag(ext.ValkeyRawCommand)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) @@ -229,6 +231,7 @@ func TestNewClient(t *testing.T) { assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.DBStatement)) assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.ResourceName)) assert.Equal(t, "HMGET", span.Tag("db.operation")) + assert.Nil(t, span.Tag(ext.ValkeyRawCommand)) assert.True(t, span.Tag(ext.ValkeyClientCacheHit).(bool)) assert.Greater(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) assert.Greater(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) @@ -253,10 +256,11 @@ func TestNewClient(t *testing.T) { }, assertSpans: []func(t *testing.T, span mocktracer.Span){ func(t *testing.T, span mocktracer.Span) { - assert.Equal(t, "GET", span.Tag(ext.DBStatement)) - assert.Equal(t, "GET", span.Tag(ext.ResourceName)) + assert.Equal(t, "GET\ntest_key", span.Tag(ext.DBStatement)) + assert.Equal(t, "GET\ntest_key", span.Tag(ext.ResourceName)) assert.Greater(t, span.Tag("db.stmt_size"), 0) assert.Equal(t, "GET", span.Tag("db.operation")) + assert.True(t, span.Tag(ext.ValkeyRawCommand).(bool)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) @@ -286,6 +290,7 @@ func TestNewClient(t *testing.T) { assert.Equal(t, "SUBSCRIBE\ntest_channel", span.Tag(ext.DBStatement)) assert.Equal(t, "SUBSCRIBE\ntest_channel", span.Tag(ext.ResourceName)) assert.Equal(t, "SUBSCRIBE", span.Tag("db.operation")) + assert.Nil(t, span.Tag(ext.ValkeyRawCommand)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) diff --git a/ddtrace/ext/db.go b/ddtrace/ext/db.go index 0b6915b361..991ffd9457 100644 --- a/ddtrace/ext/db.go +++ b/ddtrace/ext/db.go @@ -60,6 +60,9 @@ const ( // Valkey tags. const ( + // ValkeyRawCommand allows to set the raw command for tags. + ValkeyRawCommand = "valkey.raw_command" + // ValkeyDatabaseIndex specifies the index of the database being connected to. ValkeyDatabaseIndex = "db.valkey.database_index" From cdb4129430a2ed7893fd6fdd2cc2201f90b4c89e Mon Sep 17 00:00:00 2001 From: keisku Date: Tue, 21 Jan 2025 01:46:33 +0000 Subject: [PATCH 15/24] add db.out tag --- contrib/valkey-go/valkey.go | 1 + contrib/valkey-go/valkey_test.go | 2 ++ 2 files changed, 3 insertions(+) diff --git a/contrib/valkey-go/valkey.go b/contrib/valkey-go/valkey.go index 3e5d603f2e..0455863095 100644 --- a/contrib/valkey-go/valkey.go +++ b/contrib/valkey-go/valkey.go @@ -187,6 +187,7 @@ func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []t tracer.Tag(ext.SpanKind, ext.SpanKindClient), tracer.Tag(ext.DBSystem, ext.DBSystemValkey), tracer.Tag(ext.ValkeyDatabaseIndex, c.option.SelectDB), + tracer.Tag("db.out", c.option.SelectDB), } opts = append(opts, c.peerTags()...) if input.command != "" { diff --git a/contrib/valkey-go/valkey_test.go b/contrib/valkey-go/valkey_test.go index 717fcefb3a..e534cd1926 100644 --- a/contrib/valkey-go/valkey_test.go +++ b/contrib/valkey-go/valkey_test.go @@ -336,6 +336,8 @@ func TestNewClient(t *testing.T) { assert.Equal(t, "127.0.0.1", span.Tag(ext.TargetHost)) assert.Equal(t, valkeyPort, span.Tag(ext.PeerPort)) assert.Equal(t, valkeyPort, span.Tag(ext.TargetPort)) + assert.Equal(t, 0, span.Tag(ext.ValkeyDatabaseIndex)) + assert.Equal(t, 0, span.Tag("db.out")) assert.NotNil(t, span) assert.Equal(t, tt.valkeyClientOptions.Username, span.Tag(ext.DBUser)) assert.Equal(t, "valkey.command", span.OperationName()) From 21434c3714214336c50200451844ebeb83c67a14 Mon Sep 17 00:00:00 2001 From: keisku Date: Tue, 21 Jan 2025 14:08:32 +0000 Subject: [PATCH 16/24] typo --- contrib/valkey-go/valkey.go | 4 ++-- contrib/valkey-go/valkey_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/valkey-go/valkey.go b/contrib/valkey-go/valkey.go index 0455863095..6e62bc8f3c 100644 --- a/contrib/valkey-go/valkey.go +++ b/contrib/valkey-go/valkey.go @@ -3,7 +3,7 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016 Datadog, Inc. -// Package valkey provides tracing functions for tracing the valkey/valkey-go package (https://github.com/valkey-io/valkey-go). +// Package valkey provides tracing functions for tracing the valkey-io/valkey-go package (https://github.com/valkey-io/valkey-go). package valkey import ( @@ -21,7 +21,7 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry" ) -const componentName = "valkey-go/valkey" +const componentName = "valkey-io/valkey-go" func init() { telemetry.LoadIntegration(componentName) diff --git a/contrib/valkey-go/valkey_test.go b/contrib/valkey-go/valkey_test.go index e534cd1926..0c924d3b04 100644 --- a/contrib/valkey-go/valkey_test.go +++ b/contrib/valkey-go/valkey_test.go @@ -343,7 +343,7 @@ func TestNewClient(t *testing.T) { assert.Equal(t, "valkey.command", span.OperationName()) assert.Equal(t, "client", span.Tag(ext.SpanKind)) assert.Equal(t, ext.SpanTypeValkey, span.Tag(ext.SpanType)) - assert.Equal(t, "valkey-go/valkey", span.Tag(ext.Component)) + assert.Equal(t, "valkey-io/valkey-go", span.Tag(ext.Component)) assert.Equal(t, "valkey", span.Tag(ext.DBSystem)) } }) From 27b0fec7f204fd9f6f5a6b78b723067fb9456e96 Mon Sep 17 00:00:00 2001 From: keisku Date: Thu, 23 Jan 2025 08:50:18 +0000 Subject: [PATCH 17/24] remove valkeyotel tags --- contrib/valkey-go/valkey.go | 46 +++++++++++--------------------- contrib/valkey-go/valkey_test.go | 12 --------- 2 files changed, 15 insertions(+), 43 deletions(-) diff --git a/contrib/valkey-go/valkey.go b/contrib/valkey-go/valkey.go index 6e62bc8f3c..1fa9114790 100644 --- a/contrib/valkey-go/valkey.go +++ b/contrib/valkey-go/valkey.go @@ -94,31 +94,30 @@ type commander interface { Commands() []string } -func processCmd(commander commander) (command, statement string, size int) { +func processCmd(commander commander) (command, statement string) { commands := commander.Commands() if len(commands) == 0 { - return "", "", 0 + return "", "" } command = commands[0] statement = strings.Join(commands, "\n") - return command, statement, len(statement) + return command, statement } -func processMultiCmds(multi []commander) (command, statement string, size int) { +func processMultiCmds(multi []commander) (command, statement string) { var commands []string var statements []string for _, cmd := range multi { - cmdStr, stmt, cmdSize := processCmd(cmd) - size += cmdSize + cmdStr, stmt := processCmd(cmd) commands = append(commands, cmdStr) statements = append(statements, stmt) } command = strings.Join(commands, " ") statement = strings.Join(statements, "\n") - return command, statement, size + return command, statement } -func processMultiCompleted(multi ...valkey.Completed) (command, statement string, size int) { +func processMultiCompleted(multi ...valkey.Completed) (command, statement string) { cmds := make([]commander, len(multi)) for i, cmd := range multi { cmds[i] = &cmd @@ -126,7 +125,7 @@ func processMultiCompleted(multi ...valkey.Completed) (command, statement string return processMultiCmds(cmds) } -func processMultiCacheableTTL(multi ...valkey.CacheableTTL) (command, statement string, size int) { +func processMultiCacheableTTL(multi ...valkey.CacheableTTL) (command, statement string) { cmds := make([]commander, len(multi)) for i, cmd := range multi { cmds[i] = &cmd.Cmd @@ -153,7 +152,6 @@ func setClientCacheTags(s tracer.Span, result valkey.ValkeyResult) { type buildStartSpanOptionsInput struct { command string statement string - size int skipRawCommand bool } @@ -190,13 +188,6 @@ func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []t tracer.Tag("db.out", c.option.SelectDB), } opts = append(opts, c.peerTags()...) - if input.command != "" { - opts = append(opts, []tracer.StartSpanOption{ - // valkeyotel tags - tracer.Tag("db.stmt_size", input.size), - tracer.Tag("db.operation", input.command), - }...) - } if input.skipRawCommand { opts = append(opts, tracer.Tag(ext.ValkeyRawCommand, input.skipRawCommand)) } @@ -207,11 +198,10 @@ func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []t } func (c *coreClient) Do(ctx context.Context, cmd valkey.Completed) (resp valkey.ValkeyResult) { - command, statement, size := processCmd(&cmd) + command, statement := processCmd(&cmd) span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ command: command, statement: statement, - size: size, skipRawCommand: c.clientConfig.skipRaw, })...) resp = c.Client.Do(ctx, cmd) @@ -221,11 +211,10 @@ func (c *coreClient) Do(ctx context.Context, cmd valkey.Completed) (resp valkey. } func (c *coreClient) DoMulti(ctx context.Context, multi ...valkey.Completed) (resp []valkey.ValkeyResult) { - command, statement, size := processMultiCompleted(multi...) + command, statement := processMultiCompleted(multi...) span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ command: command, statement: statement, - size: size, skipRawCommand: c.clientConfig.skipRaw, })...) resp = c.Client.DoMulti(ctx, multi...) @@ -234,11 +223,10 @@ func (c *coreClient) DoMulti(ctx context.Context, multi ...valkey.Completed) (re } func (c *coreClient) Receive(ctx context.Context, subscribe valkey.Completed, fn func(msg valkey.PubSubMessage)) (err error) { - command, statement, size := processCmd(&subscribe) + command, statement := processCmd(&subscribe) span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ command: command, statement: statement, - size: size, skipRawCommand: c.clientConfig.skipRaw, })...) err = c.Client.Receive(ctx, subscribe, fn) @@ -247,11 +235,10 @@ func (c *coreClient) Receive(ctx context.Context, subscribe valkey.Completed, fn } func (c *client) DoCache(ctx context.Context, cmd valkey.Cacheable, ttl time.Duration) (resp valkey.ValkeyResult) { - command, statement, size := processCmd(&cmd) + command, statement := processCmd(&cmd) span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ command: command, statement: statement, - size: size, skipRawCommand: c.clientConfig.skipRaw, })...) resp = c.Client.DoCache(ctx, cmd, ttl) @@ -261,11 +248,10 @@ func (c *client) DoCache(ctx context.Context, cmd valkey.Cacheable, ttl time.Dur } func (c *client) DoMultiCache(ctx context.Context, multi ...valkey.CacheableTTL) (resp []valkey.ValkeyResult) { - command, statement, size := processMultiCacheableTTL(multi...) + command, statement := processMultiCacheableTTL(multi...) span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ command: command, statement: statement, - size: size, skipRawCommand: c.clientConfig.skipRaw, })...) resp = c.Client.DoMultiCache(ctx, multi...) @@ -274,11 +260,10 @@ func (c *client) DoMultiCache(ctx context.Context, multi ...valkey.CacheableTTL) } func (c *client) DoStream(ctx context.Context, cmd valkey.Completed) (resp valkey.ValkeyResultStream) { - command, statement, size := processCmd(&cmd) + command, statement := processCmd(&cmd) span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ command: command, statement: statement, - size: size, skipRawCommand: c.clientConfig.skipRaw, })...) resp = c.Client.DoStream(ctx, cmd) @@ -287,11 +272,10 @@ func (c *client) DoStream(ctx context.Context, cmd valkey.Completed) (resp valke } func (c *client) DoMultiStream(ctx context.Context, multi ...valkey.Completed) (resp valkey.MultiValkeyResultStream) { - command, statement, size := processMultiCompleted(multi...) + command, statement := processMultiCompleted(multi...) span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ command: command, statement: statement, - size: size, skipRawCommand: c.clientConfig.skipRaw, })...) resp = c.Client.DoMultiStream(ctx, multi...) diff --git a/contrib/valkey-go/valkey_test.go b/contrib/valkey-go/valkey_test.go index 0c924d3b04..f9cb55c212 100644 --- a/contrib/valkey-go/valkey_test.go +++ b/contrib/valkey-go/valkey_test.go @@ -163,8 +163,6 @@ func TestNewClient(t *testing.T) { func(t *testing.T, span mocktracer.Span) { assert.Equal(t, "SET\ntest_key\ntest_value", span.Tag(ext.DBStatement)) assert.Equal(t, "SET\ntest_key\ntest_value", span.Tag(ext.ResourceName)) - assert.Greater(t, span.Tag("db.stmt_size"), 0) - assert.Equal(t, "SET", span.Tag("db.operation")) assert.True(t, span.Tag(ext.ValkeyRawCommand).(bool)) assert.False(t, span.Tag(ext.ValkeyClientCacheHit).(bool)) assert.Less(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) @@ -190,8 +188,6 @@ func TestNewClient(t *testing.T) { func(t *testing.T, span mocktracer.Span) { assert.Equal(t, "SET\ntest_key\ntest_value\nGET\ntest_key", span.Tag(ext.DBStatement)) assert.Equal(t, "SET\ntest_key\ntest_value\nGET\ntest_key", span.Tag(ext.ResourceName)) - assert.Greater(t, span.Tag("db.stmt_size"), 0) - assert.Equal(t, "SET GET", span.Tag("db.operation")) assert.Nil(t, span.Tag(ext.ValkeyRawCommand)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) @@ -216,10 +212,8 @@ func TestNewClient(t *testing.T) { }, assertSpans: []func(t *testing.T, span mocktracer.Span){ func(t *testing.T, span mocktracer.Span) { - assert.Greater(t, span.Tag("db.stmt_size"), 0) assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.DBStatement)) assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.ResourceName)) - assert.Equal(t, "HMGET", span.Tag("db.operation")) assert.False(t, span.Tag(ext.ValkeyClientCacheHit).(bool)) assert.Greater(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) assert.Greater(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) @@ -227,10 +221,8 @@ func TestNewClient(t *testing.T) { assert.Nil(t, span.Tag(ext.Error)) }, func(t *testing.T, span mocktracer.Span) { - assert.Greater(t, span.Tag("db.stmt_size"), 0) assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.DBStatement)) assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.ResourceName)) - assert.Equal(t, "HMGET", span.Tag("db.operation")) assert.Nil(t, span.Tag(ext.ValkeyRawCommand)) assert.True(t, span.Tag(ext.ValkeyClientCacheHit).(bool)) assert.Greater(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) @@ -258,8 +250,6 @@ func TestNewClient(t *testing.T) { func(t *testing.T, span mocktracer.Span) { assert.Equal(t, "GET\ntest_key", span.Tag(ext.DBStatement)) assert.Equal(t, "GET\ntest_key", span.Tag(ext.ResourceName)) - assert.Greater(t, span.Tag("db.stmt_size"), 0) - assert.Equal(t, "GET", span.Tag("db.operation")) assert.True(t, span.Tag(ext.ValkeyRawCommand).(bool)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) @@ -286,10 +276,8 @@ func TestNewClient(t *testing.T) { }, assertSpans: []func(t *testing.T, span mocktracer.Span){ func(t *testing.T, span mocktracer.Span) { - assert.Greater(t, span.Tag("db.stmt_size"), 0) assert.Equal(t, "SUBSCRIBE\ntest_channel", span.Tag(ext.DBStatement)) assert.Equal(t, "SUBSCRIBE\ntest_channel", span.Tag(ext.ResourceName)) - assert.Equal(t, "SUBSCRIBE", span.Tag("db.operation")) assert.Nil(t, span.Tag(ext.ValkeyRawCommand)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) From 06360d23af15c6431d8e175ffdbdefadbcd7274b Mon Sep 17 00:00:00 2001 From: keisku Date: Thu, 23 Jan 2025 14:35:41 +0000 Subject: [PATCH 18/24] rename to out.db (ext.TargetDB) --- contrib/go-redis/redis.v7/redis.go | 4 ++-- contrib/go-redis/redis.v8/redis.go | 4 ++-- contrib/go-redis/redis/redis.go | 4 ++-- contrib/redis/go-redis.v9/redis.go | 4 ++-- contrib/valkey-go/valkey.go | 2 +- contrib/valkey-go/valkey_test.go | 2 +- ddtrace/ext/tags.go | 3 +++ 7 files changed, 13 insertions(+), 10 deletions(-) diff --git a/contrib/go-redis/redis.v7/redis.go b/contrib/go-redis/redis.v7/redis.go index 5376ae5e9c..4c003bebd3 100644 --- a/contrib/go-redis/redis.v7/redis.go +++ b/contrib/go-redis/redis.v7/redis.go @@ -79,7 +79,7 @@ func additionalTagOptions(client redis.UniversalClient) []ddtrace.StartSpanOptio opt := clientOptions.Options() if opt.Addr == "FailoverClient" { additionalTags = []ddtrace.StartSpanOption{ - tracer.Tag("out.db", strconv.Itoa(opt.DB)), + tracer.Tag(ext.TargetDB, strconv.Itoa(opt.DB)), tracer.Tag(ext.RedisDatabaseIndex, opt.DB), } } else { @@ -91,7 +91,7 @@ func additionalTagOptions(client redis.UniversalClient) []ddtrace.StartSpanOptio additionalTags = []ddtrace.StartSpanOption{ tracer.Tag(ext.TargetHost, host), tracer.Tag(ext.TargetPort, port), - tracer.Tag("out.db", strconv.Itoa(opt.DB)), + tracer.Tag(ext.TargetDB, strconv.Itoa(opt.DB)), tracer.Tag(ext.RedisDatabaseIndex, opt.DB), } } diff --git a/contrib/go-redis/redis.v8/redis.go b/contrib/go-redis/redis.v8/redis.go index a3ec7bc1c0..aa3ba4e6e7 100644 --- a/contrib/go-redis/redis.v8/redis.go +++ b/contrib/go-redis/redis.v8/redis.go @@ -78,7 +78,7 @@ func additionalTagOptions(client redis.UniversalClient) []ddtrace.StartSpanOptio opt := clientOptions.Options() if opt.Addr == "FailoverClient" { additionalTags = []ddtrace.StartSpanOption{ - tracer.Tag("out.db", strconv.Itoa(opt.DB)), + tracer.Tag(ext.TargetDB, strconv.Itoa(opt.DB)), tracer.Tag(ext.RedisDatabaseIndex, opt.DB), } } else { @@ -90,7 +90,7 @@ func additionalTagOptions(client redis.UniversalClient) []ddtrace.StartSpanOptio additionalTags = []ddtrace.StartSpanOption{ tracer.Tag(ext.TargetHost, host), tracer.Tag(ext.TargetPort, port), - tracer.Tag("out.db", strconv.Itoa(opt.DB)), + tracer.Tag(ext.TargetDB, strconv.Itoa(opt.DB)), tracer.Tag(ext.RedisDatabaseIndex, opt.DB), } } diff --git a/contrib/go-redis/redis/redis.go b/contrib/go-redis/redis/redis.go index 56feb6dcfd..9e40db15ba 100644 --- a/contrib/go-redis/redis/redis.go +++ b/contrib/go-redis/redis/redis.go @@ -130,7 +130,7 @@ func (c *Pipeliner) execWithContext(ctx context.Context) ([]redis.Cmder, error) tracer.ResourceName("redis"), tracer.Tag(ext.TargetHost, p.host), tracer.Tag(ext.TargetPort, p.port), - tracer.Tag("out.db", strconv.Itoa(p.db)), + tracer.Tag(ext.TargetDB, strconv.Itoa(p.db)), tracer.Tag(ext.Component, componentName), tracer.Tag(ext.SpanKind, ext.SpanKindClient), tracer.Tag(ext.DBSystem, ext.DBSystemRedis), @@ -202,7 +202,7 @@ func createWrapperFromClient(tc *Client) func(oldProcess func(cmd redis.Cmder) e tracer.ResourceName(parts[0]), tracer.Tag(ext.TargetHost, p.host), tracer.Tag(ext.TargetPort, p.port), - tracer.Tag("out.db", strconv.Itoa(p.db)), + tracer.Tag(ext.TargetDB, strconv.Itoa(p.db)), tracer.Tag("redis.raw_command", raw), tracer.Tag("redis.args_length", strconv.Itoa(length)), tracer.Tag(ext.Component, componentName), diff --git a/contrib/redis/go-redis.v9/redis.go b/contrib/redis/go-redis.v9/redis.go index 97a4fa6b10..848371533a 100644 --- a/contrib/redis/go-redis.v9/redis.go +++ b/contrib/redis/go-redis.v9/redis.go @@ -78,7 +78,7 @@ func additionalTagOptions(client redis.UniversalClient) []ddtrace.StartSpanOptio opt := clientOptions.Options() if opt.Addr == "FailoverClient" { additionalTags = []ddtrace.StartSpanOption{ - tracer.Tag("out.db", strconv.Itoa(opt.DB)), + tracer.Tag(ext.TargetDB, strconv.Itoa(opt.DB)), } } else { host, port, err := net.SplitHostPort(opt.Addr) @@ -89,7 +89,7 @@ func additionalTagOptions(client redis.UniversalClient) []ddtrace.StartSpanOptio additionalTags = []ddtrace.StartSpanOption{ tracer.Tag(ext.TargetHost, host), tracer.Tag(ext.TargetPort, port), - tracer.Tag("out.db", strconv.Itoa(opt.DB)), + tracer.Tag(ext.TargetDB, strconv.Itoa(opt.DB)), } } } else if clientOptions, ok := client.(clusterOptions); ok { diff --git a/contrib/valkey-go/valkey.go b/contrib/valkey-go/valkey.go index 1fa9114790..583cb9566e 100644 --- a/contrib/valkey-go/valkey.go +++ b/contrib/valkey-go/valkey.go @@ -179,13 +179,13 @@ func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []t tracer.SpanType(ext.SpanTypeValkey), tracer.Tag(ext.TargetHost, c.host), tracer.Tag(ext.TargetPort, c.port), + tracer.Tag(ext.TargetDB, c.option.SelectDB), tracer.Tag(ext.ValkeyClientVersion, valkey.LibVer), tracer.Tag(ext.ValkeyClientName, valkey.LibName), tracer.Tag(ext.Component, componentName), tracer.Tag(ext.SpanKind, ext.SpanKindClient), tracer.Tag(ext.DBSystem, ext.DBSystemValkey), tracer.Tag(ext.ValkeyDatabaseIndex, c.option.SelectDB), - tracer.Tag("db.out", c.option.SelectDB), } opts = append(opts, c.peerTags()...) if input.skipRawCommand { diff --git a/contrib/valkey-go/valkey_test.go b/contrib/valkey-go/valkey_test.go index f9cb55c212..0a3e8d4f8c 100644 --- a/contrib/valkey-go/valkey_test.go +++ b/contrib/valkey-go/valkey_test.go @@ -325,7 +325,7 @@ func TestNewClient(t *testing.T) { assert.Equal(t, valkeyPort, span.Tag(ext.PeerPort)) assert.Equal(t, valkeyPort, span.Tag(ext.TargetPort)) assert.Equal(t, 0, span.Tag(ext.ValkeyDatabaseIndex)) - assert.Equal(t, 0, span.Tag("db.out")) + assert.Equal(t, 0, span.Tag(ext.TargetDB)) assert.NotNil(t, span) assert.Equal(t, tt.valkeyClientOptions.Username, span.Tag(ext.DBUser)) assert.Equal(t, "valkey.command", span.OperationName()) diff --git a/ddtrace/ext/tags.go b/ddtrace/ext/tags.go index 375d7df7b5..a22d191f0f 100644 --- a/ddtrace/ext/tags.go +++ b/ddtrace/ext/tags.go @@ -22,6 +22,9 @@ const ( // Deprecated: Use NetworkDestinationPort instead. TargetPort = "out.port" + // TargetPort sets the target db. + TargetDB = "out.db" + // NetworkDestinationPort is the remote port number of the outbound connection. NetworkDestinationPort = "network.destination.port" From b8c5c35ead346fb3b88d6b9c31cf0f4a6a68c833 Mon Sep 17 00:00:00 2001 From: keisku Date: Thu, 23 Jan 2025 14:38:14 +0000 Subject: [PATCH 19/24] remove peer.* tags --- contrib/valkey-go/valkey.go | 18 ------- contrib/valkey-go/valkey_test.go | 82 -------------------------------- 2 files changed, 100 deletions(-) diff --git a/contrib/valkey-go/valkey.go b/contrib/valkey-go/valkey.go index 583cb9566e..6b24e9cab2 100644 --- a/contrib/valkey-go/valkey.go +++ b/contrib/valkey-go/valkey.go @@ -155,23 +155,6 @@ type buildStartSpanOptionsInput struct { skipRawCommand bool } -func (c *coreClient) peerTags() []tracer.StartSpanOption { - ipAddr := net.ParseIP(c.host) - var peerHostKey string - if ipAddr == nil { - peerHostKey = ext.PeerHostname - } else if ipAddr.To4() != nil { - peerHostKey = ext.PeerHostIPV4 - } else { - peerHostKey = ext.PeerHostIPV6 - } - return []tracer.StartSpanOption{ - tracer.Tag(ext.PeerService, ext.DBSystemValkey), - tracer.Tag(peerHostKey, c.host), - tracer.Tag(ext.PeerPort, c.port), - } -} - func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []tracer.StartSpanOption { opts := []tracer.StartSpanOption{ tracer.ResourceName(input.statement), @@ -187,7 +170,6 @@ func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []t tracer.Tag(ext.DBSystem, ext.DBSystemValkey), tracer.Tag(ext.ValkeyDatabaseIndex, c.option.SelectDB), } - opts = append(opts, c.peerTags()...) if input.skipRawCommand { opts = append(opts, tracer.Tag(ext.ValkeyRawCommand, input.skipRawCommand)) } diff --git a/contrib/valkey-go/valkey_test.go b/contrib/valkey-go/valkey_test.go index 0a3e8d4f8c..ff91fc35b4 100644 --- a/contrib/valkey-go/valkey_test.go +++ b/contrib/valkey-go/valkey_test.go @@ -14,7 +14,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/valkey-io/valkey-go" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/mocktracer" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" @@ -36,84 +35,6 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -func TestPeerTags(t *testing.T) { - tests := []struct { - initAddress string - expectedTags map[string]interface{} - }{ - { - initAddress: "127.0.0.1:6379", - expectedTags: map[string]interface{}{ - ext.PeerService: "valkey", - ext.PeerHostIPV4: "127.0.0.1", - ext.PeerPort: 6379, - }, - }, - { - initAddress: "[::1]:6379", - expectedTags: map[string]interface{}{ - ext.PeerService: "valkey", - ext.PeerHostIPV6: "::1", - ext.PeerPort: 6379, - }, - }, - { - initAddress: "[2001:db8::2]:6379", - expectedTags: map[string]interface{}{ - ext.PeerService: "valkey", - ext.PeerHostIPV6: "2001:db8::2", - ext.PeerPort: 6379, - }, - }, - { - initAddress: "[2001:db8::2%lo]:6379", - expectedTags: map[string]interface{}{ - ext.PeerService: "valkey", - ext.PeerHostname: "2001:db8::2%lo", - ext.PeerPort: 6379, - }, - }, - { - initAddress: "::1:7777", - expectedTags: map[string]interface{}{ - ext.PeerService: "valkey", - ext.PeerHostname: "", - ext.PeerPort: 0, - }, - }, - { - initAddress: ":::7777", - expectedTags: map[string]interface{}{ - ext.PeerService: "valkey", - ext.PeerHostname: "", - ext.PeerPort: 0, - }, - }, - { - initAddress: "localhost:7777", - expectedTags: map[string]interface{}{ - ext.PeerService: "valkey", - ext.PeerHostname: "localhost", - ext.PeerPort: 7777, - }, - }, - } - for _, tt := range tests { - t.Run(tt.initAddress, func(t *testing.T) { - host, port := splitHostPort(tt.initAddress) - client := coreClient{ - host: host, - port: port, - } - var startSpanConfig ddtrace.StartSpanConfig - for _, tag := range client.peerTags() { - tag(&startSpanConfig) - } - require.Equal(t, tt.expectedTags, startSpanConfig.Tags) - }) - } -} - func TestNewClient(t *testing.T) { tests := []struct { name string @@ -319,10 +240,7 @@ func TestNewClient(t *testing.T) { span.Tag(ext.ServiceName), "service name should not be overwritten as per DD_APM_PEER_TAGS_AGGREGATION in trace-agent", ) - assert.Equal(t, "valkey", span.Tag(ext.PeerService)) - assert.Equal(t, "127.0.0.1", span.Tag(ext.PeerHostIPV4)) assert.Equal(t, "127.0.0.1", span.Tag(ext.TargetHost)) - assert.Equal(t, valkeyPort, span.Tag(ext.PeerPort)) assert.Equal(t, valkeyPort, span.Tag(ext.TargetPort)) assert.Equal(t, 0, span.Tag(ext.ValkeyDatabaseIndex)) assert.Equal(t, 0, span.Tag(ext.TargetDB)) From fba84dfe7fb4e8081c2eff907c78940552a254d6 Mon Sep 17 00:00:00 2001 From: keisku Date: Thu, 23 Jan 2025 14:47:42 +0000 Subject: [PATCH 20/24] rework DD_TRACE_VALKEY_RAW_COMMAND behavior --- contrib/valkey-go/option.go | 14 +++---- contrib/valkey-go/valkey.go | 63 ++++++++++++++++++-------------- contrib/valkey-go/valkey_test.go | 27 +++++++------- 3 files changed, 56 insertions(+), 48 deletions(-) diff --git a/contrib/valkey-go/option.go b/contrib/valkey-go/option.go index 9c1d6e1e6b..965346a266 100644 --- a/contrib/valkey-go/option.go +++ b/contrib/valkey-go/option.go @@ -10,21 +10,21 @@ import ( ) type clientConfig struct { - skipRaw bool + rawCommand bool } // ClientOption represents an option that can be used to create or wrap a client. type ClientOption func(*clientConfig) func defaults(cfg *clientConfig) { - cfg.skipRaw = internal.BoolEnv("DD_TRACE_VALKEY_SKIP_RAW_COMMAND", false) + // Unless Agent supports obfuscation for valkey, we should not enable raw command. + cfg.rawCommand = internal.BoolEnv("DD_TRACE_VALKEY_RAW_COMMAND", false) } -// WithSkipRawCommand reports whether to skip setting the raw command value -// on instrumenation spans. This may be useful if the Datadog Agent is not -// set up to obfuscate this value and it could contain sensitive information. -func WithSkipRawCommand(skip bool) ClientOption { +// WithRawCommand reports whether to keep the raw command value +// on instrumenation spans. +func WithRawCommand(rawCommand bool) ClientOption { return func(cfg *clientConfig) { - cfg.skipRaw = skip + cfg.rawCommand = rawCommand } } diff --git a/contrib/valkey-go/valkey.go b/contrib/valkey-go/valkey.go index 6b24e9cab2..58db88dd59 100644 --- a/contrib/valkey-go/valkey.go +++ b/contrib/valkey-go/valkey.go @@ -150,15 +150,13 @@ func setClientCacheTags(s tracer.Span, result valkey.ValkeyResult) { } type buildStartSpanOptionsInput struct { - command string - statement string - skipRawCommand bool + command string + statement string + rawCommand bool } func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []tracer.StartSpanOption { opts := []tracer.StartSpanOption{ - tracer.ResourceName(input.statement), - tracer.Tag(ext.DBStatement, input.statement), tracer.SpanType(ext.SpanTypeValkey), tracer.Tag(ext.TargetHost, c.host), tracer.Tag(ext.TargetPort, c.port), @@ -169,9 +167,18 @@ func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []t tracer.Tag(ext.SpanKind, ext.SpanKindClient), tracer.Tag(ext.DBSystem, ext.DBSystemValkey), tracer.Tag(ext.ValkeyDatabaseIndex, c.option.SelectDB), + tracer.Tag(ext.ValkeyRawCommand, input.rawCommand), } - if input.skipRawCommand { - opts = append(opts, tracer.Tag(ext.ValkeyRawCommand, input.skipRawCommand)) + if c.clientConfig.rawCommand { + opts = append(opts, []tracer.StartSpanOption{ + tracer.ResourceName(input.statement), + tracer.Tag(ext.DBStatement, input.statement), + }...) + } else { + opts = append(opts, []tracer.StartSpanOption{ + tracer.ResourceName(input.command), + tracer.Tag(ext.DBStatement, input.command), + }...) } if c.option.Username != "" { opts = append(opts, tracer.Tag(ext.DBUser, c.option.Username)) @@ -182,9 +189,9 @@ func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []t func (c *coreClient) Do(ctx context.Context, cmd valkey.Completed) (resp valkey.ValkeyResult) { command, statement := processCmd(&cmd) span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ - command: command, - statement: statement, - skipRawCommand: c.clientConfig.skipRaw, + command: command, + statement: statement, + rawCommand: c.clientConfig.rawCommand, })...) resp = c.Client.Do(ctx, cmd) setClientCacheTags(span, resp) @@ -195,9 +202,9 @@ func (c *coreClient) Do(ctx context.Context, cmd valkey.Completed) (resp valkey. func (c *coreClient) DoMulti(ctx context.Context, multi ...valkey.Completed) (resp []valkey.ValkeyResult) { command, statement := processMultiCompleted(multi...) span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ - command: command, - statement: statement, - skipRawCommand: c.clientConfig.skipRaw, + command: command, + statement: statement, + rawCommand: c.clientConfig.rawCommand, })...) resp = c.Client.DoMulti(ctx, multi...) defer span.Finish(tracer.WithError(firstError(resp))) @@ -207,9 +214,9 @@ func (c *coreClient) DoMulti(ctx context.Context, multi ...valkey.Completed) (re func (c *coreClient) Receive(ctx context.Context, subscribe valkey.Completed, fn func(msg valkey.PubSubMessage)) (err error) { command, statement := processCmd(&subscribe) span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ - command: command, - statement: statement, - skipRawCommand: c.clientConfig.skipRaw, + command: command, + statement: statement, + rawCommand: c.clientConfig.rawCommand, })...) err = c.Client.Receive(ctx, subscribe, fn) defer span.Finish(tracer.WithError(err)) @@ -219,9 +226,9 @@ func (c *coreClient) Receive(ctx context.Context, subscribe valkey.Completed, fn func (c *client) DoCache(ctx context.Context, cmd valkey.Cacheable, ttl time.Duration) (resp valkey.ValkeyResult) { command, statement := processCmd(&cmd) span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ - command: command, - statement: statement, - skipRawCommand: c.clientConfig.skipRaw, + command: command, + statement: statement, + rawCommand: c.clientConfig.rawCommand, })...) resp = c.Client.DoCache(ctx, cmd, ttl) setClientCacheTags(span, resp) @@ -232,9 +239,9 @@ func (c *client) DoCache(ctx context.Context, cmd valkey.Cacheable, ttl time.Dur func (c *client) DoMultiCache(ctx context.Context, multi ...valkey.CacheableTTL) (resp []valkey.ValkeyResult) { command, statement := processMultiCacheableTTL(multi...) span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ - command: command, - statement: statement, - skipRawCommand: c.clientConfig.skipRaw, + command: command, + statement: statement, + rawCommand: c.clientConfig.rawCommand, })...) resp = c.Client.DoMultiCache(ctx, multi...) defer span.Finish(tracer.WithError(firstError(resp))) @@ -244,9 +251,9 @@ func (c *client) DoMultiCache(ctx context.Context, multi ...valkey.CacheableTTL) func (c *client) DoStream(ctx context.Context, cmd valkey.Completed) (resp valkey.ValkeyResultStream) { command, statement := processCmd(&cmd) span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ - command: command, - statement: statement, - skipRawCommand: c.clientConfig.skipRaw, + command: command, + statement: statement, + rawCommand: c.clientConfig.rawCommand, })...) resp = c.Client.DoStream(ctx, cmd) defer span.Finish(tracer.WithError(resp.Error())) @@ -256,9 +263,9 @@ func (c *client) DoStream(ctx context.Context, cmd valkey.Completed) (resp valke func (c *client) DoMultiStream(ctx context.Context, multi ...valkey.Completed) (resp valkey.MultiValkeyResultStream) { command, statement := processMultiCompleted(multi...) span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ - command: command, - statement: statement, - skipRawCommand: c.clientConfig.skipRaw, + command: command, + statement: statement, + rawCommand: c.clientConfig.rawCommand, })...) resp = c.Client.DoMultiStream(ctx, multi...) defer span.Finish(tracer.WithError(resp.Error())) diff --git a/contrib/valkey-go/valkey_test.go b/contrib/valkey-go/valkey_test.go index ff91fc35b4..a48a36ce22 100644 --- a/contrib/valkey-go/valkey_test.go +++ b/contrib/valkey-go/valkey_test.go @@ -75,7 +75,7 @@ func TestNewClient(t *testing.T) { Password: valkeyPassword, }, valkeytraceClientOptions: []ClientOption{ - WithSkipRawCommand(true), + WithRawCommand(true), }, createSpans: func(t *testing.T, ctx context.Context, client valkey.Client) { assert.NoError(t, client.Do(ctx, client.B().Set().Key("test_key").Value("test_value").Build()).Error()) @@ -107,9 +107,9 @@ func TestNewClient(t *testing.T) { }, assertSpans: []func(t *testing.T, span mocktracer.Span){ func(t *testing.T, span mocktracer.Span) { - assert.Equal(t, "SET\ntest_key\ntest_value\nGET\ntest_key", span.Tag(ext.DBStatement)) - assert.Equal(t, "SET\ntest_key\ntest_value\nGET\ntest_key", span.Tag(ext.ResourceName)) - assert.Nil(t, span.Tag(ext.ValkeyRawCommand)) + assert.Equal(t, "SET GET", span.Tag(ext.DBStatement)) + assert.Equal(t, "SET GET", span.Tag(ext.ResourceName)) + assert.False(t, span.Tag(ext.ValkeyRawCommand).(bool)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) @@ -133,8 +133,9 @@ func TestNewClient(t *testing.T) { }, assertSpans: []func(t *testing.T, span mocktracer.Span){ func(t *testing.T, span mocktracer.Span) { - assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.DBStatement)) - assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.ResourceName)) + assert.Equal(t, "HMGET", span.Tag(ext.DBStatement)) + assert.Equal(t, "HMGET", span.Tag(ext.ResourceName)) + assert.False(t, span.Tag(ext.ValkeyRawCommand).(bool)) assert.False(t, span.Tag(ext.ValkeyClientCacheHit).(bool)) assert.Greater(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) assert.Greater(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) @@ -142,9 +143,9 @@ func TestNewClient(t *testing.T) { assert.Nil(t, span.Tag(ext.Error)) }, func(t *testing.T, span mocktracer.Span) { - assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.DBStatement)) - assert.Equal(t, "HMGET\nmk\n1\n2", span.Tag(ext.ResourceName)) - assert.Nil(t, span.Tag(ext.ValkeyRawCommand)) + assert.Equal(t, "HMGET", span.Tag(ext.DBStatement)) + assert.Equal(t, "HMGET", span.Tag(ext.ResourceName)) + assert.False(t, span.Tag(ext.ValkeyRawCommand).(bool)) assert.True(t, span.Tag(ext.ValkeyClientCacheHit).(bool)) assert.Greater(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) assert.Greater(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) @@ -161,7 +162,7 @@ func TestNewClient(t *testing.T) { Password: valkeyPassword, }, valkeytraceClientEnvVars: map[string]string{ - "DD_TRACE_VALKEY_SKIP_RAW_COMMAND": "true", + "DD_TRACE_VALKEY_RAW_COMMAND": "true", }, createSpans: func(t *testing.T, ctx context.Context, client valkey.Client) { resp := client.DoStream(ctx, client.B().Get().Key("test_key").Build()) @@ -197,9 +198,9 @@ func TestNewClient(t *testing.T) { }, assertSpans: []func(t *testing.T, span mocktracer.Span){ func(t *testing.T, span mocktracer.Span) { - assert.Equal(t, "SUBSCRIBE\ntest_channel", span.Tag(ext.DBStatement)) - assert.Equal(t, "SUBSCRIBE\ntest_channel", span.Tag(ext.ResourceName)) - assert.Nil(t, span.Tag(ext.ValkeyRawCommand)) + assert.Equal(t, "SUBSCRIBE", span.Tag(ext.DBStatement)) + assert.Equal(t, "SUBSCRIBE", span.Tag(ext.ResourceName)) + assert.False(t, span.Tag(ext.ValkeyRawCommand).(bool)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) From 25088d1ad257152fb10ef53ef7a4844edf74e1e3 Mon Sep 17 00:00:00 2001 From: keisku Date: Thu, 23 Jan 2025 14:49:17 +0000 Subject: [PATCH 21/24] fix error handling when a span finishes --- contrib/valkey-go/valkey.go | 26 +++++++------- contrib/valkey-go/valkey_test.go | 59 ++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 13 deletions(-) diff --git a/contrib/valkey-go/valkey.go b/contrib/valkey-go/valkey.go index 58db88dd59..4d00fce708 100644 --- a/contrib/valkey-go/valkey.go +++ b/contrib/valkey-go/valkey.go @@ -186,40 +186,40 @@ func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []t return opts } -func (c *coreClient) Do(ctx context.Context, cmd valkey.Completed) (resp valkey.ValkeyResult) { +func (c *coreClient) Do(ctx context.Context, cmd valkey.Completed) valkey.ValkeyResult { command, statement := processCmd(&cmd) span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ command: command, statement: statement, rawCommand: c.clientConfig.rawCommand, })...) - resp = c.Client.Do(ctx, cmd) + resp := c.Client.Do(ctx, cmd) setClientCacheTags(span, resp) - defer span.Finish(tracer.WithError(resp.Error())) + span.Finish(tracer.WithError(resp.Error())) return resp } -func (c *coreClient) DoMulti(ctx context.Context, multi ...valkey.Completed) (resp []valkey.ValkeyResult) { +func (c *coreClient) DoMulti(ctx context.Context, multi ...valkey.Completed) []valkey.ValkeyResult { command, statement := processMultiCompleted(multi...) span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ command: command, statement: statement, rawCommand: c.clientConfig.rawCommand, })...) - resp = c.Client.DoMulti(ctx, multi...) - defer span.Finish(tracer.WithError(firstError(resp))) + resp := c.Client.DoMulti(ctx, multi...) + span.Finish(tracer.WithError(firstError(resp))) return resp } -func (c *coreClient) Receive(ctx context.Context, subscribe valkey.Completed, fn func(msg valkey.PubSubMessage)) (err error) { +func (c *coreClient) Receive(ctx context.Context, subscribe valkey.Completed, fn func(msg valkey.PubSubMessage)) error { command, statement := processCmd(&subscribe) span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ command: command, statement: statement, rawCommand: c.clientConfig.rawCommand, })...) - err = c.Client.Receive(ctx, subscribe, fn) - defer span.Finish(tracer.WithError(err)) + err := c.Client.Receive(ctx, subscribe, fn) + span.Finish(tracer.WithError(err)) return err } @@ -260,19 +260,19 @@ func (c *client) DoStream(ctx context.Context, cmd valkey.Completed) (resp valke return resp } -func (c *client) DoMultiStream(ctx context.Context, multi ...valkey.Completed) (resp valkey.MultiValkeyResultStream) { +func (c *client) DoMultiStream(ctx context.Context, multi ...valkey.Completed) valkey.MultiValkeyResultStream { command, statement := processMultiCompleted(multi...) span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ command: command, statement: statement, rawCommand: c.clientConfig.rawCommand, })...) - resp = c.Client.DoMultiStream(ctx, multi...) - defer span.Finish(tracer.WithError(resp.Error())) + resp := c.Client.DoMultiStream(ctx, multi...) + span.Finish(tracer.WithError(resp.Error())) return resp } -func (c *client) Dedicated(fn func(valkey.DedicatedClient) error) (err error) { +func (c *client) Dedicated(fn func(valkey.DedicatedClient) error) error { return c.Client.Dedicated(func(dc valkey.DedicatedClient) error { return fn(&dedicatedClient{ coreClient: c.coreClient, diff --git a/contrib/valkey-go/valkey_test.go b/contrib/valkey-go/valkey_test.go index a48a36ce22..c03a3cfc23 100644 --- a/contrib/valkey-go/valkey_test.go +++ b/contrib/valkey-go/valkey_test.go @@ -181,6 +181,65 @@ func TestNewClient(t *testing.T) { }, }, }, + { + name: "Test SET command with timeout", + valkeyClientOptions: valkey.ClientOption{ + InitAddress: []string{fmt.Sprintf("127.0.0.1:%d", valkeyPort)}, + Username: valkeyUsername, + Password: valkeyPassword, + }, + createSpans: func(t *testing.T, ctx context.Context, client valkey.Client) { + ctxWithTimeout, cancel := context.WithTimeout(ctx, time.Nanosecond) + client.Do(ctxWithTimeout, client.B().Set().Key("k1").Value("v1").Build()) + cancel() + }, + assertSpans: []func(t *testing.T, span mocktracer.Span){ + func(t *testing.T, span mocktracer.Span) { + assert.Equal(t, "SET", span.Tag(ext.DBStatement)) + assert.Equal(t, "SET", span.Tag(ext.ResourceName)) + assert.False(t, span.Tag(ext.ValkeyRawCommand).(bool)) + assert.False(t, span.Tag(ext.ValkeyClientCacheHit).(bool)) + assert.Less(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) + assert.Less(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) + assert.Less(t, span.Tag(ext.ValkeyClientCachePTTL), int64(0)) + assert.Equal(t, context.DeadlineExceeded, span.Tag(ext.Error).(error)) + }, + }, + }, + { + name: "Test SET/GET/SET/GET command with timeout and option", + valkeyClientOptions: valkey.ClientOption{ + InitAddress: []string{fmt.Sprintf("127.0.0.1:%d", valkeyPort)}, + Username: valkeyUsername, + Password: valkeyPassword, + }, + valkeytraceClientOptions: []ClientOption{ + WithRawCommand(true), + }, + createSpans: func(t *testing.T, ctx context.Context, client valkey.Client) { + ctxWithTimeout, cancel := context.WithTimeout(ctx, time.Nanosecond) + client.DoMulti( + ctxWithTimeout, + client.B().Set().Key("k1").Value("v1").Build(), + client.B().Get().Key("k1").Build(), + client.B().Set().Key("k2").Value("v2").Build(), + client.B().Get().Key("k2").Build(), + ) + cancel() + }, + assertSpans: []func(t *testing.T, span mocktracer.Span){ + func(t *testing.T, span mocktracer.Span) { + assert.Equal(t, "SET\nk1\nv1\nGET\nk1\nSET\nk2\nv2\nGET\nk2", span.Tag(ext.DBStatement)) + assert.Equal(t, "SET\nk1\nv1\nGET\nk1\nSET\nk2\nv2\nGET\nk2", span.Tag(ext.ResourceName)) + assert.True(t, span.Tag(ext.ValkeyRawCommand).(bool)) + assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) + assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) + assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) + assert.Nil(t, span.Tag(ext.ValkeyClientCachePTTL)) + assert.Equal(t, context.DeadlineExceeded, span.Tag(ext.Error).(error)) + }, + }, + }, { name: "Test SUBSCRIBE command with timeout", valkeyClientOptions: valkey.ClientOption{ From d9bb0114738e7e5f5450b0f8671d743526522aa6 Mon Sep 17 00:00:00 2001 From: Rodrigo Arguello Date: Mon, 27 Jan 2025 17:28:52 +0100 Subject: [PATCH 22/24] fix raw command + add missing service name option + refactor --- contrib/valkey-go/option.go | 34 ++- contrib/valkey-go/valkey.go | 403 ++++++++++++++----------------- contrib/valkey-go/valkey_test.go | 395 +++++++++++++++--------------- ddtrace/ext/db.go | 9 - ddtrace/ext/tags.go | 4 +- internal/namingschema/op.go | 5 - 6 files changed, 402 insertions(+), 448 deletions(-) diff --git a/contrib/valkey-go/option.go b/contrib/valkey-go/option.go index 965346a266..60766ef45a 100644 --- a/contrib/valkey-go/option.go +++ b/contrib/valkey-go/option.go @@ -7,24 +7,36 @@ package valkey import ( "gopkg.in/DataDog/dd-trace-go.v1/internal" + "gopkg.in/DataDog/dd-trace-go.v1/internal/namingschema" ) -type clientConfig struct { - rawCommand bool +type config struct { + rawCommand bool + serviceName string } -// ClientOption represents an option that can be used to create or wrap a client. -type ClientOption func(*clientConfig) +// Option represents an option that can be used to create or wrap a client. +type Option func(*config) -func defaults(cfg *clientConfig) { - // Unless Agent supports obfuscation for valkey, we should not enable raw command. - cfg.rawCommand = internal.BoolEnv("DD_TRACE_VALKEY_RAW_COMMAND", false) +func defaultConfig() *config { + return &config{ + // Do not include the raw command by default since it could contain sensitive data. + rawCommand: internal.BoolEnv("DD_TRACE_VALKEY_RAW_COMMAND", false), + serviceName: namingschema.ServiceName(defaultServiceName), + } } -// WithRawCommand reports whether to keep the raw command value -// on instrumenation spans. -func WithRawCommand(rawCommand bool) ClientOption { - return func(cfg *clientConfig) { +// WithRawCommand can be used to set a tag `valkey.raw_command` in the created spans (disabled by default). +// Warning: please note the datadog-agent currently does not support obfuscation for this tag, so use this at your own risk. +func WithRawCommand(rawCommand bool) Option { + return func(cfg *config) { cfg.rawCommand = rawCommand } } + +// WithServiceName sets the given service name for the client. +func WithServiceName(name string) Option { + return func(cfg *config) { + cfg.serviceName = name + } +} diff --git a/contrib/valkey-go/valkey.go b/contrib/valkey-go/valkey.go index 4d00fce708..5362b397e7 100644 --- a/contrib/valkey-go/valkey.go +++ b/contrib/valkey-go/valkey.go @@ -16,12 +16,13 @@ import ( "github.com/valkey-io/valkey-go" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" - "gopkg.in/DataDog/dd-trace-go.v1/internal/log" - "gopkg.in/DataDog/dd-trace-go.v1/internal/namingschema" "gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry" ) -const componentName = "valkey-io/valkey-go" +const ( + componentName = "valkey-io/valkey-go" + defaultServiceName = "valkey.client" +) func init() { telemetry.LoadIntegration(componentName) @@ -29,283 +30,255 @@ func init() { } var ( - _ valkey.CoreClient = (*coreClient)(nil) - _ valkey.Client = (*client)(nil) - _ valkey.DedicatedClient = (*dedicatedClient)(nil) + _ valkey.Client = (*client)(nil) ) -type coreClient struct { - valkey.Client - option valkey.ClientOption - clientConfig clientConfig - spanName string - host string - port int +type client struct { + client valkey.Client + cfg *config + host string + port string + dbIndex string + user string } -type client struct { - coreClient +func (c *client) B() valkey.Builder { + return c.client.B() } -type dedicatedClient struct { - coreClient - dedicatedClient valkey.DedicatedClient +func (c *client) Close() { + c.client.Close() } -func NewClient(option valkey.ClientOption, opts ...ClientOption) (valkey.Client, error) { - valkeyClient, err := valkey.NewClient(option) +// NewClient returns a new valkey.Client enhanced with tracing. +func NewClient(clientOption valkey.ClientOption, opts ...Option) (valkey.Client, error) { + valkeyClient, err := valkey.NewClient(clientOption) if err != nil { return nil, err } - var cfg clientConfig - defaults(&cfg) + cfg := defaultConfig() for _, fn := range opts { - fn(&cfg) - } - var host string - var port int - if len(option.InitAddress) == 1 { - host, port = splitHostPort(option.InitAddress[0]) - } - core := coreClient{ - Client: valkeyClient, - option: option, - clientConfig: cfg, - spanName: namingschema.OpName(namingschema.ValkeyOutbound), - host: host, - port: port, + fn(cfg) } - return &client{ - coreClient: core, - }, nil -} - -func splitHostPort(addr string) (string, int) { - host, portStr, err := net.SplitHostPort(addr) - if err != nil { - log.Error("%q cannot be split: %s", addr, err) - return "", 0 + tClient := &client{ + client: valkeyClient, + cfg: cfg, + dbIndex: strconv.FormatInt(int64(clientOption.SelectDB), 10), + user: clientOption.Username, } - port, _ := strconv.Atoi(portStr) - return host, port -} - -type commander interface { - Commands() []string -} - -func processCmd(commander commander) (command, statement string) { - commands := commander.Commands() - if len(commands) == 0 { - return "", "" - } - command = commands[0] - statement = strings.Join(commands, "\n") - return command, statement -} - -func processMultiCmds(multi []commander) (command, statement string) { - var commands []string - var statements []string - for _, cmd := range multi { - cmdStr, stmt := processCmd(cmd) - commands = append(commands, cmdStr) - statements = append(statements, stmt) - } - command = strings.Join(commands, " ") - statement = strings.Join(statements, "\n") - return command, statement -} - -func processMultiCompleted(multi ...valkey.Completed) (command, statement string) { - cmds := make([]commander, len(multi)) - for i, cmd := range multi { - cmds[i] = &cmd - } - return processMultiCmds(cmds) -} - -func processMultiCacheableTTL(multi ...valkey.CacheableTTL) (command, statement string) { - cmds := make([]commander, len(multi)) - for i, cmd := range multi { - cmds[i] = &cmd.Cmd - } - return processMultiCmds(cmds) -} - -func firstError(s []valkey.ValkeyResult) error { - for _, result := range s { - if err := result.Error(); err != nil && !valkey.IsValkeyNil(err) { - return err + if len(clientOption.InitAddress) > 0 { + host, port, err := net.SplitHostPort(clientOption.InitAddress[0]) + if err == nil { + tClient.host = host + tClient.port = port } } - return nil -} - -func setClientCacheTags(s tracer.Span, result valkey.ValkeyResult) { - s.SetTag(ext.ValkeyClientCacheHit, result.IsCacheHit()) - s.SetTag(ext.ValkeyClientCacheTTL, result.CacheTTL()) - s.SetTag(ext.ValkeyClientCachePTTL, result.CachePTTL()) - s.SetTag(ext.ValkeyClientCachePXAT, result.CachePXAT()) -} - -type buildStartSpanOptionsInput struct { - command string - statement string - rawCommand bool -} - -func (c *coreClient) buildStartSpanOptions(input buildStartSpanOptionsInput) []tracer.StartSpanOption { - opts := []tracer.StartSpanOption{ - tracer.SpanType(ext.SpanTypeValkey), - tracer.Tag(ext.TargetHost, c.host), - tracer.Tag(ext.TargetPort, c.port), - tracer.Tag(ext.TargetDB, c.option.SelectDB), - tracer.Tag(ext.ValkeyClientVersion, valkey.LibVer), - tracer.Tag(ext.ValkeyClientName, valkey.LibName), - tracer.Tag(ext.Component, componentName), - tracer.Tag(ext.SpanKind, ext.SpanKindClient), - tracer.Tag(ext.DBSystem, ext.DBSystemValkey), - tracer.Tag(ext.ValkeyDatabaseIndex, c.option.SelectDB), - tracer.Tag(ext.ValkeyRawCommand, input.rawCommand), - } - if c.clientConfig.rawCommand { - opts = append(opts, []tracer.StartSpanOption{ - tracer.ResourceName(input.statement), - tracer.Tag(ext.DBStatement, input.statement), - }...) - } else { - opts = append(opts, []tracer.StartSpanOption{ - tracer.ResourceName(input.command), - tracer.Tag(ext.DBStatement, input.command), - }...) - } - if c.option.Username != "" { - opts = append(opts, tracer.Tag(ext.DBUser, c.option.Username)) - } - return opts + return tClient, nil } -func (c *coreClient) Do(ctx context.Context, cmd valkey.Completed) valkey.ValkeyResult { - command, statement := processCmd(&cmd) - span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ - command: command, - statement: statement, - rawCommand: c.clientConfig.rawCommand, - })...) - resp := c.Client.Do(ctx, cmd) +func (c *client) Do(ctx context.Context, cmd valkey.Completed) valkey.ValkeyResult { + span, ctx := c.startSpan(ctx, processCommand(&cmd)) + resp := c.client.Do(ctx, cmd) setClientCacheTags(span, resp) span.Finish(tracer.WithError(resp.Error())) return resp } -func (c *coreClient) DoMulti(ctx context.Context, multi ...valkey.Completed) []valkey.ValkeyResult { - command, statement := processMultiCompleted(multi...) - span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ - command: command, - statement: statement, - rawCommand: c.clientConfig.rawCommand, - })...) - resp := c.Client.DoMulti(ctx, multi...) - span.Finish(tracer.WithError(firstError(resp))) +func (c *client) DoMulti(ctx context.Context, multi ...valkey.Completed) []valkey.ValkeyResult { + span, ctx := c.startSpan(ctx, processCommandMulti(multi)) + resp := c.client.DoMulti(ctx, multi...) + c.finishSpan(span, firstError(resp)) return resp } -func (c *coreClient) Receive(ctx context.Context, subscribe valkey.Completed, fn func(msg valkey.PubSubMessage)) error { - command, statement := processCmd(&subscribe) - span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ - command: command, - statement: statement, - rawCommand: c.clientConfig.rawCommand, - })...) - err := c.Client.Receive(ctx, subscribe, fn) - span.Finish(tracer.WithError(err)) +func (c *client) Receive(ctx context.Context, subscribe valkey.Completed, fn func(msg valkey.PubSubMessage)) error { + span, ctx := c.startSpan(ctx, processCommand(&subscribe)) + err := c.client.Receive(ctx, subscribe, fn) + c.finishSpan(span, err) return err } -func (c *client) DoCache(ctx context.Context, cmd valkey.Cacheable, ttl time.Duration) (resp valkey.ValkeyResult) { - command, statement := processCmd(&cmd) - span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ - command: command, - statement: statement, - rawCommand: c.clientConfig.rawCommand, - })...) - resp = c.Client.DoCache(ctx, cmd, ttl) +func (c *client) DoCache(ctx context.Context, cmd valkey.Cacheable, ttl time.Duration) valkey.ValkeyResult { + span, ctx := c.startSpan(ctx, processCommand(&cmd)) + resp := c.client.DoCache(ctx, cmd, ttl) setClientCacheTags(span, resp) - defer span.Finish(tracer.WithError(resp.Error())) + c.finishSpan(span, resp.Error()) return resp } -func (c *client) DoMultiCache(ctx context.Context, multi ...valkey.CacheableTTL) (resp []valkey.ValkeyResult) { - command, statement := processMultiCacheableTTL(multi...) - span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ - command: command, - statement: statement, - rawCommand: c.clientConfig.rawCommand, - })...) - resp = c.Client.DoMultiCache(ctx, multi...) - defer span.Finish(tracer.WithError(firstError(resp))) +func (c *client) DoMultiCache(ctx context.Context, multi ...valkey.CacheableTTL) []valkey.ValkeyResult { + span, ctx := c.startSpan(ctx, processCommandMultiCache(multi)) + resp := c.client.DoMultiCache(ctx, multi...) + c.finishSpan(span, firstError(resp)) return resp } func (c *client) DoStream(ctx context.Context, cmd valkey.Completed) (resp valkey.ValkeyResultStream) { - command, statement := processCmd(&cmd) - span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ - command: command, - statement: statement, - rawCommand: c.clientConfig.rawCommand, - })...) - resp = c.Client.DoStream(ctx, cmd) - defer span.Finish(tracer.WithError(resp.Error())) + span, ctx := c.startSpan(ctx, processCommand(&cmd)) + resp = c.client.DoStream(ctx, cmd) + c.finishSpan(span, resp.Error()) return resp } func (c *client) DoMultiStream(ctx context.Context, multi ...valkey.Completed) valkey.MultiValkeyResultStream { - command, statement := processMultiCompleted(multi...) - span, ctx := tracer.StartSpanFromContext(ctx, c.spanName, c.buildStartSpanOptions(buildStartSpanOptionsInput{ - command: command, - statement: statement, - rawCommand: c.clientConfig.rawCommand, - })...) - resp := c.Client.DoMultiStream(ctx, multi...) - span.Finish(tracer.WithError(resp.Error())) + span, ctx := c.startSpan(ctx, processCommandMulti(multi)) + resp := c.client.DoMultiStream(ctx, multi...) + c.finishSpan(span, resp.Error()) return resp } func (c *client) Dedicated(fn func(valkey.DedicatedClient) error) error { - return c.Client.Dedicated(func(dc valkey.DedicatedClient) error { + return c.client.Dedicated(func(dc valkey.DedicatedClient) error { return fn(&dedicatedClient{ - coreClient: c.coreClient, + client: c, dedicatedClient: dc, }) }) } func (c *client) Dedicate() (client valkey.DedicatedClient, cancel func()) { - dedicated, cancel := c.coreClient.Client.Dedicate() + dedicated, cancel := c.client.Dedicate() return &dedicatedClient{ - coreClient: c.coreClient, + client: c, dedicatedClient: dedicated, }, cancel } func (c *client) Nodes() map[string]valkey.Client { - nodes := c.Client.Nodes() + nodes := c.client.Nodes() for addr, valkeyClient := range nodes { - host, port := splitHostPort(addr) + host, port, _ := net.SplitHostPort(addr) nodes[addr] = &client{ - coreClient: coreClient{ - Client: valkeyClient, - option: c.option, - clientConfig: c.clientConfig, - host: host, - port: port, - }, + client: valkeyClient, + cfg: c.cfg, + host: host, + port: port, + dbIndex: c.dbIndex, + user: c.user, } } return nodes } +var ( + _ valkey.DedicatedClient = (*dedicatedClient)(nil) +) + +type dedicatedClient struct { + *client + dedicatedClient valkey.DedicatedClient +} + func (c *dedicatedClient) SetPubSubHooks(hooks valkey.PubSubHooks) <-chan error { return c.dedicatedClient.SetPubSubHooks(hooks) } + +type command struct { + statement string + raw string +} + +func (c *client) startSpan(ctx context.Context, cmd command) (tracer.Span, context.Context) { + opts := []tracer.StartSpanOption{ + tracer.ServiceName(c.cfg.serviceName), + tracer.ResourceName(cmd.statement), + tracer.SpanType(ext.SpanTypeValkey), + tracer.Tag(ext.TargetHost, c.host), + tracer.Tag(ext.TargetPort, c.port), + tracer.Tag(ext.Component, componentName), + tracer.Tag(ext.SpanKind, ext.SpanKindClient), + tracer.Tag(ext.DBSystem, ext.DBSystemValkey), + tracer.Tag(ext.TargetDB, c.dbIndex), + } + if c.cfg.rawCommand { + opts = append(opts, tracer.Tag(ext.ValkeyRawCommand, cmd.raw)) + } + if c.host != "" { + opts = append(opts, tracer.Tag(ext.TargetHost, c.host)) + } + if c.port != "" { + opts = append(opts, tracer.Tag(ext.TargetPort, c.port)) + } + if c.user != "" { + opts = append(opts, tracer.Tag(ext.DBUser, c.user)) + } + return tracer.StartSpanFromContext(ctx, "valkey.command", opts...) +} + +func (c *client) finishSpan(span tracer.Span, err error) { + var opts []tracer.FinishOption + if err != nil && !valkey.IsValkeyNil(err) { + opts = append(opts, tracer.WithError(err)) + } + span.Finish(opts...) +} + +type commander interface { + Commands() []string +} + +func processCommand(cmd commander) command { + cmds := cmd.Commands() + if len(cmds) == 0 { + return command{} + } + statement := cmds[0] + raw := strings.Join(cmds, " ") + return command{ + statement: statement, + raw: raw, + } +} + +func processCommandMulti(multi []valkey.Completed) command { + var cmds []command + for _, cmd := range multi { + cmds = append(cmds, processCommand(&cmd)) + } + return multiCommand(cmds) +} + +func processCommandMultiCache(multi []valkey.CacheableTTL) command { + var cmds []command + for _, cmd := range multi { + cmds = append(cmds, processCommand(&cmd.Cmd)) + } + return multiCommand(cmds) +} + +func multiCommand(cmds []command) command { + // limit to the 5 first + if len(cmds) > 5 { + cmds = cmds[:5] + } + statement := strings.Builder{} + raw := strings.Builder{} + for i, cmd := range cmds { + statement.WriteString(cmd.statement) + raw.WriteString(cmd.raw) + if i != len(cmds)-1 { + statement.WriteString(" ") + raw.WriteString(" ") + } + } + return command{ + statement: statement.String(), + raw: raw.String(), + } +} + +func firstError(s []valkey.ValkeyResult) error { + for _, result := range s { + if err := result.Error(); err != nil && !valkey.IsValkeyNil(err) { + return err + } + } + return nil +} + +func setClientCacheTags(s tracer.Span, result valkey.ValkeyResult) { + s.SetTag(ext.ValkeyClientCacheHit, result.IsCacheHit()) + s.SetTag(ext.ValkeyClientCacheTTL, result.CacheTTL()) + s.SetTag(ext.ValkeyClientCachePTTL, result.CachePTTL()) + s.SetTag(ext.ValkeyClientCachePXAT, result.CachePXAT()) +} diff --git a/contrib/valkey-go/valkey_test.go b/contrib/valkey-go/valkey_test.go index c03a3cfc23..ba29551070 100644 --- a/contrib/valkey-go/valkey_test.go +++ b/contrib/valkey-go/valkey_test.go @@ -7,6 +7,7 @@ package valkey import ( "context" "fmt" + "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig" "os" "testing" "time" @@ -19,13 +20,17 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) -var ( +const ( // See docker-compose.yaml valkeyPort = 6380 valkeyUsername = "default" valkeyPassword = "password-for-default" ) +var ( + valkeyAddrs = []string{fmt.Sprintf("127.0.0.1:%d", valkeyPort)} +) + func TestMain(m *testing.M) { _, ok := os.LookupEnv("INTEGRATION") if !ok { @@ -36,187 +41,146 @@ func TestMain(m *testing.M) { } func TestNewClient(t *testing.T) { + prevName := globalconfig.ServiceName() + defer globalconfig.SetServiceName(prevName) + globalconfig.SetServiceName("global-service") + tests := []struct { - name string - valkeyClientOptions valkey.ClientOption - valkeytraceClientOptions []ClientOption - valkeytraceClientEnvVars map[string]string - createSpans func(*testing.T, context.Context, valkey.Client) - assertNewClientError func(*testing.T, error) - assertSpans []func(*testing.T, mocktracer.Span) + name string + opts []Option + runTest func(*testing.T, context.Context, valkey.Client) + assertSpans func(*testing.T, []mocktracer.Span) + wantServiceName string }{ { - name: "Test invalid username", - valkeyClientOptions: valkey.ClientOption{ - InitAddress: []string{fmt.Sprintf("127.0.0.1:%d", valkeyPort)}, - Username: "invalid-username", - Password: valkeyPassword, - }, - assertNewClientError: func(t *testing.T, err error) { - assert.EqualError(t, err, "WRONGPASS invalid username-password pair or user is disabled.") + name: "Test SET command with raw command", + opts: []Option{ + WithRawCommand(true), + WithServiceName("test-service"), }, - }, - { - name: "Test invalid password", - valkeyClientOptions: valkey.ClientOption{ - InitAddress: []string{fmt.Sprintf("127.0.0.1:%d", valkeyPort)}, - Username: valkeyUsername, - Password: "invalid", + runTest: func(t *testing.T, ctx context.Context, client valkey.Client) { + assert.NoError(t, client.Do(ctx, client.B().Set().Key("test_key").Value("test_value").Build()).Error()) }, - assertNewClientError: func(t *testing.T, err error) { - assert.EqualError(t, err, "WRONGPASS invalid username-password pair or user is disabled.") + assertSpans: func(t *testing.T, spans []mocktracer.Span) { + require.Len(t, spans, 1) + + span := spans[0] + assert.Equal(t, "SET", span.Tag(ext.ResourceName)) + assert.Equal(t, "SET test_key test_value", span.Tag(ext.ValkeyRawCommand)) + assert.Equal(t, false, span.Tag(ext.ValkeyClientCacheHit)) + assert.Less(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) + assert.Less(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) + assert.Less(t, span.Tag(ext.ValkeyClientCachePTTL), int64(0)) + assert.Nil(t, span.Tag(ext.Error)) }, + wantServiceName: "test-service", }, { - name: "Test SET command with custom options", - valkeyClientOptions: valkey.ClientOption{ - InitAddress: []string{fmt.Sprintf("127.0.0.1:%d", valkeyPort)}, - Username: valkeyUsername, - Password: valkeyPassword, - }, - valkeytraceClientOptions: []ClientOption{ - WithRawCommand(true), + name: "Test SET command without raw command", + opts: nil, + runTest: func(t *testing.T, ctx context.Context, client valkey.Client) { + require.NoError(t, client.Do(ctx, client.B().Set().Key("test_key").Value("test_value").Build()).Error()) }, - createSpans: func(t *testing.T, ctx context.Context, client valkey.Client) { - assert.NoError(t, client.Do(ctx, client.B().Set().Key("test_key").Value("test_value").Build()).Error()) - }, - assertSpans: []func(t *testing.T, span mocktracer.Span){ - func(t *testing.T, span mocktracer.Span) { - assert.Equal(t, "SET\ntest_key\ntest_value", span.Tag(ext.DBStatement)) - assert.Equal(t, "SET\ntest_key\ntest_value", span.Tag(ext.ResourceName)) - assert.True(t, span.Tag(ext.ValkeyRawCommand).(bool)) - assert.False(t, span.Tag(ext.ValkeyClientCacheHit).(bool)) - assert.Less(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) - assert.Less(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) - assert.Less(t, span.Tag(ext.ValkeyClientCachePTTL), int64(0)) - assert.Nil(t, span.Tag(ext.Error)) - }, + assertSpans: func(t *testing.T, spans []mocktracer.Span) { + require.Len(t, spans, 1) + + span := spans[0] + assert.Equal(t, "SET", span.Tag(ext.ResourceName)) + assert.Nil(t, span.Tag(ext.ValkeyRawCommand)) + assert.Equal(t, false, span.Tag(ext.ValkeyClientCacheHit)) + assert.Less(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) + assert.Less(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) + assert.Less(t, span.Tag(ext.ValkeyClientCachePTTL), int64(0)) + assert.Nil(t, span.Tag(ext.Error)) }, + wantServiceName: "global-service", }, { - name: "Test SET/GET commands", - valkeyClientOptions: valkey.ClientOption{ - InitAddress: []string{fmt.Sprintf("127.0.0.1:%d", valkeyPort)}, - Username: valkeyUsername, - Password: valkeyPassword, - ClientName: "my-valkey-client", + name: "Test SET GET multi command", + opts: []Option{ + WithRawCommand(true), }, - createSpans: func(t *testing.T, ctx context.Context, client valkey.Client) { + runTest: func(t *testing.T, ctx context.Context, client valkey.Client) { resp := client.DoMulti(ctx, client.B().Set().Key("test_key").Value("test_value").Build(), client.B().Get().Key("test_key").Build()) - assert.Len(t, resp, 2) + require.Len(t, resp, 2) }, - assertSpans: []func(t *testing.T, span mocktracer.Span){ - func(t *testing.T, span mocktracer.Span) { - assert.Equal(t, "SET GET", span.Tag(ext.DBStatement)) - assert.Equal(t, "SET GET", span.Tag(ext.ResourceName)) - assert.False(t, span.Tag(ext.ValkeyRawCommand).(bool)) - assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) - assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) - assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) - assert.Nil(t, span.Tag(ext.ValkeyClientCachePTTL)) - assert.Nil(t, span.Tag(ext.Error)) - }, + assertSpans: func(t *testing.T, spans []mocktracer.Span) { + require.Len(t, spans, 1) + + span := spans[0] + assert.Equal(t, "SET GET", span.Tag(ext.ResourceName)) + assert.Equal(t, "SET test_key test_value GET test_key", span.Tag(ext.ValkeyRawCommand)) + assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) + assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) + assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) + assert.Nil(t, span.Tag(ext.ValkeyClientCachePTTL)) + assert.Nil(t, span.Tag(ext.Error)) }, + wantServiceName: "global-service", }, { name: "Test HMGET command with cache", - valkeyClientOptions: valkey.ClientOption{ - InitAddress: []string{fmt.Sprintf("127.0.0.1:%d", valkeyPort)}, - Username: valkeyUsername, - Password: valkeyPassword, + opts: []Option{ + WithRawCommand(true), }, - createSpans: func(t *testing.T, ctx context.Context, client valkey.Client) { + runTest: func(t *testing.T, ctx context.Context, client valkey.Client) { assert.NoError(t, client.DoCache(ctx, client.B().Hmget().Key("mk").Field("1", "2").Cache(), time.Minute).Error()) resp, err := client.DoCache(ctx, client.B().Hmget().Key("mk").Field("1", "2").Cache(), time.Minute).ToArray() - assert.Len(t, resp, 2) - assert.NoError(t, err) + require.Len(t, resp, 2) + require.NoError(t, err) }, - assertSpans: []func(t *testing.T, span mocktracer.Span){ - func(t *testing.T, span mocktracer.Span) { - assert.Equal(t, "HMGET", span.Tag(ext.DBStatement)) - assert.Equal(t, "HMGET", span.Tag(ext.ResourceName)) - assert.False(t, span.Tag(ext.ValkeyRawCommand).(bool)) - assert.False(t, span.Tag(ext.ValkeyClientCacheHit).(bool)) - assert.Greater(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) - assert.Greater(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) - assert.Greater(t, span.Tag(ext.ValkeyClientCachePTTL), int64(0)) - assert.Nil(t, span.Tag(ext.Error)) - }, - func(t *testing.T, span mocktracer.Span) { - assert.Equal(t, "HMGET", span.Tag(ext.DBStatement)) - assert.Equal(t, "HMGET", span.Tag(ext.ResourceName)) - assert.False(t, span.Tag(ext.ValkeyRawCommand).(bool)) - assert.True(t, span.Tag(ext.ValkeyClientCacheHit).(bool)) - assert.Greater(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) - assert.Greater(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) - assert.Greater(t, span.Tag(ext.ValkeyClientCachePTTL), int64(0)) - assert.Nil(t, span.Tag(ext.Error)) - }, + assertSpans: func(t *testing.T, spans []mocktracer.Span) { + require.Len(t, spans, 2) + + span := spans[0] + assert.Equal(t, "HMGET", span.Tag(ext.ResourceName)) + assert.Equal(t, "HMGET mk 1 2", span.Tag(ext.ValkeyRawCommand)) + assert.Equal(t, false, span.Tag(ext.ValkeyClientCacheHit)) + assert.Greater(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) + assert.Greater(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) + assert.Greater(t, span.Tag(ext.ValkeyClientCachePTTL), int64(0)) + assert.Nil(t, span.Tag(ext.Error)) + + span = spans[1] + assert.Equal(t, "HMGET", span.Tag(ext.ResourceName)) + assert.Equal(t, "HMGET mk 1 2", span.Tag(ext.ValkeyRawCommand)) + assert.Equal(t, true, span.Tag(ext.ValkeyClientCacheHit)) + assert.Greater(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) + assert.Greater(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) + assert.Greater(t, span.Tag(ext.ValkeyClientCachePTTL), int64(0)) + assert.Nil(t, span.Tag(ext.Error)) }, + wantServiceName: "global-service", }, { - name: "Test GET command with stream with env vars", - valkeyClientOptions: valkey.ClientOption{ - InitAddress: []string{fmt.Sprintf("127.0.0.1:%d", valkeyPort)}, - Username: valkeyUsername, - Password: valkeyPassword, - }, - valkeytraceClientEnvVars: map[string]string{ - "DD_TRACE_VALKEY_RAW_COMMAND": "true", + name: "Test GET stream command", + opts: []Option{ + WithRawCommand(true), }, - createSpans: func(t *testing.T, ctx context.Context, client valkey.Client) { + runTest: func(t *testing.T, ctx context.Context, client valkey.Client) { resp := client.DoStream(ctx, client.B().Get().Key("test_key").Build()) - assert.NoError(t, resp.Error()) - }, - assertSpans: []func(t *testing.T, span mocktracer.Span){ - func(t *testing.T, span mocktracer.Span) { - assert.Equal(t, "GET\ntest_key", span.Tag(ext.DBStatement)) - assert.Equal(t, "GET\ntest_key", span.Tag(ext.ResourceName)) - assert.True(t, span.Tag(ext.ValkeyRawCommand).(bool)) - assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) - assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) - assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) - assert.Nil(t, span.Tag(ext.ValkeyClientCachePTTL)) - assert.Nil(t, span.Tag(ext.Error)) - }, + require.NoError(t, resp.Error()) }, - }, - { - name: "Test SET command with timeout", - valkeyClientOptions: valkey.ClientOption{ - InitAddress: []string{fmt.Sprintf("127.0.0.1:%d", valkeyPort)}, - Username: valkeyUsername, - Password: valkeyPassword, - }, - createSpans: func(t *testing.T, ctx context.Context, client valkey.Client) { - ctxWithTimeout, cancel := context.WithTimeout(ctx, time.Nanosecond) - client.Do(ctxWithTimeout, client.B().Set().Key("k1").Value("v1").Build()) - cancel() - }, - assertSpans: []func(t *testing.T, span mocktracer.Span){ - func(t *testing.T, span mocktracer.Span) { - assert.Equal(t, "SET", span.Tag(ext.DBStatement)) - assert.Equal(t, "SET", span.Tag(ext.ResourceName)) - assert.False(t, span.Tag(ext.ValkeyRawCommand).(bool)) - assert.False(t, span.Tag(ext.ValkeyClientCacheHit).(bool)) - assert.Less(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) - assert.Less(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) - assert.Less(t, span.Tag(ext.ValkeyClientCachePTTL), int64(0)) - assert.Equal(t, context.DeadlineExceeded, span.Tag(ext.Error).(error)) - }, + assertSpans: func(t *testing.T, spans []mocktracer.Span) { + require.Len(t, spans, 1) + + span := spans[0] + assert.Equal(t, "GET", span.Tag(ext.ResourceName)) + assert.Equal(t, "GET test_key", span.Tag(ext.ValkeyRawCommand)) + assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) + assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) + assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) + assert.Nil(t, span.Tag(ext.ValkeyClientCachePTTL)) + assert.Nil(t, span.Tag(ext.Error)) }, + wantServiceName: "global-service", }, { - name: "Test SET/GET/SET/GET command with timeout and option", - valkeyClientOptions: valkey.ClientOption{ - InitAddress: []string{fmt.Sprintf("127.0.0.1:%d", valkeyPort)}, - Username: valkeyUsername, - Password: valkeyPassword, - }, - valkeytraceClientOptions: []ClientOption{ + name: "Test multi command should be limited to 5", + opts: []Option{ WithRawCommand(true), }, - createSpans: func(t *testing.T, ctx context.Context, client valkey.Client) { + runTest: func(t *testing.T, ctx context.Context, client valkey.Client) { ctxWithTimeout, cancel := context.WithTimeout(ctx, time.Nanosecond) client.DoMulti( ctxWithTimeout, @@ -224,91 +188,112 @@ func TestNewClient(t *testing.T) { client.B().Get().Key("k1").Build(), client.B().Set().Key("k2").Value("v2").Build(), client.B().Get().Key("k2").Build(), + client.B().Set().Key("k3").Value("v3").Build(), + client.B().Get().Key("k3").Build(), ) cancel() }, - assertSpans: []func(t *testing.T, span mocktracer.Span){ - func(t *testing.T, span mocktracer.Span) { - assert.Equal(t, "SET\nk1\nv1\nGET\nk1\nSET\nk2\nv2\nGET\nk2", span.Tag(ext.DBStatement)) - assert.Equal(t, "SET\nk1\nv1\nGET\nk1\nSET\nk2\nv2\nGET\nk2", span.Tag(ext.ResourceName)) - assert.True(t, span.Tag(ext.ValkeyRawCommand).(bool)) - assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) - assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) - assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) - assert.Nil(t, span.Tag(ext.ValkeyClientCachePTTL)) - assert.Equal(t, context.DeadlineExceeded, span.Tag(ext.Error).(error)) - }, + assertSpans: func(t *testing.T, spans []mocktracer.Span) { + require.Len(t, spans, 1) + + span := spans[0] + assert.Equal(t, "SET GET SET GET SET", span.Tag(ext.ResourceName)) + assert.Equal(t, "SET k1 v1 GET k1 SET k2 v2 GET k2 SET k3 v3", span.Tag(ext.ValkeyRawCommand)) + assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) + assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) + assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) + assert.Nil(t, span.Tag(ext.ValkeyClientCachePTTL)) + assert.Equal(t, context.DeadlineExceeded, span.Tag(ext.Error).(error)) }, + wantServiceName: "global-service", }, { name: "Test SUBSCRIBE command with timeout", - valkeyClientOptions: valkey.ClientOption{ - InitAddress: []string{fmt.Sprintf("127.0.0.1:%d", valkeyPort)}, - Username: valkeyUsername, - Password: valkeyPassword, + opts: []Option{ + WithRawCommand(true), }, - createSpans: func(t *testing.T, ctx context.Context, client valkey.Client) { + runTest: func(t *testing.T, ctx context.Context, client valkey.Client) { ctxWithTimeout, cancel := context.WithTimeout(ctx, time.Millisecond) - assert.Equal(t, + require.EqualError(t, context.DeadlineExceeded, - client.Receive(ctxWithTimeout, client.B().Subscribe().Channel("test_channel").Build(), func(msg valkey.PubSubMessage) {}), + client.Receive(ctxWithTimeout, client.B().Subscribe().Channel("test_channel").Build(), func(msg valkey.PubSubMessage) {}).Error(), ) cancel() }, - assertSpans: []func(t *testing.T, span mocktracer.Span){ - func(t *testing.T, span mocktracer.Span) { - assert.Equal(t, "SUBSCRIBE", span.Tag(ext.DBStatement)) - assert.Equal(t, "SUBSCRIBE", span.Tag(ext.ResourceName)) - assert.False(t, span.Tag(ext.ValkeyRawCommand).(bool)) - assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) - assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) - assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) - assert.Nil(t, span.Tag(ext.ValkeyClientCachePTTL)) - assert.Equal(t, context.DeadlineExceeded, span.Tag(ext.Error).(error)) - }, + assertSpans: func(t *testing.T, spans []mocktracer.Span) { + require.Len(t, spans, 1) + + span := spans[0] + assert.Equal(t, "SUBSCRIBE", span.Tag(ext.ResourceName)) + assert.Equal(t, "SUBSCRIBE test_channel", span.Tag(ext.ValkeyRawCommand)) + assert.Nil(t, span.Tag(ext.ValkeyClientCacheHit)) + assert.Nil(t, span.Tag(ext.ValkeyClientCacheTTL)) + assert.Nil(t, span.Tag(ext.ValkeyClientCachePXAT)) + assert.Nil(t, span.Tag(ext.ValkeyClientCachePTTL)) + assert.Equal(t, context.DeadlineExceeded, span.Tag(ext.Error).(error)) }, + wantServiceName: "global-service", + }, + { + name: "Test Dedicated client", + opts: []Option{ + WithRawCommand(true), + }, + runTest: func(t *testing.T, ctx context.Context, client valkey.Client) { + err := client.Dedicated(func(d valkey.DedicatedClient) error { + return d.Do(ctx, client.B().Set().Key("test_key").Value("test_value").Build()).Error() + }) + require.NoError(t, err) + }, + assertSpans: func(t *testing.T, spans []mocktracer.Span) { + require.Len(t, spans, 1) + + span := spans[0] + assert.Equal(t, "SET", span.Tag(ext.ResourceName)) + assert.Equal(t, "SET test_key test_value", span.Tag(ext.ValkeyRawCommand)) + assert.Equal(t, false, span.Tag(ext.ValkeyClientCacheHit)) + assert.Less(t, span.Tag(ext.ValkeyClientCacheTTL), int64(0)) + assert.Less(t, span.Tag(ext.ValkeyClientCachePXAT), int64(0)) + assert.Less(t, span.Tag(ext.ValkeyClientCachePTTL), int64(0)) + assert.Nil(t, span.Tag(ext.Error)) + }, + wantServiceName: "global-service", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mt := mocktracer.Start() defer mt.Stop() - for k, v := range tt.valkeytraceClientEnvVars { - t.Setenv(k, v) - } - span, ctx := tracer.StartSpanFromContext(context.Background(), "test.root", tracer.ServiceName("test-service")) - client, err := NewClient(tt.valkeyClientOptions, tt.valkeytraceClientOptions...) - if tt.assertNewClientError == nil { - require.NoErrorf(t, err, tt.name) - } else { - tt.assertNewClientError(t, err) - span.Finish() - return + + valkeyClientOption := valkey.ClientOption{ + InitAddress: valkeyAddrs, + Username: valkeyUsername, + Password: valkeyPassword, } - tt.createSpans(t, ctx, client) - span.Finish() // test.root exists in the last span. + client, err := NewClient(valkeyClientOption, tt.opts...) + require.NoError(t, err) + + root, ctx := tracer.StartSpanFromContext(context.Background(), "test.root", tracer.ServiceName("test-service")) + tt.runTest(t, ctx, client) + root.Finish() // test.root exists in the last span. + spans := mt.FinishedSpans() - require.Len(t, spans, len(tt.assertSpans)+1) // +1 for test.root - for i, span := range spans { + tt.assertSpans(t, spans[:len(spans)-1]) + + for _, span := range spans { if span.OperationName() == "test.root" { continue } - tt.assertSpans[i](t, span) - t.Log("Following assertions are common to all spans") - assert.Equalf(t, - "test-service", - span.Tag(ext.ServiceName), - "service name should not be overwritten as per DD_APM_PEER_TAGS_AGGREGATION in trace-agent", - ) + + // The following assertions are common to all spans + assert.Equal(t, tt.wantServiceName, span.Tag(ext.ServiceName)) assert.Equal(t, "127.0.0.1", span.Tag(ext.TargetHost)) - assert.Equal(t, valkeyPort, span.Tag(ext.TargetPort)) - assert.Equal(t, 0, span.Tag(ext.ValkeyDatabaseIndex)) - assert.Equal(t, 0, span.Tag(ext.TargetDB)) - assert.NotNil(t, span) - assert.Equal(t, tt.valkeyClientOptions.Username, span.Tag(ext.DBUser)) + assert.Equal(t, "6380", span.Tag(ext.TargetPort)) + assert.Equal(t, "0", span.Tag(ext.TargetDB)) + assert.Equal(t, "default", span.Tag(ext.DBUser)) assert.Equal(t, "valkey.command", span.OperationName()) assert.Equal(t, "client", span.Tag(ext.SpanKind)) - assert.Equal(t, ext.SpanTypeValkey, span.Tag(ext.SpanType)) + assert.Equal(t, "valkey", span.Tag(ext.SpanType)) assert.Equal(t, "valkey-io/valkey-go", span.Tag(ext.Component)) assert.Equal(t, "valkey", span.Tag(ext.DBSystem)) } diff --git a/ddtrace/ext/db.go b/ddtrace/ext/db.go index 991ffd9457..ff9b2efe33 100644 --- a/ddtrace/ext/db.go +++ b/ddtrace/ext/db.go @@ -63,15 +63,6 @@ const ( // ValkeyRawCommand allows to set the raw command for tags. ValkeyRawCommand = "valkey.raw_command" - // ValkeyDatabaseIndex specifies the index of the database being connected to. - ValkeyDatabaseIndex = "db.valkey.database_index" - - // ValkeyClientVersion denotes the version of the Valkey client in use. - ValkeyClientVersion = "db.valkey.client.version" - - // ValkeyClientName indicates the name of the Valkey client being used. - ValkeyClientName = "db.valkey.client.name" - // ValkeyClientCacheHit is the remaining TTL in seconds of client side cache. ValkeyClientCacheHit = "db.valkey.client.cache.hit" diff --git a/ddtrace/ext/tags.go b/ddtrace/ext/tags.go index a22d191f0f..b070b2fcd6 100644 --- a/ddtrace/ext/tags.go +++ b/ddtrace/ext/tags.go @@ -9,7 +9,6 @@ package ext const ( // TargetHost sets the target host address. - // Deprecated: Use NetworkDestinationName instead for hostname and NetworkDestinationIP for IP addresses TargetHost = "out.host" // NetworkDestinationName is the remote hostname or similar where the outbound connection is being made to. @@ -19,10 +18,9 @@ const ( NetworkDestinationIP = "network.destination.ip" // TargetPort sets the target host port. - // Deprecated: Use NetworkDestinationPort instead. TargetPort = "out.port" - // TargetPort sets the target db. + // TargetDB sets the target db. TargetDB = "out.db" // NetworkDestinationPort is the remote port number of the outbound connection. diff --git a/internal/namingschema/op.go b/internal/namingschema/op.go index 2d3059facf..01c90726c1 100644 --- a/internal/namingschema/op.go +++ b/internal/namingschema/op.go @@ -31,7 +31,6 @@ const ( // cache MemcachedOutbound RedisOutbound - ValkeyOutbound // db ElasticSearchOutbound @@ -76,8 +75,6 @@ func opV1(t IntegrationType) string { return "memcached.command" case RedisOutbound: return "redis.command" - case ValkeyOutbound: - return "valkey.command" // Database case ElasticSearchOutbound: @@ -124,8 +121,6 @@ func opV0(t IntegrationType) string { return "memcached.query" case RedisOutbound: return "redis.command" - case ValkeyOutbound: - return "valkey.command" case ElasticSearchOutbound: return "elasticsearch.query" case MongoDBOutbound: From 7eeeb6c2613d12ffee6a241ae7133fa89936168e Mon Sep 17 00:00:00 2001 From: keisku Date: Tue, 28 Jan 2025 08:46:28 +0000 Subject: [PATCH 23/24] fix: go mod tidy smoke test Signed-off-by: keisku --- internal/orchestrion/_integration/go.sum | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/orchestrion/_integration/go.sum b/internal/orchestrion/_integration/go.sum index 180a65562c..11a07a06c8 100644 --- a/internal/orchestrion/_integration/go.sum +++ b/internal/orchestrion/_integration/go.sum @@ -1785,8 +1785,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= From 3234dc91e91438a856c4a780255efc9ffb485f9e Mon Sep 17 00:00:00 2001 From: Rodrigo Arguello Date: Tue, 28 Jan 2025 15:54:16 +0100 Subject: [PATCH 24/24] fix github workflows file --- .github/workflows/unit-integration-tests.yml | 12 ++++++------ contrib/valkey-go/valkey_test.go | 5 +++-- docker-compose.yaml | 4 ++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/unit-integration-tests.yml b/.github/workflows/unit-integration-tests.yml index 5b8643c8ba..a27aa3c138 100644 --- a/.github/workflows/unit-integration-tests.yml +++ b/.github/workflows/unit-integration-tests.yml @@ -144,12 +144,12 @@ jobs: image: redis:3.2 ports: - 6379:6379 - valkey: - image: valkey/valkey:8 - ports: - - "6380:6380" - # https://valkey.io/topics/acl/ - command: [ "valkey-server", "--port", "6380", "--requirepass", "password-for-default" ] + valkey: + image: valkey/valkey:8 + env: + VALKEY_EXTRA_FLAGS: "--port 6380 --requirepass password-for-default" + ports: + - 6380:6380 elasticsearch2: image: elasticsearch:2 env: diff --git a/contrib/valkey-go/valkey_test.go b/contrib/valkey-go/valkey_test.go index ba29551070..19799bf5d6 100644 --- a/contrib/valkey-go/valkey_test.go +++ b/contrib/valkey-go/valkey_test.go @@ -7,7 +7,6 @@ package valkey import ( "context" "fmt" - "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig" "os" "testing" "time" @@ -15,9 +14,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/valkey-io/valkey-go" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/mocktracer" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" + "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig" ) const ( @@ -44,7 +45,7 @@ func TestNewClient(t *testing.T) { prevName := globalconfig.ServiceName() defer globalconfig.SetServiceName(prevName) globalconfig.SetServiceName("global-service") - + tests := []struct { name string opts []Option diff --git a/docker-compose.yaml b/docker-compose.yaml index c397a91758..8eae54d8a3 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -43,10 +43,10 @@ services: - "6379:6379" valkey: image: valkey/valkey:8 + environment: + VALKEY_EXTRA_FLAGS: "--port 6380 --requirepass password-for-default" ports: - "6380:6380" - # https://valkey.io/topics/acl/ - command: [ "valkey-server", "--port", "6380", "--requirepass", "password-for-default" ] elasticsearch2: image: elasticsearch:2 environment: