Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions attribute/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,29 @@ func (k Key) StringSlice(v []string) KeyValue {
}
}

// Slice creates a KeyValue instance with a SLICE Value.
//
// If creating both a key and value at the same time, use the provided
// convenience function instead -- Slice(name, value).
func (k Key) Slice(v []Value) KeyValue {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be better called Array to match the OTel Spec.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😁 For reference only, a recommendation from AI: Sequence.

I consulted AI to obtain an answer based on a neutral stance across multiple languages.

https://chatgpt.com/share/68beda19-2a1c-800c-8eb6-0e31bf7a8170 —— The content is in Chinese; please translate it into English yourself for reference.

return KeyValue{
Key: k,
Value: SliceValue(v),
}
}

// Map creates a KeyValue instance with a MAP Value.
// v is sorted by key.
//
// If creating both a key and value at the same time, use the provided
// convenience function instead -- Map(name, value).
func (k Key) Map(v []KeyValue) KeyValue {
return KeyValue{
Key: k,
Value: MapValue(v),
}
}

// Defined returns true for non-empty keys.
func (k Key) Defined() bool {
return len(k) != 0
Expand Down
13 changes: 12 additions & 1 deletion attribute/kv.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type KeyValue struct {

// Valid returns if kv is a valid OpenTelemetry attribute.
func (kv KeyValue) Valid() bool {
return kv.Key.Defined() && kv.Value.Type() != INVALID
return kv.Key.Defined()
}

// Bool creates a KeyValue with a BOOL Value type.
Expand Down Expand Up @@ -68,6 +68,17 @@ func StringSlice(k string, v []string) KeyValue {
return Key(k).StringSlice(v)
}

// Slice creates a KeyValue with a SLICE Value type.
func Slice(k string, v []Value) KeyValue {
return Key(k).Slice(v)
}

// Map creates a KeyValue with a Map Value type.
// v are sorted by key.
func Map(k string, v []KeyValue) KeyValue {
return Key(k).Map(v)
}

// Stringer creates a new key-value pair with a passed name and a string
// value generated by the passed Stringer interface.
func Stringer(k string, v fmt.Stringer) KeyValue {
Expand Down
7 changes: 3 additions & 4 deletions attribute/kv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,10 @@ func TestKeyValueValid(t *testing.T) {
kv: attribute.Key("").Bool(true),
},
{
desc: "INVALID value type should be invalid",
valid: false,
desc: "empty value type should be valid",
valid: true,
kv: attribute.KeyValue{
Key: attribute.Key("valid key"),
// Default type is INVALID.
Key: attribute.Key("valid key"),
Value: attribute.Value{},
},
},
Expand Down
9 changes: 6 additions & 3 deletions attribute/type_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 59 additions & 12 deletions attribute/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
package attribute // import "go.opentelemetry.io/otel/attribute"

import (
"cmp"
"encoding/json"
"fmt"
"reflect"
"slices"
"strconv"

attribute "go.opentelemetry.io/otel/attribute/internal"
Expand All @@ -22,28 +24,50 @@ type Value struct {
vtype Type
numeric uint64
stringly string
slice interface{}
iface any
}

const (
// INVALID is used for a Value with no value set.
INVALID Type = iota
// EMPTY is used for a Value with no value set.
EMPTY Type = iota

// BOOL is a boolean Type Value.
BOOL

// INT64 is a 64-bit signed integral Type Value.
INT64

// FLOAT64 is a 64-bit floating point Type Value.
FLOAT64

// STRING is a string Type Value.
STRING

// BOOLSLICE is a slice of booleans Type Value.
BOOLSLICE

// INT64SLICE is a slice of 64-bit signed integral numbers Type Value.
INT64SLICE

// FLOAT64SLICE is a slice of 64-bit floating point numbers Type Value.
FLOAT64SLICE

// STRINGSLICE is a slice of strings Type Value.
STRINGSLICE

// BYTES is a slice of bytes Type value.
BYTES

// SLICE is a slice of heterogeneous values Type value.
SLICE

// MAP is a map of heterogeneous values Type value.
MAP

// INVALID is used for a Value with no value set.
//
// Deprecated: Use EMPTY instead as an empty value is a valid value.
INVALID = EMPTY
)

// BoolValue creates a BOOL Value.
Expand All @@ -56,7 +80,7 @@ func BoolValue(v bool) Value {

// BoolSliceValue creates a BOOLSLICE Value.
func BoolSliceValue(v []bool) Value {
return Value{vtype: BOOLSLICE, slice: attribute.BoolSliceValue(v)}
return Value{vtype: BOOLSLICE, iface: attribute.BoolSliceValue(v)}
}

// IntValue creates an INT64 Value.
Expand All @@ -73,7 +97,7 @@ func IntSliceValue(v []int) Value {
}
return Value{
vtype: INT64SLICE,
slice: cp.Elem().Interface(),
iface: cp.Elem().Interface(),
}
}

Expand All @@ -87,7 +111,7 @@ func Int64Value(v int64) Value {

// Int64SliceValue creates an INT64SLICE Value.
func Int64SliceValue(v []int64) Value {
return Value{vtype: INT64SLICE, slice: attribute.Int64SliceValue(v)}
return Value{vtype: INT64SLICE, iface: attribute.Int64SliceValue(v)}
}

// Float64Value creates a FLOAT64 Value.
Expand All @@ -100,7 +124,7 @@ func Float64Value(v float64) Value {

// Float64SliceValue creates a FLOAT64SLICE Value.
func Float64SliceValue(v []float64) Value {
return Value{vtype: FLOAT64SLICE, slice: attribute.Float64SliceValue(v)}
return Value{vtype: FLOAT64SLICE, iface: attribute.Float64SliceValue(v)}
}

// StringValue creates a STRING Value.
Expand All @@ -113,7 +137,30 @@ func StringValue(v string) Value {

// StringSliceValue creates a STRINGSLICE Value.
func StringSliceValue(v []string) Value {
return Value{vtype: STRINGSLICE, slice: attribute.StringSliceValue(v)}
return Value{vtype: STRINGSLICE, iface: attribute.StringSliceValue(v)}
}

// SliceValue creates a SLICE Value.
func SliceValue(v []Value) Value {
var zero Value
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))).Elem()
reflect.Copy(cp, reflect.ValueOf(v))
return Value{vtype: SLICE, iface: cp.Interface()}
}

// MapValue creates a MAP Value.
// v is sorted by key.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also deduplicate keys, but this is an implementation detail not relevant for the prototype.

func MapValue(v []KeyValue) Value {
sv := make([]KeyValue, len(v))
copy(sv, v)
slices.SortFunc(sv, func(a, b KeyValue) int {
return cmp.Compare(a.Key, b.Key)
})

var zero KeyValue
cp := reflect.New(reflect.ArrayOf(len(sv), reflect.TypeOf(zero))).Elem()
reflect.Copy(cp, reflect.ValueOf(sv))
return Value{vtype: MAP, iface: cp.Interface()}
}

// Type returns a type of the Value.
Expand All @@ -137,7 +184,7 @@ func (v Value) AsBoolSlice() []bool {
}

func (v Value) asBoolSlice() []bool {
return attribute.AsBoolSlice(v.slice)
return attribute.AsBoolSlice(v.iface)
}

// AsInt64 returns the int64 value. Make sure that the Value's type is
Expand All @@ -156,7 +203,7 @@ func (v Value) AsInt64Slice() []int64 {
}

func (v Value) asInt64Slice() []int64 {
return attribute.AsInt64Slice(v.slice)
return attribute.AsInt64Slice(v.iface)
}

// AsFloat64 returns the float64 value. Make sure that the Value's
Expand All @@ -175,7 +222,7 @@ func (v Value) AsFloat64Slice() []float64 {
}

func (v Value) asFloat64Slice() []float64 {
return attribute.AsFloat64Slice(v.slice)
return attribute.AsFloat64Slice(v.iface)
}

// AsString returns the string value. Make sure that the Value's type
Expand All @@ -194,7 +241,7 @@ func (v Value) AsStringSlice() []string {
}

func (v Value) asStringSlice() []string {
return attribute.AsStringSlice(v.slice)
return attribute.AsStringSlice(v.iface)
}

type unknownValueType struct{}
Expand Down
Loading
Loading