From 632cc5bcec85e6c749ef593d670f4ad6c47b85af Mon Sep 17 00:00:00 2001 From: Florian Forster Date: Wed, 15 Oct 2025 09:54:08 +0200 Subject: [PATCH] fix(tracing): Add back support for formatting derived scalar types. This is a partial revert of b3f5eeb3e5c8e1c90bfec5db19415de01419b973. While using a type switch is more efficient, it also differentiates between a `string` type and a derived type, such as `ext.SpanTypeEnum`. By reverting back to using reflect for the type match, we fix this regression. This change keeps the `fmt.Stringer` interface match, which is used for `*url.URL`. A test case for `ext.SpanTypeEnum` has been added. --- tracing/impl/stackdriver_tracer.go | 45 ++++++++++++++----------- tracing/impl/stackdriver_tracer_test.go | 29 ++++++++++++++-- 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/tracing/impl/stackdriver_tracer.go b/tracing/impl/stackdriver_tracer.go index 56cc579..9f785a0 100644 --- a/tracing/impl/stackdriver_tracer.go +++ b/tracing/impl/stackdriver_tracer.go @@ -7,6 +7,7 @@ import ( "encoding/base64" "fmt" "io" + "math" "reflect" "strconv" "time" @@ -155,24 +156,33 @@ func (span *adapterSpan) SetOperationName(operationName string) opentracing.Span } func castToAttribute(key string, value any) []trace.Attribute { + // Scalar types + switch v := reflect.ValueOf(value); v.Kind() { + case reflect.Bool: + return []trace.Attribute{trace.BoolAttribute(key, v.Bool())} + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return []trace.Attribute{trace.Int64Attribute(key, v.Int())} + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if v.Uint() > math.MaxInt64 { + return newStringAttribute(key, strconv.FormatUint(v.Uint(), 10)) + } + return []trace.Attribute{trace.Int64Attribute(key, int64(v.Uint()))} + case reflect.Float32, reflect.Float64: + return []trace.Attribute{trace.Float64Attribute(key, v.Float())} + case reflect.String: + return newStringAttribute(key, v.String()) + } + + // Interfaces switch v := value.(type) { - case bool: - return []trace.Attribute{trace.BoolAttribute(key, v)} - case int, int8, int16, int32, int64: - return []trace.Attribute{trace.Int64Attribute(key, reflect.ValueOf(v).Int())} - case uint, uint8, uint16, uint32, uint64: - return []trace.Attribute{trace.Int64Attribute(key, int64(reflect.ValueOf(v).Uint()))} - case float32, float64: - return []trace.Attribute{trace.Float64Attribute(key, reflect.ValueOf(v).Float())} - case string: - return newStringAttribute(key, v) case fmt.Stringer: return newStringAttribute(key, v.String()) - default: - return []trace.Attribute{ - trace.StringAttribute(key, fmt.Sprint(value)), - trace.StringAttribute(key+".error", fmt.Sprintf("unknown type: %T", value)), - } + } + + // Fall-back: fmt.Sprint formatting + return []trace.Attribute{ + trace.StringAttribute(key, fmt.Sprint(value)), + trace.StringAttribute(key+".error", fmt.Sprintf("unknown type: %T", value)), } } @@ -209,10 +219,7 @@ func chunkString(s string, chunkSize int) []string { } var strs []string for i := 0; i*chunkSize < len(s); i++ { - end := (i + 1) * chunkSize - if end > len(s) { - end = len(s) - } + end := min((i+1)*chunkSize, len(s)) strs = append(strs, s[i*chunkSize:end]) } return strs diff --git a/tracing/impl/stackdriver_tracer_test.go b/tracing/impl/stackdriver_tracer_test.go index 66fff08..b17afa0 100644 --- a/tracing/impl/stackdriver_tracer_test.go +++ b/tracing/impl/stackdriver_tracer_test.go @@ -4,14 +4,17 @@ package impl import ( "fmt" + "math" "net/url" "reflect" + "strconv" "strings" "testing" "time" "contrib.go.opencensus.io/exporter/stackdriver" "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -53,6 +56,20 @@ func TestOcSpanAdapterCastToAttribute(t *testing.T) { value: 42, want: []trace.Attribute{trace.Int64Attribute("foo", 42)}, }, + { + name: "small unsigned integer", + key: "foo", + value: uint(4211), + want: []trace.Attribute{trace.Int64Attribute("foo", 4211)}, + }, + { + name: "unsigned integer exceeding int64 range", + key: "foo", + value: uint64(math.MaxInt64 + math.MaxInt32), + want: []trace.Attribute{ + trace.StringAttribute("foo", strconv.FormatUint(math.MaxInt64+math.MaxInt32, 10)), + }, + }, { name: "42.1", key: "foo", @@ -135,13 +152,21 @@ func TestOcSpanAdapterCastToAttribute(t *testing.T) { }, { name: "net.url type", - key: "foo", + key: "http.url", value: func() *url.URL { u, _ := url.Parse("https://example.com") return u }(), want: []trace.Attribute{ - trace.StringAttribute("foo", "https://example.com"), + trace.StringAttribute("http.url", "https://example.com"), + }, + }, + { + name: "span.kind", + key: "span.kind", + value: ext.SpanKindRPCServerEnum, + want: []trace.Attribute{ + trace.StringAttribute("span.kind", "server"), }, }, } -- GitLab