From 8cf1c4dbc2e9b4cf692b77a9815099089aef9196 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 20 Jan 2025 11:43:57 +0100 Subject: [PATCH 01/16] Sketch out API and tests --- log/convert.go | 16 +++++++++++++++ log/convert_test.go | 49 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 log/convert.go create mode 100644 log/convert_test.go diff --git a/log/convert.go b/log/convert.go new file mode 100644 index 00000000000..3a40b10d10b --- /dev/null +++ b/log/convert.go @@ -0,0 +1,16 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package log // import "go.opentelemetry.io/otel/log" + +import "go.opentelemetry.io/otel/attribute" + +// ConvertAttributeValue converts [attribute.Value) to [Value]. +func ConvertAttributeValue(v attribute.Value) Value { + return Value{} +} + +// ConvertAttributeKeyValue converts [attribute.KeyValue) to [KeyValue]. +func ConvertAttributeKeyValue(kv attribute.KeyValue) KeyValue { + return KeyValue{} +} diff --git a/log/convert_test.go b/log/convert_test.go new file mode 100644 index 00000000000..9bb8343ee73 --- /dev/null +++ b/log/convert_test.go @@ -0,0 +1,49 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package log_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/log" +) + +func TestConvertAttributeValue(t *testing.T) { + testCases := []struct { + desc string + v attribute.Value + want log.Value + }{ + { + desc: "", + }, + } + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + got := log.ConvertAttributeValue(tc.v) + assert.True(t, got.Equal(tc.want), "%v.Equal(%v)", got, tc.want) + }) + } +} + +func TestConvertAttributeKeyValue(t *testing.T) { + testCases := []struct { + desc string + kv attribute.KeyValue + want log.KeyValue + }{ + { + desc: "", + }, + } + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + got := log.ConvertAttributeKeyValue(tc.kv) + assert.True(t, got.Equal(tc.want), "%v.Equal(%v)", got, tc.want) + }) + } +} From 2ae47b8b6b78b794b2f5b9940fb318a32d7b1155 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 20 Jan 2025 11:53:20 +0100 Subject: [PATCH 02/16] Empty, Bool --- log/convert.go | 26 ++++++++++++++++++++++++-- log/convert_test.go | 9 ++++++++- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/log/convert.go b/log/convert.go index 3a40b10d10b..dd68373e8c2 100644 --- a/log/convert.go +++ b/log/convert.go @@ -3,11 +3,33 @@ package log // import "go.opentelemetry.io/otel/log" -import "go.opentelemetry.io/otel/attribute" +import ( + "go.opentelemetry.io/otel/attribute" +) // ConvertAttributeValue converts [attribute.Value) to [Value]. func ConvertAttributeValue(v attribute.Value) Value { - return Value{} + switch v.Type() { + case attribute.INVALID: + return Value{} + case attribute.BOOL: + return BoolValue(v.AsBool()) + case attribute.BOOLSLICE: + // return v.asBoolSlice() + case attribute.INT64: + // return v.AsInt64() + case attribute.INT64SLICE: + // return v.asInt64Slice() + case attribute.FLOAT64: + // return v.AsFloat64() + case attribute.FLOAT64SLICE: + // return v.asFloat64Slice() + case attribute.STRING: + // return v.stringly + case attribute.STRINGSLICE: + // return v.asStringSlice() + } + panic("unknown attribute type") } // ConvertAttributeKeyValue converts [attribute.KeyValue) to [KeyValue]. diff --git a/log/convert_test.go b/log/convert_test.go index 9bb8343ee73..443b4710f78 100644 --- a/log/convert_test.go +++ b/log/convert_test.go @@ -19,7 +19,14 @@ func TestConvertAttributeValue(t *testing.T) { want log.Value }{ { - desc: "", + desc: "Empty", + v: attribute.Value{}, + want: log.Value{}, + }, + { + desc: "Bool", + v: attribute.BoolValue(true), + want: log.BoolValue(true), }, } for _, tc := range testCases { From 9a5989363403847fcb29c79b46e0bc95ee876c25 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 20 Jan 2025 11:57:02 +0100 Subject: [PATCH 03/16] BoolSlice --- log/convert.go | 13 +++++++++---- log/convert_test.go | 5 +++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/log/convert.go b/log/convert.go index dd68373e8c2..25ce8022705 100644 --- a/log/convert.go +++ b/log/convert.go @@ -8,14 +8,19 @@ import ( ) // ConvertAttributeValue converts [attribute.Value) to [Value]. -func ConvertAttributeValue(v attribute.Value) Value { - switch v.Type() { +func ConvertAttributeValue(value attribute.Value) Value { + switch value.Type() { case attribute.INVALID: return Value{} case attribute.BOOL: - return BoolValue(v.AsBool()) + return BoolValue(value.AsBool()) case attribute.BOOLSLICE: - // return v.asBoolSlice() + val := value.AsBoolSlice() + res := make([]Value, 0, len(val)) + for _, v := range val { + res = append(res, BoolValue(v)) + } + return SliceValue(res...) case attribute.INT64: // return v.AsInt64() case attribute.INT64SLICE: diff --git a/log/convert_test.go b/log/convert_test.go index 443b4710f78..1e2eb38ffec 100644 --- a/log/convert_test.go +++ b/log/convert_test.go @@ -28,6 +28,11 @@ func TestConvertAttributeValue(t *testing.T) { v: attribute.BoolValue(true), want: log.BoolValue(true), }, + { + desc: "BoolSlice", + v: attribute.BoolSliceValue([]bool{true, false}), + want: log.SliceValue(log.BoolValue(true), log.BoolValue(false)), + }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { From 592b1366830b700732fb04b8df7c713ccd25c4d8 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 20 Jan 2025 12:04:20 +0100 Subject: [PATCH 04/16] Int64 --- log/convert.go | 2 +- log/convert_test.go | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/log/convert.go b/log/convert.go index 25ce8022705..841af4c9e47 100644 --- a/log/convert.go +++ b/log/convert.go @@ -22,7 +22,7 @@ func ConvertAttributeValue(value attribute.Value) Value { } return SliceValue(res...) case attribute.INT64: - // return v.AsInt64() + return Int64Value(value.AsInt64()) case attribute.INT64SLICE: // return v.asInt64Slice() case attribute.FLOAT64: diff --git a/log/convert_test.go b/log/convert_test.go index 1e2eb38ffec..1da461e58d9 100644 --- a/log/convert_test.go +++ b/log/convert_test.go @@ -6,8 +6,6 @@ package log_test import ( "testing" - "github.com/stretchr/testify/assert" - "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/log" ) @@ -33,11 +31,18 @@ func TestConvertAttributeValue(t *testing.T) { v: attribute.BoolSliceValue([]bool{true, false}), want: log.SliceValue(log.BoolValue(true), log.BoolValue(false)), }, + { + desc: "Int64", + v: attribute.Int64Value(13), + want: log.Int64Value(13), + }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { got := log.ConvertAttributeValue(tc.v) - assert.True(t, got.Equal(tc.want), "%v.Equal(%v)", got, tc.want) + if !got.Equal(tc.want) { + t.Errorf("got: %v; want:%v", got, tc.want) + } }) } } @@ -55,7 +60,9 @@ func TestConvertAttributeKeyValue(t *testing.T) { for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { got := log.ConvertAttributeKeyValue(tc.kv) - assert.True(t, got.Equal(tc.want), "%v.Equal(%v)", got, tc.want) + if !got.Equal(tc.want) { + t.Errorf("got: %v; want:%v", got, tc.want) + } }) } } From 0dc03f121613c56d42b023bc79c8cd469fe00c0b Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 20 Jan 2025 12:05:58 +0100 Subject: [PATCH 05/16] Int64Slice --- log/convert.go | 7 ++++++- log/convert_test.go | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/log/convert.go b/log/convert.go index 841af4c9e47..4d88866c148 100644 --- a/log/convert.go +++ b/log/convert.go @@ -24,7 +24,12 @@ func ConvertAttributeValue(value attribute.Value) Value { case attribute.INT64: return Int64Value(value.AsInt64()) case attribute.INT64SLICE: - // return v.asInt64Slice() + val := value.AsInt64Slice() + res := make([]Value, 0, len(val)) + for _, v := range val { + res = append(res, Int64Value(v)) + } + return SliceValue(res...) case attribute.FLOAT64: // return v.AsFloat64() case attribute.FLOAT64SLICE: diff --git a/log/convert_test.go b/log/convert_test.go index 1da461e58d9..5e18a42ea69 100644 --- a/log/convert_test.go +++ b/log/convert_test.go @@ -36,6 +36,11 @@ func TestConvertAttributeValue(t *testing.T) { v: attribute.Int64Value(13), want: log.Int64Value(13), }, + { + desc: "Int64Slice", + v: attribute.Int64SliceValue([]int64{12, 34}), + want: log.SliceValue(log.Int64Value(12), log.Int64Value(34)), + }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { From 105664d6079fc3145424829cc340db746deaecad Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 20 Jan 2025 12:10:26 +0100 Subject: [PATCH 06/16] Float64, Float64Slice --- log/convert.go | 9 +++++++-- log/convert_test.go | 10 ++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/log/convert.go b/log/convert.go index 4d88866c148..e383c9b9387 100644 --- a/log/convert.go +++ b/log/convert.go @@ -31,9 +31,14 @@ func ConvertAttributeValue(value attribute.Value) Value { } return SliceValue(res...) case attribute.FLOAT64: - // return v.AsFloat64() + return Float64Value(value.AsFloat64()) case attribute.FLOAT64SLICE: - // return v.asFloat64Slice() + val := value.AsFloat64Slice() + res := make([]Value, 0, len(val)) + for _, v := range val { + res = append(res, Float64Value(v)) + } + return SliceValue(res...) case attribute.STRING: // return v.stringly case attribute.STRINGSLICE: diff --git a/log/convert_test.go b/log/convert_test.go index 5e18a42ea69..3d415906077 100644 --- a/log/convert_test.go +++ b/log/convert_test.go @@ -41,6 +41,16 @@ func TestConvertAttributeValue(t *testing.T) { v: attribute.Int64SliceValue([]int64{12, 34}), want: log.SliceValue(log.Int64Value(12), log.Int64Value(34)), }, + { + desc: "Float64", + v: attribute.Float64Value(3.14), + want: log.Float64Value(3.14), + }, + { + desc: "Float64Slice", + v: attribute.Float64SliceValue([]float64{3.14, 2.72}), + want: log.SliceValue(log.Float64Value(3.14), log.Float64Value(2.72)), + }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { From bd0215308dd318975bea5f7fe65b63ae799a34c2 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 20 Jan 2025 12:12:29 +0100 Subject: [PATCH 07/16] String, StringSlice --- log/convert.go | 9 +++++++-- log/convert_test.go | 10 ++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/log/convert.go b/log/convert.go index e383c9b9387..05577a99898 100644 --- a/log/convert.go +++ b/log/convert.go @@ -40,9 +40,14 @@ func ConvertAttributeValue(value attribute.Value) Value { } return SliceValue(res...) case attribute.STRING: - // return v.stringly + return StringValue(value.AsString()) case attribute.STRINGSLICE: - // return v.asStringSlice() + val := value.AsStringSlice() + res := make([]Value, 0, len(val)) + for _, v := range val { + res = append(res, StringValue(v)) + } + return SliceValue(res...) } panic("unknown attribute type") } diff --git a/log/convert_test.go b/log/convert_test.go index 3d415906077..ce164c20e5e 100644 --- a/log/convert_test.go +++ b/log/convert_test.go @@ -51,6 +51,16 @@ func TestConvertAttributeValue(t *testing.T) { v: attribute.Float64SliceValue([]float64{3.14, 2.72}), want: log.SliceValue(log.Float64Value(3.14), log.Float64Value(2.72)), }, + { + desc: "String", + v: attribute.StringValue("foo"), + want: log.StringValue("foo"), + }, + { + desc: "StringSlice", + v: attribute.StringSliceValue([]string{"foo", "bar"}), + want: log.SliceValue(log.StringValue("foo"), log.StringValue("bar")), + }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { From b6ffbea5e52a8ef265fb9c4ba6b1b4e68da7fc81 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 20 Jan 2025 12:15:53 +0100 Subject: [PATCH 08/16] ConvertAttributeKeyValue --- log/convert.go | 5 ++++- log/convert_test.go | 44 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/log/convert.go b/log/convert.go index 05577a99898..796821d1a21 100644 --- a/log/convert.go +++ b/log/convert.go @@ -54,5 +54,8 @@ func ConvertAttributeValue(value attribute.Value) Value { // ConvertAttributeKeyValue converts [attribute.KeyValue) to [KeyValue]. func ConvertAttributeKeyValue(kv attribute.KeyValue) KeyValue { - return KeyValue{} + return KeyValue{ + Key: string(kv.Key), + Value: ConvertAttributeValue(kv.Value), + } } diff --git a/log/convert_test.go b/log/convert_test.go index ce164c20e5e..57a16a0da84 100644 --- a/log/convert_test.go +++ b/log/convert_test.go @@ -79,7 +79,49 @@ func TestConvertAttributeKeyValue(t *testing.T) { want log.KeyValue }{ { - desc: "", + desc: "Empty", + kv: attribute.KeyValue{}, + want: log.KeyValue{}, + }, + { + desc: "Bool", + kv: attribute.Bool("k", true), + want: log.Bool("k", true), + }, + { + desc: "BoolSlice", + kv: attribute.BoolSlice("k", []bool{true, false}), + want: log.Slice("k", log.BoolValue(true), log.BoolValue(false)), + }, + { + desc: "Int64", + kv: attribute.Int64("k", 13), + want: log.Int64("k", 13), + }, + { + desc: "Int64Slice", + kv: attribute.Int64Slice("k", []int64{12, 34}), + want: log.Slice("k", log.Int64Value(12), log.Int64Value(34)), + }, + { + desc: "Float64", + kv: attribute.Float64("k", 3.14), + want: log.Float64("k", 3.14), + }, + { + desc: "Float64Slice", + kv: attribute.Float64Slice("k", []float64{3.14, 2.72}), + want: log.Slice("k", log.Float64Value(3.14), log.Float64Value(2.72)), + }, + { + desc: "String", + kv: attribute.String("k", "foo"), + want: log.String("k", "foo"), + }, + { + desc: "StringSlice", + kv: attribute.StringSlice("k", []string{"foo", "bar"}), + want: log.Slice("k", log.StringValue("foo"), log.StringValue("bar")), }, } for _, tc := range testCases { From 7e25711dc88393f984dc1b4f5daaf8526434c08c Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 20 Jan 2025 12:26:31 +0100 Subject: [PATCH 09/16] Add benchmark and refactor --- log/convert.go | 61 ----------------- log/convert_test.go | 135 ------------------------------------- log/keyvalue.go | 54 +++++++++++++++ log/keyvalue_bench_test.go | 63 +++++++++++++++++ log/keyvalue_test.go | 124 ++++++++++++++++++++++++++++++++++ 5 files changed, 241 insertions(+), 196 deletions(-) delete mode 100644 log/convert.go delete mode 100644 log/convert_test.go diff --git a/log/convert.go b/log/convert.go deleted file mode 100644 index 796821d1a21..00000000000 --- a/log/convert.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package log // import "go.opentelemetry.io/otel/log" - -import ( - "go.opentelemetry.io/otel/attribute" -) - -// ConvertAttributeValue converts [attribute.Value) to [Value]. -func ConvertAttributeValue(value attribute.Value) Value { - switch value.Type() { - case attribute.INVALID: - return Value{} - case attribute.BOOL: - return BoolValue(value.AsBool()) - case attribute.BOOLSLICE: - val := value.AsBoolSlice() - res := make([]Value, 0, len(val)) - for _, v := range val { - res = append(res, BoolValue(v)) - } - return SliceValue(res...) - case attribute.INT64: - return Int64Value(value.AsInt64()) - case attribute.INT64SLICE: - val := value.AsInt64Slice() - res := make([]Value, 0, len(val)) - for _, v := range val { - res = append(res, Int64Value(v)) - } - return SliceValue(res...) - case attribute.FLOAT64: - return Float64Value(value.AsFloat64()) - case attribute.FLOAT64SLICE: - val := value.AsFloat64Slice() - res := make([]Value, 0, len(val)) - for _, v := range val { - res = append(res, Float64Value(v)) - } - return SliceValue(res...) - case attribute.STRING: - return StringValue(value.AsString()) - case attribute.STRINGSLICE: - val := value.AsStringSlice() - res := make([]Value, 0, len(val)) - for _, v := range val { - res = append(res, StringValue(v)) - } - return SliceValue(res...) - } - panic("unknown attribute type") -} - -// ConvertAttributeKeyValue converts [attribute.KeyValue) to [KeyValue]. -func ConvertAttributeKeyValue(kv attribute.KeyValue) KeyValue { - return KeyValue{ - Key: string(kv.Key), - Value: ConvertAttributeValue(kv.Value), - } -} diff --git a/log/convert_test.go b/log/convert_test.go deleted file mode 100644 index 57a16a0da84..00000000000 --- a/log/convert_test.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package log_test - -import ( - "testing" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/log" -) - -func TestConvertAttributeValue(t *testing.T) { - testCases := []struct { - desc string - v attribute.Value - want log.Value - }{ - { - desc: "Empty", - v: attribute.Value{}, - want: log.Value{}, - }, - { - desc: "Bool", - v: attribute.BoolValue(true), - want: log.BoolValue(true), - }, - { - desc: "BoolSlice", - v: attribute.BoolSliceValue([]bool{true, false}), - want: log.SliceValue(log.BoolValue(true), log.BoolValue(false)), - }, - { - desc: "Int64", - v: attribute.Int64Value(13), - want: log.Int64Value(13), - }, - { - desc: "Int64Slice", - v: attribute.Int64SliceValue([]int64{12, 34}), - want: log.SliceValue(log.Int64Value(12), log.Int64Value(34)), - }, - { - desc: "Float64", - v: attribute.Float64Value(3.14), - want: log.Float64Value(3.14), - }, - { - desc: "Float64Slice", - v: attribute.Float64SliceValue([]float64{3.14, 2.72}), - want: log.SliceValue(log.Float64Value(3.14), log.Float64Value(2.72)), - }, - { - desc: "String", - v: attribute.StringValue("foo"), - want: log.StringValue("foo"), - }, - { - desc: "StringSlice", - v: attribute.StringSliceValue([]string{"foo", "bar"}), - want: log.SliceValue(log.StringValue("foo"), log.StringValue("bar")), - }, - } - for _, tc := range testCases { - t.Run(tc.desc, func(t *testing.T) { - got := log.ConvertAttributeValue(tc.v) - if !got.Equal(tc.want) { - t.Errorf("got: %v; want:%v", got, tc.want) - } - }) - } -} - -func TestConvertAttributeKeyValue(t *testing.T) { - testCases := []struct { - desc string - kv attribute.KeyValue - want log.KeyValue - }{ - { - desc: "Empty", - kv: attribute.KeyValue{}, - want: log.KeyValue{}, - }, - { - desc: "Bool", - kv: attribute.Bool("k", true), - want: log.Bool("k", true), - }, - { - desc: "BoolSlice", - kv: attribute.BoolSlice("k", []bool{true, false}), - want: log.Slice("k", log.BoolValue(true), log.BoolValue(false)), - }, - { - desc: "Int64", - kv: attribute.Int64("k", 13), - want: log.Int64("k", 13), - }, - { - desc: "Int64Slice", - kv: attribute.Int64Slice("k", []int64{12, 34}), - want: log.Slice("k", log.Int64Value(12), log.Int64Value(34)), - }, - { - desc: "Float64", - kv: attribute.Float64("k", 3.14), - want: log.Float64("k", 3.14), - }, - { - desc: "Float64Slice", - kv: attribute.Float64Slice("k", []float64{3.14, 2.72}), - want: log.Slice("k", log.Float64Value(3.14), log.Float64Value(2.72)), - }, - { - desc: "String", - kv: attribute.String("k", "foo"), - want: log.String("k", "foo"), - }, - { - desc: "StringSlice", - kv: attribute.StringSlice("k", []string{"foo", "bar"}), - want: log.Slice("k", log.StringValue("foo"), log.StringValue("bar")), - }, - } - for _, tc := range testCases { - t.Run(tc.desc, func(t *testing.T) { - got := log.ConvertAttributeKeyValue(tc.kv) - if !got.Equal(tc.want) { - t.Errorf("got: %v; want:%v", got, tc.want) - } - }) - } -} diff --git a/log/keyvalue.go b/log/keyvalue.go index 2e1d30c1b88..419f3d95e8c 100644 --- a/log/keyvalue.go +++ b/log/keyvalue.go @@ -15,6 +15,7 @@ import ( "strconv" "unsafe" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/internal/global" ) @@ -385,3 +386,56 @@ func Empty(key string) KeyValue { func (a KeyValue) String() string { return fmt.Sprintf("%s:%s", a.Key, a.Value) } + +// ConvertAttributeValue converts [attribute.Value) to [Value]. +func ConvertAttributeValue(value attribute.Value) Value { + switch value.Type() { + case attribute.INVALID: + return Value{} + case attribute.BOOL: + return BoolValue(value.AsBool()) + case attribute.BOOLSLICE: + val := value.AsBoolSlice() + res := make([]Value, 0, len(val)) + for _, v := range val { + res = append(res, BoolValue(v)) + } + return SliceValue(res...) + case attribute.INT64: + return Int64Value(value.AsInt64()) + case attribute.INT64SLICE: + val := value.AsInt64Slice() + res := make([]Value, 0, len(val)) + for _, v := range val { + res = append(res, Int64Value(v)) + } + return SliceValue(res...) + case attribute.FLOAT64: + return Float64Value(value.AsFloat64()) + case attribute.FLOAT64SLICE: + val := value.AsFloat64Slice() + res := make([]Value, 0, len(val)) + for _, v := range val { + res = append(res, Float64Value(v)) + } + return SliceValue(res...) + case attribute.STRING: + return StringValue(value.AsString()) + case attribute.STRINGSLICE: + val := value.AsStringSlice() + res := make([]Value, 0, len(val)) + for _, v := range val { + res = append(res, StringValue(v)) + } + return SliceValue(res...) + } + panic("unknown attribute type") +} + +// ConvertAttributeKeyValue converts [attribute.KeyValue) to [KeyValue]. +func ConvertAttributeKeyValue(kv attribute.KeyValue) KeyValue { + return KeyValue{ + Key: string(kv.Key), + Value: ConvertAttributeValue(kv.Value), + } +} diff --git a/log/keyvalue_bench_test.go b/log/keyvalue_bench_test.go index 1cfd839d045..8d6deb39e19 100644 --- a/log/keyvalue_bench_test.go +++ b/log/keyvalue_bench_test.go @@ -6,6 +6,7 @@ package log_test import ( "testing" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/log" ) @@ -232,3 +233,65 @@ func BenchmarkValueEqual(b *testing.B) { } } } + +func BenchmarkConvertAttributeKeyValue(b *testing.B) { + testCases := []struct { + desc string + kv attribute.KeyValue + want log.KeyValue + }{ + { + desc: "Empty", + kv: attribute.KeyValue{}, + want: log.KeyValue{}, + }, + { + desc: "Bool", + kv: attribute.Bool("k", true), + want: log.Bool("k", true), + }, + { + desc: "BoolSlice", + kv: attribute.BoolSlice("k", []bool{true, false}), + want: log.Slice("k", log.BoolValue(true), log.BoolValue(false)), + }, + { + desc: "Int64", + kv: attribute.Int64("k", 13), + want: log.Int64("k", 13), + }, + { + desc: "Int64Slice", + kv: attribute.Int64Slice("k", []int64{12, 34}), + want: log.Slice("k", log.Int64Value(12), log.Int64Value(34)), + }, + { + desc: "Float64", + kv: attribute.Float64("k", 3.14), + want: log.Float64("k", 3.14), + }, + { + desc: "Float64Slice", + kv: attribute.Float64Slice("k", []float64{3.14, 2.72}), + want: log.Slice("k", log.Float64Value(3.14), log.Float64Value(2.72)), + }, + { + desc: "String", + kv: attribute.String("k", "foo"), + want: log.String("k", "foo"), + }, + { + desc: "StringSlice", + kv: attribute.StringSlice("k", []string{"foo", "bar"}), + want: log.Slice("k", log.StringValue("foo"), log.StringValue("bar")), + }, + } + for _, tc := range testCases { + b.Run(tc.desc, func(b *testing.B) { + b.ReportAllocs() + for range b.N { + outKV = log.ConvertAttributeKeyValue(tc.kv) + } + }) + } +} diff --git a/log/keyvalue_test.go b/log/keyvalue_test.go index 4d5afcfa328..41a921d3889 100644 --- a/log/keyvalue_test.go +++ b/log/keyvalue_test.go @@ -15,6 +15,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/log" ) @@ -308,6 +309,129 @@ func TestValueString(t *testing.T) { assert.Equal(t, test.want, got) } } +func TestConvertAttributeValue(t *testing.T) { + testCases := []struct { + desc string + v attribute.Value + want log.Value + }{ + { + desc: "Empty", + v: attribute.Value{}, + want: log.Value{}, + }, + { + desc: "Bool", + v: attribute.BoolValue(true), + want: log.BoolValue(true), + }, + { + desc: "BoolSlice", + v: attribute.BoolSliceValue([]bool{true, false}), + want: log.SliceValue(log.BoolValue(true), log.BoolValue(false)), + }, + { + desc: "Int64", + v: attribute.Int64Value(13), + want: log.Int64Value(13), + }, + { + desc: "Int64Slice", + v: attribute.Int64SliceValue([]int64{12, 34}), + want: log.SliceValue(log.Int64Value(12), log.Int64Value(34)), + }, + { + desc: "Float64", + v: attribute.Float64Value(3.14), + want: log.Float64Value(3.14), + }, + { + desc: "Float64Slice", + v: attribute.Float64SliceValue([]float64{3.14, 2.72}), + want: log.SliceValue(log.Float64Value(3.14), log.Float64Value(2.72)), + }, + { + desc: "String", + v: attribute.StringValue("foo"), + want: log.StringValue("foo"), + }, + { + desc: "StringSlice", + v: attribute.StringSliceValue([]string{"foo", "bar"}), + want: log.SliceValue(log.StringValue("foo"), log.StringValue("bar")), + }, + } + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + got := log.ConvertAttributeValue(tc.v) + if !got.Equal(tc.want) { + t.Errorf("got: %v; want:%v", got, tc.want) + } + }) + } +} + +func TestConvertAttributeKeyValue(t *testing.T) { + testCases := []struct { + desc string + kv attribute.KeyValue + want log.KeyValue + }{ + { + desc: "Empty", + kv: attribute.KeyValue{}, + want: log.KeyValue{}, + }, + { + desc: "Bool", + kv: attribute.Bool("k", true), + want: log.Bool("k", true), + }, + { + desc: "BoolSlice", + kv: attribute.BoolSlice("k", []bool{true, false}), + want: log.Slice("k", log.BoolValue(true), log.BoolValue(false)), + }, + { + desc: "Int64", + kv: attribute.Int64("k", 13), + want: log.Int64("k", 13), + }, + { + desc: "Int64Slice", + kv: attribute.Int64Slice("k", []int64{12, 34}), + want: log.Slice("k", log.Int64Value(12), log.Int64Value(34)), + }, + { + desc: "Float64", + kv: attribute.Float64("k", 3.14), + want: log.Float64("k", 3.14), + }, + { + desc: "Float64Slice", + kv: attribute.Float64Slice("k", []float64{3.14, 2.72}), + want: log.Slice("k", log.Float64Value(3.14), log.Float64Value(2.72)), + }, + { + desc: "String", + kv: attribute.String("k", "foo"), + want: log.String("k", "foo"), + }, + { + desc: "StringSlice", + kv: attribute.StringSlice("k", []string{"foo", "bar"}), + want: log.Slice("k", log.StringValue("foo"), log.StringValue("bar")), + }, + } + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + got := log.ConvertAttributeKeyValue(tc.kv) + if !got.Equal(tc.want) { + t.Errorf("got: %v; want:%v", got, tc.want) + } + }) + } +} type logSink struct { logr.LogSink From c9cde196fc2d754696b95aaf0c4cd94cc71b0d0d Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 20 Jan 2025 12:30:49 +0100 Subject: [PATCH 10/16] Add changelog entry --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 599d59cd130..d6fad3b0940 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm +### Added + +- Add `ConvertAttributeValue` and `ConvertAttributeKeyValue` in `go.opentelemetry.io/otel/log`. (#6180) + ## [1.34.0/0.56.0/0.10.0] 2025-01-17 ### Changed From 97e74b44fe1617bd460bb84075b3b5ba27b08dd2 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 20 Jan 2025 12:31:41 +0100 Subject: [PATCH 11/16] add missing EOL --- log/keyvalue_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/log/keyvalue_test.go b/log/keyvalue_test.go index 41a921d3889..bcdeacb8b85 100644 --- a/log/keyvalue_test.go +++ b/log/keyvalue_test.go @@ -309,6 +309,7 @@ func TestValueString(t *testing.T) { assert.Equal(t, test.want, got) } } + func TestConvertAttributeValue(t *testing.T) { testCases := []struct { desc string From 9320619b1cc5f2fae64ff1ad9f4f5bdf5b4dd5a9 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 20 Jan 2025 12:34:30 +0100 Subject: [PATCH 12/16] Add comment --- log/keyvalue.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/log/keyvalue.go b/log/keyvalue.go index 419f3d95e8c..743847ed310 100644 --- a/log/keyvalue.go +++ b/log/keyvalue.go @@ -429,6 +429,8 @@ func ConvertAttributeValue(value attribute.Value) Value { } return SliceValue(res...) } + // This code should never be reached + // as log attributes are a superset of standard attributes. panic("unknown attribute type") } From dd376a48f6a84ce7af8a9ee3298a7eec7d8c6f3c Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 20 Jan 2025 12:35:16 +0100 Subject: [PATCH 13/16] Fix changelog --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6fad3b0940..61a7c8ebe88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,13 +8,13 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] - - - ### Added - Add `ConvertAttributeValue` and `ConvertAttributeKeyValue` in `go.opentelemetry.io/otel/log`. (#6180) + + + ## [1.34.0/0.56.0/0.10.0] 2025-01-17 ### Changed From bc26b1b437b2fede9fe17ab13a5707ab1dc20ecb Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Tue, 21 Jan 2025 17:38:54 +0100 Subject: [PATCH 14/16] Rename functions --- CHANGELOG.md | 2 +- log/keyvalue.go | 10 +++++----- log/keyvalue_bench_test.go | 4 ++-- log/keyvalue_test.go | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61a7c8ebe88..30ada7a39e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added -- Add `ConvertAttributeValue` and `ConvertAttributeKeyValue` in `go.opentelemetry.io/otel/log`. (#6180) +- Add `ValueFromAttribute` and `KeyValueFromAttribute` in `go.opentelemetry.io/otel/log`. (#6180) diff --git a/log/keyvalue.go b/log/keyvalue.go index 743847ed310..4b245450f6c 100644 --- a/log/keyvalue.go +++ b/log/keyvalue.go @@ -387,8 +387,8 @@ func (a KeyValue) String() string { return fmt.Sprintf("%s:%s", a.Key, a.Value) } -// ConvertAttributeValue converts [attribute.Value) to [Value]. -func ConvertAttributeValue(value attribute.Value) Value { +// ValueFromAttribute converts [attribute.Value) to [Value]. +func ValueFromAttribute(value attribute.Value) Value { switch value.Type() { case attribute.INVALID: return Value{} @@ -434,10 +434,10 @@ func ConvertAttributeValue(value attribute.Value) Value { panic("unknown attribute type") } -// ConvertAttributeKeyValue converts [attribute.KeyValue) to [KeyValue]. -func ConvertAttributeKeyValue(kv attribute.KeyValue) KeyValue { +// KeyValueFromAttribute converts [attribute.KeyValue) to [KeyValue]. +func KeyValueFromAttribute(kv attribute.KeyValue) KeyValue { return KeyValue{ Key: string(kv.Key), - Value: ConvertAttributeValue(kv.Value), + Value: ValueFromAttribute(kv.Value), } } diff --git a/log/keyvalue_bench_test.go b/log/keyvalue_bench_test.go index 8d6deb39e19..1094554d459 100644 --- a/log/keyvalue_bench_test.go +++ b/log/keyvalue_bench_test.go @@ -234,7 +234,7 @@ func BenchmarkValueEqual(b *testing.B) { } } -func BenchmarkConvertAttributeKeyValue(b *testing.B) { +func BenchmarkKeyValueFromAttribute(b *testing.B) { testCases := []struct { desc string kv attribute.KeyValue @@ -290,7 +290,7 @@ func BenchmarkConvertAttributeKeyValue(b *testing.B) { b.Run(tc.desc, func(b *testing.B) { b.ReportAllocs() for range b.N { - outKV = log.ConvertAttributeKeyValue(tc.kv) + outKV = log.KeyValueFromAttribute(tc.kv) } }) } diff --git a/log/keyvalue_test.go b/log/keyvalue_test.go index bcdeacb8b85..01e239d356d 100644 --- a/log/keyvalue_test.go +++ b/log/keyvalue_test.go @@ -310,7 +310,7 @@ func TestValueString(t *testing.T) { } } -func TestConvertAttributeValue(t *testing.T) { +func TestValueFromAttribute(t *testing.T) { testCases := []struct { desc string v attribute.Value @@ -364,7 +364,7 @@ func TestConvertAttributeValue(t *testing.T) { } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { - got := log.ConvertAttributeValue(tc.v) + got := log.ValueFromAttribute(tc.v) if !got.Equal(tc.want) { t.Errorf("got: %v; want:%v", got, tc.want) } @@ -372,7 +372,7 @@ func TestConvertAttributeValue(t *testing.T) { } } -func TestConvertAttributeKeyValue(t *testing.T) { +func TestKeyValueFromAttribute(t *testing.T) { testCases := []struct { desc string kv attribute.KeyValue @@ -426,7 +426,7 @@ func TestConvertAttributeKeyValue(t *testing.T) { } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { - got := log.ConvertAttributeKeyValue(tc.kv) + got := log.KeyValueFromAttribute(tc.kv) if !got.Equal(tc.want) { t.Errorf("got: %v; want:%v", got, tc.want) } From ba125b08bdcf459ea938a387240d0ad251881f2d Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Tue, 21 Jan 2025 17:40:19 +0100 Subject: [PATCH 15/16] Fix GoDoc references --- log/keyvalue.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/log/keyvalue.go b/log/keyvalue.go index 4b245450f6c..73e4e7dca10 100644 --- a/log/keyvalue.go +++ b/log/keyvalue.go @@ -387,7 +387,7 @@ func (a KeyValue) String() string { return fmt.Sprintf("%s:%s", a.Key, a.Value) } -// ValueFromAttribute converts [attribute.Value) to [Value]. +// ValueFromAttribute converts [attribute.Value] to [Value]. func ValueFromAttribute(value attribute.Value) Value { switch value.Type() { case attribute.INVALID: @@ -434,7 +434,7 @@ func ValueFromAttribute(value attribute.Value) Value { panic("unknown attribute type") } -// KeyValueFromAttribute converts [attribute.KeyValue) to [KeyValue]. +// KeyValueFromAttribute converts [attribute.KeyValue] to [KeyValue]. func KeyValueFromAttribute(kv attribute.KeyValue) KeyValue { return KeyValue{ Key: string(kv.Key), From 904113d6a10575108cfa3c813c25ea6b3b38b63b Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Tue, 21 Jan 2025 21:46:36 +0100 Subject: [PATCH 16/16] Refactor BenchmarkKeyValueFromAttribute --- log/keyvalue_bench_test.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/log/keyvalue_bench_test.go b/log/keyvalue_bench_test.go index 1094554d459..ac925bac2c2 100644 --- a/log/keyvalue_bench_test.go +++ b/log/keyvalue_bench_test.go @@ -238,52 +238,42 @@ func BenchmarkKeyValueFromAttribute(b *testing.B) { testCases := []struct { desc string kv attribute.KeyValue - want log.KeyValue }{ { desc: "Empty", kv: attribute.KeyValue{}, - want: log.KeyValue{}, }, { desc: "Bool", kv: attribute.Bool("k", true), - want: log.Bool("k", true), }, { desc: "BoolSlice", kv: attribute.BoolSlice("k", []bool{true, false}), - want: log.Slice("k", log.BoolValue(true), log.BoolValue(false)), }, { desc: "Int64", kv: attribute.Int64("k", 13), - want: log.Int64("k", 13), }, { desc: "Int64Slice", kv: attribute.Int64Slice("k", []int64{12, 34}), - want: log.Slice("k", log.Int64Value(12), log.Int64Value(34)), }, { desc: "Float64", kv: attribute.Float64("k", 3.14), - want: log.Float64("k", 3.14), }, { desc: "Float64Slice", kv: attribute.Float64Slice("k", []float64{3.14, 2.72}), - want: log.Slice("k", log.Float64Value(3.14), log.Float64Value(2.72)), }, { desc: "String", kv: attribute.String("k", "foo"), - want: log.String("k", "foo"), }, { desc: "StringSlice", kv: attribute.StringSlice("k", []string{"foo", "bar"}), - want: log.Slice("k", log.StringValue("foo"), log.StringValue("bar")), }, } for _, tc := range testCases {