You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-08-10 22:31:50 +02:00
log: Add ValueFromAttribute and KeyValueFromAttribute (#6180)
Fixes https://github.com/open-telemetry/opentelemetry-go/issues/6158 Related spec PR: https://github.com/open-telemetry/opentelemetry-specification/pull/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 ```
This commit is contained in:
@@ -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 -->
|
||||
|
||||
|
@@ -15,6 +15,7 @@ import (
|
||||
"strconv"
|
||||
"unsafe"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
)
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ package log_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/log"
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -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"
|
||||
)
|
||||
@@ -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
|
||||
|
||||
|
Reference in New Issue
Block a user