Skip to content

Commit

Permalink
log: Add ValueFromAttribute and KeyValueFromAttribute (#6180)
Browse files Browse the repository at this point in the history
Fixes #6158

Related spec PR:
open-telemetry/opentelemetry-specification#4373

Benchmark results:

```
goos: linux
goarch: amd64
pkg: go.opentelemetry.io/otel/log
cpu: 13th Gen Intel(R) Core(TM) i7-13800H
BenchmarkKeyValueFromAttribute/Empty-20              72029505                16.47 ns/op            0 B/op          0 allocs/op
BenchmarkKeyValueFromAttribute/Bool-20               68560222                16.99 ns/op            0 B/op          0 allocs/op
BenchmarkKeyValueFromAttribute/BoolSlice-20          14647401                76.21 ns/op           50 B/op          2 allocs/op
BenchmarkKeyValueFromAttribute/Int64-20              70737378                16.92 ns/op            0 B/op          0 allocs/op
BenchmarkKeyValueFromAttribute/Int64Slice-20         16780069                96.87 ns/op           64 B/op          2 allocs/op
BenchmarkKeyValueFromAttribute/Float64-20            59299638                16.93 ns/op            0 B/op          0 allocs/op
BenchmarkKeyValueFromAttribute/Float64Slice-20       12691222               106.2 ns/op            64 B/op          2 allocs/op
BenchmarkKeyValueFromAttribute/String-20             63837711                16.97 ns/op            0 B/op          0 allocs/op
BenchmarkKeyValueFromAttribute/StringSlice-20         9251001               114.7 ns/op            80 B/op          2 allocs/op
PASS
ok      go.opentelemetry.io/otel/log    14.776s
```
  • Loading branch information
pellared authored Jan 22, 2025
1 parent 538e869 commit d7ebb7a
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased]

### Added

- Add `ValueFromAttribute` and `KeyValueFromAttribute` in `go.opentelemetry.io/otel/log`. (#6180)

<!-- Released section -->
<!-- Don't change this section unless doing release -->

Expand Down
56 changes: 56 additions & 0 deletions log/keyvalue.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"strconv"
"unsafe"

"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/internal/global"
)

Expand Down Expand Up @@ -385,3 +386,58 @@ func Empty(key string) KeyValue {
func (a KeyValue) String() string {
return fmt.Sprintf("%s:%s", a.Key, a.Value)
}

// ValueFromAttribute converts [attribute.Value] to [Value].
func ValueFromAttribute(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...)
}
// This code should never be reached
// as log attributes are a superset of standard attributes.
panic("unknown attribute type")
}

// KeyValueFromAttribute converts [attribute.KeyValue] to [KeyValue].
func KeyValueFromAttribute(kv attribute.KeyValue) KeyValue {
return KeyValue{
Key: string(kv.Key),
Value: ValueFromAttribute(kv.Value),
}
}
53 changes: 53 additions & 0 deletions log/keyvalue_bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package log_test
import (
"testing"

"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/log"
)

Expand Down Expand Up @@ -232,3 +233,55 @@ func BenchmarkValueEqual(b *testing.B) {
}
}
}

func BenchmarkKeyValueFromAttribute(b *testing.B) {
testCases := []struct {
desc string
kv attribute.KeyValue
}{
{
desc: "Empty",
kv: attribute.KeyValue{},
},
{
desc: "Bool",
kv: attribute.Bool("k", true),
},
{
desc: "BoolSlice",
kv: attribute.BoolSlice("k", []bool{true, false}),
},
{
desc: "Int64",
kv: attribute.Int64("k", 13),
},
{
desc: "Int64Slice",
kv: attribute.Int64Slice("k", []int64{12, 34}),
},
{
desc: "Float64",
kv: attribute.Float64("k", 3.14),
},
{
desc: "Float64Slice",
kv: attribute.Float64Slice("k", []float64{3.14, 2.72}),
},
{
desc: "String",
kv: attribute.String("k", "foo"),
},
{
desc: "StringSlice",
kv: attribute.StringSlice("k", []string{"foo", "bar"}),
},
}
for _, tc := range testCases {
b.Run(tc.desc, func(b *testing.B) {
b.ReportAllocs()
for range b.N {
outKV = log.KeyValueFromAttribute(tc.kv)
}
})
}
}
125 changes: 125 additions & 0 deletions log/keyvalue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -309,6 +310,130 @@ func TestValueString(t *testing.T) {
}
}

func TestValueFromAttribute(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.ValueFromAttribute(tc.v)
if !got.Equal(tc.want) {
t.Errorf("got: %v; want:%v", got, tc.want)
}
})
}
}

func TestKeyValueFromAttribute(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.KeyValueFromAttribute(tc.kv)
if !got.Equal(tc.want) {
t.Errorf("got: %v; want:%v", got, tc.want)
}
})
}
}

type logSink struct {
logr.LogSink

Expand Down

0 comments on commit d7ebb7a

Please sign in to comment.