From 38d19254e0f9345ec8e9fd7bbbb780179c42d582 Mon Sep 17 00:00:00 2001 From: dmathieu Date: Fri, 5 Apr 2024 10:33:41 +0200 Subject: [PATCH 1/5] emit slices as their json representation --- attribute/key_test.go | 15 +++++++++++++++ attribute/value.go | 9 ++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/attribute/key_test.go b/attribute/key_test.go index 45de9d81817..d0f1b042f79 100644 --- a/attribute/key_test.go +++ b/attribute/key_test.go @@ -63,11 +63,21 @@ func TestEmit(t *testing.T) { v: attribute.BoolValue(true), want: "true", }, + { + name: `test Key.Emit() can emit a string representing self.INT64SLICE`, + v: attribute.Int64SliceValue([]int64{1, 42}), + want: `[1,42]`, + }, { name: `test Key.Emit() can emit a string representing self.INT64`, v: attribute.Int64Value(42), want: "42", }, + { + name: `test Key.Emit() can emit a string representing self.FLOAT64SLICE`, + v: attribute.Float64SliceValue([]float64{1.0, 42.5}), + want: `[1,42.5]`, + }, { name: `test Key.Emit() can emit a string representing self.FLOAT64`, v: attribute.Float64Value(42.1), @@ -78,6 +88,11 @@ func TestEmit(t *testing.T) { v: attribute.StringValue("foo"), want: "foo", }, + { + name: `test Key.Emit() can emit a string representing self.STRINGSLICE`, + v: attribute.StringSliceValue([]string{"foo", "bar"}), + want: `["foo","bar"]`, + }, } { t.Run(testcase.name, func(t *testing.T) { // proto: func (v attribute.Value) Emit() string { diff --git a/attribute/value.go b/attribute/value.go index b320314133a..027177b2e62 100644 --- a/attribute/value.go +++ b/attribute/value.go @@ -231,15 +231,18 @@ func (v Value) Emit() string { case BOOL: return strconv.FormatBool(v.AsBool()) case INT64SLICE: - return fmt.Sprint(v.asInt64Slice()) + j, _ := json.Marshal(v.asInt64Slice()) + return string(j) case INT64: return strconv.FormatInt(v.AsInt64(), 10) case FLOAT64SLICE: - return fmt.Sprint(v.asFloat64Slice()) + j, _ := json.Marshal(v.asFloat64Slice()) + return string(j) case FLOAT64: return fmt.Sprint(v.AsFloat64()) case STRINGSLICE: - return fmt.Sprint(v.asStringSlice()) + j, _ := json.Marshal(v.asStringSlice()) + return string(j) case STRING: return v.stringly default: From 12cf46f4f7f364e5dc82d48fbbad90d7ac1d8673 Mon Sep 17 00:00:00 2001 From: dmathieu Date: Fri, 5 Apr 2024 10:59:56 +0200 Subject: [PATCH 2/5] add changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dee8e73a8d..62f8fb4b95d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,6 +78,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc` and `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` now create a gRPC client in idle mode and with "dns" as the default resolver using [`grpc.NewClient`](https://pkg.go.dev/google.golang.org/grpc#NewClient). (#5151) Because of that `WithDialOption` ignores [`grpc.WithBlock`](https://pkg.go.dev/google.golang.org/grpc#WithBlock), [`grpc.WithTimeout`](https://pkg.go.dev/google.golang.org/grpc#WithTimeout), and [`grpc.WithReturnConnectionError`](https://pkg.go.dev/google.golang.org/grpc#WithReturnConnectionError). Notice that [`grpc.DialContext`](https://pkg.go.dev/google.golang.org/grpc#DialContext) which was used before is now deprecated. +- Slice attribute values in `go.opentelemetry.io/otel/attribute` are now emitted as their JSON representation. (#5159) ### Fixed From 4988d35916bc54c21aeb96fc533ba91b91e30031 Mon Sep 17 00:00:00 2001 From: dmathieu Date: Fri, 5 Apr 2024 11:03:10 +0200 Subject: [PATCH 3/5] fix resource tests --- sdk/resource/resource_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sdk/resource/resource_test.go b/sdk/resource/resource_test.go index 8bf3fa1ac6b..85e29087b0a 100644 --- a/sdk/resource/resource_test.go +++ b/sdk/resource/resource_test.go @@ -601,8 +601,9 @@ func TestWithProcessCommandArgs(t *testing.T) { ) require.NoError(t, err) + jsonCommandArgs, _ := json.Marshal(fakeCommandArgs) require.EqualValues(t, map[string]string{ - "process.command_args": fmt.Sprint(fakeCommandArgs), + "process.command_args": string(jsonCommandArgs), }, toMap(res)) } @@ -671,11 +672,12 @@ func TestWithProcess(t *testing.T) { ) require.NoError(t, err) + jsonCommandArgs, _ := json.Marshal(fakeCommandArgs) require.EqualValues(t, map[string]string{ "process.pid": fmt.Sprint(fakePID), "process.executable.name": fakeExecutableName, "process.executable.path": fakeExecutablePath, - "process.command_args": fmt.Sprint(fakeCommandArgs), + "process.command_args": string(jsonCommandArgs), "process.owner": fakeOwner, "process.runtime.name": fakeRuntimeName, "process.runtime.version": fakeRuntimeVersion, From 6b69c8f4af6a25741fcdb43a1820817a0443f806 Mon Sep 17 00:00:00 2001 From: dmathieu Date: Mon, 8 Apr 2024 09:49:12 +0200 Subject: [PATCH 4/5] indicate invalid slice if we couldn't turn them into json --- attribute/value.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/attribute/value.go b/attribute/value.go index 027177b2e62..9ea0ecbbd27 100644 --- a/attribute/value.go +++ b/attribute/value.go @@ -231,17 +231,26 @@ func (v Value) Emit() string { case BOOL: return strconv.FormatBool(v.AsBool()) case INT64SLICE: - j, _ := json.Marshal(v.asInt64Slice()) + j, err := json.Marshal(v.asInt64Slice()) + if err != nil { + return fmt.Sprintf("invalid: %v", v.asInt64Slice()) + } return string(j) case INT64: return strconv.FormatInt(v.AsInt64(), 10) case FLOAT64SLICE: - j, _ := json.Marshal(v.asFloat64Slice()) + j, err := json.Marshal(v.asFloat64Slice()) + if err != nil { + return fmt.Sprintf("invalid: %v", v.asFloat64Slice()) + } return string(j) case FLOAT64: return fmt.Sprint(v.AsFloat64()) case STRINGSLICE: - j, _ := json.Marshal(v.asStringSlice()) + j, err := json.Marshal(v.asStringSlice()) + if err != nil { + return fmt.Sprintf("invalid: %v", v.asStringSlice()) + } return string(j) case STRING: return v.stringly From 9e96e517aae12f8a72fcfe24eca2f029df5dab10 Mon Sep 17 00:00:00 2001 From: dmathieu Date: Tue, 7 May 2024 09:21:24 +0200 Subject: [PATCH 5/5] move changelog entry to the unreleased section --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62f8fb4b95d..31974f04c65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Improve performance of baggage member character validation in `go.opentelemetry.io/otel/baggage`. (#5214) - The `otel-collector` example now uses docker compose to bring up services instead of kubernetes. (#5244) +### Fixed + +- Slice attribute values in `go.opentelemetry.io/otel/attribute` are now emitted as their JSON representation. (#5159) + ## [1.25.0/0.47.0/0.0.8/0.1.0-alpha] 2024-04-05 ### Added @@ -78,7 +82,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc` and `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` now create a gRPC client in idle mode and with "dns" as the default resolver using [`grpc.NewClient`](https://pkg.go.dev/google.golang.org/grpc#NewClient). (#5151) Because of that `WithDialOption` ignores [`grpc.WithBlock`](https://pkg.go.dev/google.golang.org/grpc#WithBlock), [`grpc.WithTimeout`](https://pkg.go.dev/google.golang.org/grpc#WithTimeout), and [`grpc.WithReturnConnectionError`](https://pkg.go.dev/google.golang.org/grpc#WithReturnConnectionError). Notice that [`grpc.DialContext`](https://pkg.go.dev/google.golang.org/grpc#DialContext) which was used before is now deprecated. -- Slice attribute values in `go.opentelemetry.io/otel/attribute` are now emitted as their JSON representation. (#5159) ### Fixed