1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-07-17 01:12:45 +02:00

Deprecate Array attribute in favor of *Slice types (#2162)

* Deprecate Array attribute in favor of *Slice types

* Use new attr types in Jaeger exporter

* Use slice attr types in otlpmetric

* Use slice attr types in otlptrace

* Use slice attr types in zipkin exporter

* Remove array attr test from deprectated oteltest func

* Use StringSlice for cmd arg resource attr

* Add changes to the changelog

* Remove use of deprecated Array func
This commit is contained in:
Tyler Yahn
2021-08-12 08:05:42 -07:00
committed by GitHub
parent df384a9a33
commit 87d09df346
17 changed files with 592 additions and 204 deletions

View File

@ -11,6 +11,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Added
- Added `ErrorHandlerFunc` to use a function as an `"go.opentelemetry.io/otel".ErrorHandler`. (#2149)
- Added typed slice attribute types and functionality to the `go.opentelemetry.io/otel/attribute` package to replace the existing array type and functions. (#2162)
- `BoolSlice`, `IntSlice`, `Int64Slice`, `Float64Slice`, and `StringSlice` replace the use of the `Array` function in the package.
### Changed
@ -21,6 +23,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- The `go.opentelemetry.io/otel/bridge/opencensus/utils` package is deprecated.
All functionality from this package now exists in the `go.opentelemetry.io/otel/bridge/opencensus` package.
The functions from that package should be used instead. (#2166)
- The `"go.opentelemetry.io/otel/attribute".Array` function and the related `ARRAY` value type is deprecated.
Use the typed `*Slice` functions and types added to the package instead. (#2162)
### Removed

View File

@ -27,9 +27,13 @@ var (
outKV attribute.KeyValue
outBool bool
outBoolSlice []bool
outInt64 int64
outInt64Slice []int64
outFloat64 float64
outFloat64Slice []float64
outStr string
outStrSlice []string
)
func benchmarkAny(k string, v interface{}) func(*testing.B) {
@ -91,6 +95,32 @@ func BenchmarkBool(b *testing.B) {
b.Run("Array", benchmarkArray(k, array))
}
func BenchmarkBoolSlice(b *testing.B) {
k, v := "bool slice", []bool{true, false, true}
kv := attribute.BoolSlice(k, v)
b.Run("Value", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
outV = attribute.BoolSliceValue(v)
}
})
b.Run("KeyValue", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
outKV = attribute.BoolSlice(k, v)
}
})
b.Run("AsBoolSlice", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
outBoolSlice = kv.Value.AsBoolSlice()
}
})
b.Run("Any", benchmarkAny(k, v))
b.Run("Emit", benchmarkEmit(kv))
}
func BenchmarkInt(b *testing.B) {
k, v := "int", int(42)
kv := attribute.Int(k, v)
@ -113,6 +143,26 @@ func BenchmarkInt(b *testing.B) {
b.Run("Array", benchmarkArray(k, array))
}
func BenchmarkIntSlice(b *testing.B) {
k, v := "int slice", []int{42, -3, 12}
kv := attribute.IntSlice(k, v)
b.Run("Value", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
outV = attribute.IntSliceValue(v)
}
})
b.Run("KeyValue", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
outKV = attribute.IntSlice(k, v)
}
})
b.Run("Any", benchmarkAny(k, v))
b.Run("Emit", benchmarkEmit(kv))
}
func BenchmarkInt8(b *testing.B) {
b.Run("Any", benchmarkAny("int8", int8(42)))
}
@ -153,6 +203,32 @@ func BenchmarkInt64(b *testing.B) {
b.Run("Array", benchmarkArray(k, array))
}
func BenchmarkInt64Slice(b *testing.B) {
k, v := "int64 slice", []int64{42, -3, 12}
kv := attribute.Int64Slice(k, v)
b.Run("Value", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
outV = attribute.Int64SliceValue(v)
}
})
b.Run("KeyValue", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
outKV = attribute.Int64Slice(k, v)
}
})
b.Run("AsInt64Slice", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
outInt64Slice = kv.Value.AsInt64Slice()
}
})
b.Run("Any", benchmarkAny(k, v))
b.Run("Emit", benchmarkEmit(kv))
}
func BenchmarkFloat64(b *testing.B) {
k, v := "float64", float64(42)
kv := attribute.Float64(k, v)
@ -181,6 +257,32 @@ func BenchmarkFloat64(b *testing.B) {
b.Run("Array", benchmarkArray(k, array))
}
func BenchmarkFloat64Slice(b *testing.B) {
k, v := "float64 slice", []float64{42, -3, 12}
kv := attribute.Float64Slice(k, v)
b.Run("Value", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
outV = attribute.Float64SliceValue(v)
}
})
b.Run("KeyValue", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
outKV = attribute.Float64Slice(k, v)
}
})
b.Run("AsFloat64Slice", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
outFloat64Slice = kv.Value.AsFloat64Slice()
}
})
b.Run("Any", benchmarkAny(k, v))
b.Run("Emit", benchmarkEmit(kv))
}
func BenchmarkString(b *testing.B) {
k, v := "string", "42"
kv := attribute.String(k, v)
@ -209,12 +311,32 @@ func BenchmarkString(b *testing.B) {
b.Run("Array", benchmarkArray(k, array))
}
func BenchmarkStringSlice(b *testing.B) {
k, v := "float64 slice", []string{"forty-two", "negative three", "twelve"}
kv := attribute.StringSlice(k, v)
b.Run("Value", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
outV = attribute.StringSliceValue(v)
}
})
b.Run("KeyValue", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
outKV = attribute.StringSlice(k, v)
}
})
b.Run("AsStringSlice", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
outStrSlice = kv.Value.AsStringSlice()
}
})
b.Run("Any", benchmarkAny(k, v))
b.Run("Emit", benchmarkEmit(kv))
}
func BenchmarkBytes(b *testing.B) {
b.Run("Any", benchmarkAny("bytes", []byte("bytes")))
}
type test struct{}
func BenchmarkStruct(b *testing.B) {
b.Run("Any", benchmarkAny("struct", test{}))
}

View File

@ -29,6 +29,17 @@ func (k Key) Bool(v bool) KeyValue {
}
}
// BoolSlice creates a KeyValue instance with a BOOLSLICE Value.
//
// If creating both a key and value at the same time, use the provided
// convenience function instead -- BoolSlice(name, value).
func (k Key) BoolSlice(v []bool) KeyValue {
return KeyValue{
Key: k,
Value: BoolSliceValue(v),
}
}
// Int creates a KeyValue instance with an INT64 Value.
//
// If creating both a key and value at the same time, use the provided
@ -40,6 +51,17 @@ func (k Key) Int(v int) KeyValue {
}
}
// IntSlice creates a KeyValue instance with an INT64SLICE Value.
//
// If creating both a key and value at the same time, use the provided
// convenience function instead -- IntSlice(name, value).
func (k Key) IntSlice(v []int) KeyValue {
return KeyValue{
Key: k,
Value: IntSliceValue(v),
}
}
// Int64 creates a KeyValue instance with an INT64 Value.
//
// If creating both a key and value at the same time, use the provided
@ -51,6 +73,17 @@ func (k Key) Int64(v int64) KeyValue {
}
}
// Int64Slice creates a KeyValue instance with an INT64SLICE Value.
//
// If creating both a key and value at the same time, use the provided
// convenience function instead -- Int64Slice(name, value).
func (k Key) Int64Slice(v []int64) KeyValue {
return KeyValue{
Key: k,
Value: Int64SliceValue(v),
}
}
// Float64 creates a KeyValue instance with a FLOAT64 Value.
//
// If creating both a key and value at the same time, use the provided
@ -62,6 +95,17 @@ func (k Key) Float64(v float64) KeyValue {
}
}
// Float64Slice creates a KeyValue instance with a FLOAT64SLICE Value.
//
// If creating both a key and value at the same time, use the provided
// convenience function instead -- Float64(name, value).
func (k Key) Float64Slice(v []float64) KeyValue {
return KeyValue{
Key: k,
Value: Float64SliceValue(v),
}
}
// String creates a KeyValue instance with a STRING Value.
//
// If creating both a key and value at the same time, use the provided
@ -73,10 +117,23 @@ func (k Key) String(v string) KeyValue {
}
}
// StringSlice creates a KeyValue instance with a STRINGSLICE Value.
//
// If creating both a key and value at the same time, use the provided
// convenience function instead -- StringSlice(name, value).
func (k Key) StringSlice(v []string) KeyValue {
return KeyValue{
Key: k,
Value: StringSliceValue(v),
}
}
// Array creates a KeyValue instance with an ARRAY Value.
//
// If creating both a key and value at the same time, use the provided
// convenience function instead -- Array(name, value).
//
// Deprecated: Use the typed *Slice methods instead.
func (k Key) Array(v interface{}) KeyValue {
return KeyValue{
Key: k,

View File

@ -36,26 +36,51 @@ func Bool(k string, v bool) KeyValue {
return Key(k).Bool(v)
}
// BoolSlice creates a KeyValue with a BOOLSLICE Value type.
func BoolSlice(k string, v []bool) KeyValue {
return Key(k).BoolSlice(v)
}
// Int creates a KeyValue with an INT64 Value type.
func Int(k string, v int) KeyValue {
return Key(k).Int(v)
}
// Int64 creates a KeyValue with a INT64 Value type.
// IntSlice creates a KeyValue with an INT64SLICE Value type.
func IntSlice(k string, v []int) KeyValue {
return Key(k).IntSlice(v)
}
// Int64 creates a KeyValue with an INT64 Value type.
func Int64(k string, v int64) KeyValue {
return Key(k).Int64(v)
}
// Int64Slice creates a KeyValue with an INT64SLICE Value type.
func Int64Slice(k string, v []int64) KeyValue {
return Key(k).Int64Slice(v)
}
// Float64 creates a KeyValue with a FLOAT64 Value type.
func Float64(k string, v float64) KeyValue {
return Key(k).Float64(v)
}
// Float64Slice creates a KeyValue with a FLOAT64SLICE Value type.
func Float64Slice(k string, v []float64) KeyValue {
return Key(k).Float64Slice(v)
}
// String creates a KeyValue with a STRING Value type.
func String(k, v string) KeyValue {
return Key(k).String(v)
}
// StringSlice creates a KeyValue with a STRINGSLICE Value type.
func StringSlice(k string, v []string) KeyValue {
return Key(k).StringSlice(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 {
@ -64,6 +89,8 @@ func Stringer(k string, v fmt.Stringer) KeyValue {
// Array creates a new key-value pair with a passed name and a array.
// Only arrays of primitive type are supported.
//
// Deprecated: Use the typed *Slice functions instead.
func Array(k string, v interface{}) KeyValue {
return Key(k).Array(v)
}
@ -82,8 +109,24 @@ func Any(k string, value interface{}) KeyValue {
rv := reflect.ValueOf(value)
switch rv.Kind() {
case reflect.Array, reflect.Slice:
return Array(k, value)
case reflect.Array:
rv = rv.Slice(0, rv.Len())
fallthrough
case reflect.Slice:
switch reflect.TypeOf(value).Elem().Kind() {
case reflect.Bool:
return BoolSlice(k, rv.Interface().([]bool))
case reflect.Int:
return IntSlice(k, rv.Interface().([]int))
case reflect.Int64:
return Int64Slice(k, rv.Interface().([]int64))
case reflect.Float64:
return Float64Slice(k, rv.Interface().([]float64))
case reflect.String:
return StringSlice(k, rv.Interface().([]string))
default:
return KeyValue{Key: Key(k), Value: Value{vtype: INVALID}}
}
case reflect.Bool:
return Bool(k, rv.Bool())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:

View File

@ -13,12 +13,16 @@ func _() {
_ = x[INT64-2]
_ = x[FLOAT64-3]
_ = x[STRING-4]
_ = x[ARRAY-5]
_ = x[BOOLSLICE-5]
_ = x[INT64SLICE-6]
_ = x[FLOAT64SLICE-7]
_ = x[STRINGSLICE-8]
_ = x[ARRAY-9]
}
const _Type_name = "INVALIDBOOLINT64FLOAT64STRINGARRAY"
const _Type_name = "INVALIDBOOLINT64FLOAT64STRINGBOOLSLICEINT64SLICEFLOAT64SLICESTRINGSLICEARRAY"
var _Type_index = [...]uint8{0, 7, 11, 16, 23, 29, 34}
var _Type_index = [...]uint8{0, 7, 11, 16, 23, 29, 38, 48, 60, 71, 76}
func (i Type) String() string {
if i < 0 || i >= Type(len(_Type_index)-1) {

View File

@ -33,9 +33,7 @@ type Value struct {
vtype Type
numeric uint64
stringly string
// TODO Lazy value type?
array interface{}
slice interface{}
}
const (
@ -49,9 +47,19 @@ const (
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
// ARRAY is an array Type Value used to store 1-dimensional slices or
// arrays of bool, int, int32, int64, uint, uint32, uint64, float,
// float32, float64, or string types.
//
// Deprecated: Use slice types instead.
ARRAY
)
@ -63,11 +71,33 @@ func BoolValue(v bool) Value {
}
}
// BoolSliceValue creates a BOOLSLICE Value.
func BoolSliceValue(v []bool) Value {
cp := make([]bool, len(v))
copy(cp, v)
return Value{
vtype: BOOLSLICE,
slice: cp,
}
}
// IntValue creates an INT64 Value.
func IntValue(v int) Value {
return Int64Value(int64(v))
}
// IntSliceValue creates an INTSLICE Value.
func IntSliceValue(v []int) Value {
cp := make([]int64, 0, len(v))
for _, i := range v {
cp = append(cp, int64(i))
}
return Value{
vtype: INT64SLICE,
slice: cp,
}
}
// Int64Value creates an INT64 Value.
func Int64Value(v int64) Value {
return Value{
@ -76,6 +106,16 @@ func Int64Value(v int64) Value {
}
}
// Int64SliceValue creates an INT64SLICE Value.
func Int64SliceValue(v []int64) Value {
cp := make([]int64, len(v))
copy(cp, v)
return Value{
vtype: INT64SLICE,
slice: cp,
}
}
// Float64Value creates a FLOAT64 Value.
func Float64Value(v float64) Value {
return Value{
@ -84,6 +124,16 @@ func Float64Value(v float64) Value {
}
}
// Float64SliceValue creates a FLOAT64SLICE Value.
func Float64SliceValue(v []float64) Value {
cp := make([]float64, len(v))
copy(cp, v)
return Value{
vtype: FLOAT64SLICE,
slice: cp,
}
}
// StringValue creates a STRING Value.
func StringValue(v string) Value {
return Value{
@ -92,11 +142,23 @@ func StringValue(v string) Value {
}
}
// StringSliceValue creates a STRINGSLICE Value.
func StringSliceValue(v []string) Value {
cp := make([]string, len(v))
copy(cp, v)
return Value{
vtype: STRINGSLICE,
slice: cp,
}
}
// ArrayValue creates an ARRAY value from an array or slice.
// Only arrays or slices of bool, int, int64, float, float64, or string types are allowed.
// Specifically, arrays and slices can not contain other arrays, slices, structs, or non-standard
// types. If the passed value is not an array or slice of these types an
// INVALID value is returned.
//
// Deprecated: Use the typed *SliceValue functions instead.
func ArrayValue(v interface{}) Value {
switch reflect.TypeOf(v).Kind() {
case reflect.Array, reflect.Slice:
@ -112,7 +174,7 @@ func ArrayValue(v interface{}) Value {
reflect.Copy(frozen, val)
return Value{
vtype: ARRAY,
array: frozen.Interface(),
slice: frozen.Interface(),
}
default:
return Value{vtype: INVALID}
@ -132,27 +194,65 @@ func (v Value) AsBool() bool {
return internal.RawToBool(v.numeric)
}
// AsBoolSlice returns the []bool value. Make sure that the Value's type is
// BOOLSLICE.
func (v Value) AsBoolSlice() []bool {
if s, ok := v.slice.([]bool); ok {
return s
}
return nil
}
// AsInt64 returns the int64 value. Make sure that the Value's type is
// INT64.
func (v Value) AsInt64() int64 {
return internal.RawToInt64(v.numeric)
}
// AsInt64Slice returns the []int64 value. Make sure that the Value's type is
// INT64SLICE.
func (v Value) AsInt64Slice() []int64 {
if s, ok := v.slice.([]int64); ok {
return s
}
return nil
}
// AsFloat64 returns the float64 value. Make sure that the Value's
// type is FLOAT64.
func (v Value) AsFloat64() float64 {
return internal.RawToFloat64(v.numeric)
}
// AsFloat64Slice returns the []float64 value. Make sure that the Value's type is
// INT64SLICE.
func (v Value) AsFloat64Slice() []float64 {
if s, ok := v.slice.([]float64); ok {
return s
}
return nil
}
// AsString returns the string value. Make sure that the Value's type
// is STRING.
func (v Value) AsString() string {
return v.stringly
}
// AsStringSlice returns the []string value. Make sure that the Value's type is
// INT64SLICE.
func (v Value) AsStringSlice() []string {
if s, ok := v.slice.([]string); ok {
return s
}
return nil
}
// AsArray returns the array Value as an interface{}.
//
// Deprecated: Use the typed As*Slice functions instead.
func (v Value) AsArray() interface{} {
return v.array
return v.slice
}
type unknownValueType struct{}
@ -164,12 +264,20 @@ func (v Value) AsInterface() interface{} {
return v.AsArray()
case BOOL:
return v.AsBool()
case BOOLSLICE:
return v.AsBoolSlice()
case INT64:
return v.AsInt64()
case INT64SLICE:
return v.AsInt64Slice()
case FLOAT64:
return v.AsFloat64()
case FLOAT64SLICE:
return v.AsFloat64Slice()
case STRING:
return v.stringly
case STRINGSLICE:
return v.AsStringSlice()
}
return unknownValueType{}
}
@ -177,8 +285,8 @@ func (v Value) AsInterface() interface{} {
// Emit returns a string representation of Value's data.
func (v Value) Emit() string {
switch v.Type() {
case ARRAY:
return fmt.Sprint(v.array)
case ARRAY, BOOLSLICE, INT64SLICE, FLOAT64SLICE, STRINGSLICE:
return fmt.Sprint(v.slice)
case BOOL:
return strconv.FormatBool(v.AsBool())
case INT64:

View File

@ -25,7 +25,6 @@ import (
func TestValue(t *testing.T) {
k := attribute.Key("test")
bli := getBitlessInfo(42)
for _, testcase := range []struct {
name string
value attribute.Value
@ -38,6 +37,12 @@ func TestValue(t *testing.T) {
wantType: attribute.BOOL,
wantValue: true,
},
{
name: "Key.BoolSlice() correctly returns keys's internal []bool value",
value: k.BoolSlice([]bool{true, false, true}).Value,
wantType: attribute.BOOLSLICE,
wantValue: []bool{true, false, true},
},
{
name: "Key.Array([]bool) correctly return key's internal bool values",
value: k.Array([]bool{true, false}).Value,
@ -51,22 +56,10 @@ func TestValue(t *testing.T) {
wantValue: int64(42),
},
{
name: "Key.Float64() correctly returns keys's internal float64 value",
value: k.Float64(42.1).Value,
wantType: attribute.FLOAT64,
wantValue: 42.1,
},
{
name: "Key.String() correctly returns keys's internal string value",
value: k.String("foo").Value,
wantType: attribute.STRING,
wantValue: "foo",
},
{
name: "Key.Int() correctly returns keys's internal signed integral value",
value: k.Int(bli.intValue).Value,
wantType: bli.signedType,
wantValue: bli.signedValue,
name: "Key.Int64Slice() correctly returns keys's internal []int64 value",
value: k.Int64Slice([]int64{42, -3, 12}).Value,
wantType: attribute.INT64SLICE,
wantValue: []int64{42, -3, 12},
},
{
name: "Key.Array([]int64) correctly returns keys's internal int64 values",
@ -75,16 +68,16 @@ func TestValue(t *testing.T) {
wantValue: [2]int64{42, 43},
},
{
name: "Key.Array([]float64) correctly returns keys's internal float64 values",
value: k.Array([]float64{42, 43}).Value,
wantType: attribute.ARRAY,
wantValue: [2]float64{42, 43},
name: "Key.Int() correctly returns keys's internal signed integral value",
value: k.Int(42).Value,
wantType: attribute.INT64,
wantValue: int64(42),
},
{
name: "Key.Array([]string) correctly return key's internal string values",
value: k.Array([]string{"foo", "bar"}).Value,
wantType: attribute.ARRAY,
wantValue: [2]string{"foo", "bar"},
name: "Key.IntSlice() correctly returns keys's internal []int64 value",
value: k.IntSlice([]int{42, -3, 12}).Value,
wantType: attribute.INT64SLICE,
wantValue: []int64{42, -3, 12},
},
{
name: "Key.Array([]int) correctly returns keys's internal signed integral values",
@ -92,6 +85,42 @@ func TestValue(t *testing.T) {
wantType: attribute.ARRAY,
wantValue: [2]int{42, 43},
},
{
name: "Key.Float64() correctly returns keys's internal float64 value",
value: k.Float64(42.1).Value,
wantType: attribute.FLOAT64,
wantValue: 42.1,
},
{
name: "Key.Float64Slice() correctly returns keys's internal []float64 value",
value: k.Float64Slice([]float64{42, -3, 12}).Value,
wantType: attribute.FLOAT64SLICE,
wantValue: []float64{42, -3, 12},
},
{
name: "Key.Array([]float64) correctly returns keys's internal float64 values",
value: k.Array([]float64{42, 43}).Value,
wantType: attribute.ARRAY,
wantValue: [2]float64{42, 43},
},
{
name: "Key.String() correctly returns keys's internal string value",
value: k.String("foo").Value,
wantType: attribute.STRING,
wantValue: "foo",
},
{
name: "Key.StringSlice() correctly returns keys's internal []string value",
value: k.StringSlice([]string{"forty-two", "negative three", "twelve"}).Value,
wantType: attribute.STRINGSLICE,
wantValue: []string{"forty-two", "negative three", "twelve"},
},
{
name: "Key.Array([]string) correctly return key's internal string values",
value: k.Array([]string{"foo", "bar"}).Value,
wantType: attribute.ARRAY,
wantValue: [2]string{"foo", "bar"},
},
{
name: "Key.Array([][]int) refuses to create multi-dimensional array",
value: k.Array([][]int{{1, 2}, {3, 4}}).Value,
@ -113,22 +142,6 @@ func TestValue(t *testing.T) {
}
}
type bitlessInfo struct {
intValue int
uintValue uint
signedType attribute.Type
signedValue interface{}
}
func getBitlessInfo(i int) bitlessInfo {
return bitlessInfo{
intValue: i,
uintValue: uint(i),
signedType: attribute.INT64,
signedValue: int64(i),
}
}
func TestAsArrayValue(t *testing.T) {
v := attribute.ArrayValue([]int{1, 2, 3}).AsArray()
// Ensure the returned dynamic type is stable.

View File

@ -248,8 +248,11 @@ func keyValueToTag(keyValue attribute.KeyValue) *gen.Tag {
VDouble: &f,
VType: gen.TagType_DOUBLE,
}
case attribute.ARRAY:
json, _ := json.Marshal(keyValue.Value.AsArray())
case attribute.BOOLSLICE,
attribute.INT64SLICE,
attribute.FLOAT64SLICE,
attribute.STRINGSLICE:
json, _ := json.Marshal(keyValue.Value.AsInterface())
a := (string)(json)
tag = &gen.Tag{
Key: string(keyValue.Key),

View File

@ -323,7 +323,7 @@ func Test_spanSnapshotToThrift(t *testing.T) {
StartTime: now,
EndTime: now,
Attributes: []attribute.KeyValue{
attribute.Array("arr", []int{0, 1, 2, 3}),
attribute.IntSlice("arr", []int{0, 1, 2, 3}),
},
Status: sdktrace.Status{
Code: codes.Unset,

View File

@ -15,8 +15,6 @@
package metrictransform
import (
"reflect"
"go.opentelemetry.io/otel/attribute"
commonpb "go.opentelemetry.io/proto/otlp/common/v1"
@ -68,22 +66,40 @@ func Value(v attribute.Value) *commonpb.AnyValue {
av.Value = &commonpb.AnyValue_BoolValue{
BoolValue: v.AsBool(),
}
case attribute.BOOLSLICE:
av.Value = &commonpb.AnyValue_ArrayValue{
ArrayValue: &commonpb.ArrayValue{
Values: boolSliceValues(v.AsBoolSlice()),
},
}
case attribute.INT64:
av.Value = &commonpb.AnyValue_IntValue{
IntValue: v.AsInt64(),
}
case attribute.INT64SLICE:
av.Value = &commonpb.AnyValue_ArrayValue{
ArrayValue: &commonpb.ArrayValue{
Values: int64SliceValues(v.AsInt64Slice()),
},
}
case attribute.FLOAT64:
av.Value = &commonpb.AnyValue_DoubleValue{
DoubleValue: v.AsFloat64(),
}
case attribute.FLOAT64SLICE:
av.Value = &commonpb.AnyValue_ArrayValue{
ArrayValue: &commonpb.ArrayValue{
Values: float64SliceValues(v.AsFloat64Slice()),
},
}
case attribute.STRING:
av.Value = &commonpb.AnyValue_StringValue{
StringValue: v.AsString(),
}
case attribute.ARRAY:
case attribute.STRINGSLICE:
av.Value = &commonpb.AnyValue_ArrayValue{
ArrayValue: &commonpb.ArrayValue{
Values: arrayValues(v),
Values: stringSliceValues(v.AsStringSlice()),
},
}
default:
@ -94,56 +110,50 @@ func Value(v attribute.Value) *commonpb.AnyValue {
return av
}
func arrayValues(v attribute.Value) []*commonpb.AnyValue {
a := v.AsArray()
aType := reflect.TypeOf(a)
var valueFunc func(reflect.Value) *commonpb.AnyValue
switch aType.Elem().Kind() {
case reflect.Bool:
valueFunc = func(v reflect.Value) *commonpb.AnyValue {
return &commonpb.AnyValue{
func boolSliceValues(vals []bool) []*commonpb.AnyValue {
converted := make([]*commonpb.AnyValue, len(vals))
for i, v := range vals {
converted[i] = &commonpb.AnyValue{
Value: &commonpb.AnyValue_BoolValue{
BoolValue: v.Bool(),
BoolValue: v,
},
}
}
case reflect.Int, reflect.Int64:
valueFunc = func(v reflect.Value) *commonpb.AnyValue {
return &commonpb.AnyValue{
Value: &commonpb.AnyValue_IntValue{
IntValue: v.Int(),
},
}
}
case reflect.Uintptr:
valueFunc = func(v reflect.Value) *commonpb.AnyValue {
return &commonpb.AnyValue{
Value: &commonpb.AnyValue_IntValue{
IntValue: int64(v.Uint()),
},
}
}
case reflect.Float64:
valueFunc = func(v reflect.Value) *commonpb.AnyValue {
return &commonpb.AnyValue{
Value: &commonpb.AnyValue_DoubleValue{
DoubleValue: v.Float(),
},
}
}
case reflect.String:
valueFunc = func(v reflect.Value) *commonpb.AnyValue {
return &commonpb.AnyValue{
Value: &commonpb.AnyValue_StringValue{
StringValue: v.String(),
},
}
}
}
results := make([]*commonpb.AnyValue, aType.Len())
for i, aValue := 0, reflect.ValueOf(a); i < aValue.Len(); i++ {
results[i] = valueFunc(aValue.Index(i))
}
return results
return converted
}
func int64SliceValues(vals []int64) []*commonpb.AnyValue {
converted := make([]*commonpb.AnyValue, len(vals))
for i, v := range vals {
converted[i] = &commonpb.AnyValue{
Value: &commonpb.AnyValue_IntValue{
IntValue: v,
},
}
}
return converted
}
func float64SliceValues(vals []float64) []*commonpb.AnyValue {
converted := make([]*commonpb.AnyValue, len(vals))
for i, v := range vals {
converted[i] = &commonpb.AnyValue{
Value: &commonpb.AnyValue_DoubleValue{
DoubleValue: v,
},
}
}
return converted
}
func stringSliceValues(vals []string) []*commonpb.AnyValue {
converted := make([]*commonpb.AnyValue, len(vals))
for i, v := range vals {
converted[i] = &commonpb.AnyValue{
Value: &commonpb.AnyValue_StringValue{
StringValue: v,
},
}
}
return converted
}

View File

@ -112,7 +112,10 @@ func TestArrayAttributes(t *testing.T) {
{nil, nil},
{
[]attribute.KeyValue{
attribute.Array("invalid", [][]string{{"1", "2"}, {"a"}}),
{
Key: attribute.Key("invalid"),
Value: attribute.Value{},
},
},
[]*commonpb.KeyValue{
{
@ -127,18 +130,18 @@ func TestArrayAttributes(t *testing.T) {
},
{
[]attribute.KeyValue{
attribute.Array("bool array to bool array", []bool{true, false}),
attribute.Array("int array to int64 array", []int{1, 2, 3}),
attribute.Array("int64 array to int64 array", []int64{1, 2, 3}),
attribute.Array("float64 array to double array", []float64{1.11, 2.22, 3.33}),
attribute.Array("string array to string array", []string{"foo", "bar", "baz"}),
attribute.BoolSlice("bool slice to bool array", []bool{true, false}),
attribute.IntSlice("int slice to int64 array", []int{1, 2, 3}),
attribute.Int64Slice("int64 slice to int64 array", []int64{1, 2, 3}),
attribute.Float64Slice("float64 slice to double array", []float64{1.11, 2.22, 3.33}),
attribute.StringSlice("string slice to string array", []string{"foo", "bar", "baz"}),
},
[]*commonpb.KeyValue{
newOTelBoolArray("bool array to bool array", []bool{true, false}),
newOTelIntArray("int array to int64 array", []int64{1, 2, 3}),
newOTelIntArray("int64 array to int64 array", []int64{1, 2, 3}),
newOTelDoubleArray("float64 array to double array", []float64{1.11, 2.22, 3.33}),
newOTelStringArray("string array to string array", []string{"foo", "bar", "baz"}),
newOTelBoolArray("bool slice to bool array", []bool{true, false}),
newOTelIntArray("int slice to int64 array", []int64{1, 2, 3}),
newOTelIntArray("int64 slice to int64 array", []int64{1, 2, 3}),
newOTelDoubleArray("float64 slice to double array", []float64{1.11, 2.22, 3.33}),
newOTelStringArray("string slice to string array", []string{"foo", "bar", "baz"}),
},
},
} {

View File

@ -15,8 +15,6 @@
package tracetransform
import (
"reflect"
"go.opentelemetry.io/otel/attribute"
commonpb "go.opentelemetry.io/proto/otlp/common/v1"
@ -68,22 +66,40 @@ func Value(v attribute.Value) *commonpb.AnyValue {
av.Value = &commonpb.AnyValue_BoolValue{
BoolValue: v.AsBool(),
}
case attribute.BOOLSLICE:
av.Value = &commonpb.AnyValue_ArrayValue{
ArrayValue: &commonpb.ArrayValue{
Values: boolSliceValues(v.AsBoolSlice()),
},
}
case attribute.INT64:
av.Value = &commonpb.AnyValue_IntValue{
IntValue: v.AsInt64(),
}
case attribute.INT64SLICE:
av.Value = &commonpb.AnyValue_ArrayValue{
ArrayValue: &commonpb.ArrayValue{
Values: int64SliceValues(v.AsInt64Slice()),
},
}
case attribute.FLOAT64:
av.Value = &commonpb.AnyValue_DoubleValue{
DoubleValue: v.AsFloat64(),
}
case attribute.FLOAT64SLICE:
av.Value = &commonpb.AnyValue_ArrayValue{
ArrayValue: &commonpb.ArrayValue{
Values: float64SliceValues(v.AsFloat64Slice()),
},
}
case attribute.STRING:
av.Value = &commonpb.AnyValue_StringValue{
StringValue: v.AsString(),
}
case attribute.ARRAY:
case attribute.STRINGSLICE:
av.Value = &commonpb.AnyValue_ArrayValue{
ArrayValue: &commonpb.ArrayValue{
Values: arrayValues(v),
Values: stringSliceValues(v.AsStringSlice()),
},
}
default:
@ -94,56 +110,50 @@ func Value(v attribute.Value) *commonpb.AnyValue {
return av
}
func arrayValues(v attribute.Value) []*commonpb.AnyValue {
a := v.AsArray()
aType := reflect.TypeOf(a)
var valueFunc func(reflect.Value) *commonpb.AnyValue
switch aType.Elem().Kind() {
case reflect.Bool:
valueFunc = func(v reflect.Value) *commonpb.AnyValue {
return &commonpb.AnyValue{
func boolSliceValues(vals []bool) []*commonpb.AnyValue {
converted := make([]*commonpb.AnyValue, len(vals))
for i, v := range vals {
converted[i] = &commonpb.AnyValue{
Value: &commonpb.AnyValue_BoolValue{
BoolValue: v.Bool(),
BoolValue: v,
},
}
}
case reflect.Int, reflect.Int64:
valueFunc = func(v reflect.Value) *commonpb.AnyValue {
return &commonpb.AnyValue{
Value: &commonpb.AnyValue_IntValue{
IntValue: v.Int(),
},
}
}
case reflect.Uintptr:
valueFunc = func(v reflect.Value) *commonpb.AnyValue {
return &commonpb.AnyValue{
Value: &commonpb.AnyValue_IntValue{
IntValue: int64(v.Uint()),
},
}
}
case reflect.Float64:
valueFunc = func(v reflect.Value) *commonpb.AnyValue {
return &commonpb.AnyValue{
Value: &commonpb.AnyValue_DoubleValue{
DoubleValue: v.Float(),
},
}
}
case reflect.String:
valueFunc = func(v reflect.Value) *commonpb.AnyValue {
return &commonpb.AnyValue{
Value: &commonpb.AnyValue_StringValue{
StringValue: v.String(),
},
}
}
}
results := make([]*commonpb.AnyValue, aType.Len())
for i, aValue := 0, reflect.ValueOf(a); i < aValue.Len(); i++ {
results[i] = valueFunc(aValue.Index(i))
}
return results
return converted
}
func int64SliceValues(vals []int64) []*commonpb.AnyValue {
converted := make([]*commonpb.AnyValue, len(vals))
for i, v := range vals {
converted[i] = &commonpb.AnyValue{
Value: &commonpb.AnyValue_IntValue{
IntValue: v,
},
}
}
return converted
}
func float64SliceValues(vals []float64) []*commonpb.AnyValue {
converted := make([]*commonpb.AnyValue, len(vals))
for i, v := range vals {
converted[i] = &commonpb.AnyValue{
Value: &commonpb.AnyValue_DoubleValue{
DoubleValue: v,
},
}
}
return converted
}
func stringSliceValues(vals []string) []*commonpb.AnyValue {
converted := make([]*commonpb.AnyValue, len(vals))
for i, v := range vals {
converted[i] = &commonpb.AnyValue{
Value: &commonpb.AnyValue_StringValue{
StringValue: v,
},
}
}
return converted
}

View File

@ -112,7 +112,10 @@ func TestArrayAttributes(t *testing.T) {
{nil, nil},
{
[]attribute.KeyValue{
attribute.Array("invalid", [][]string{{"1", "2"}, {"a"}}),
{
Key: attribute.Key("invalid"),
Value: attribute.Value{},
},
},
[]*commonpb.KeyValue{
{
@ -127,18 +130,18 @@ func TestArrayAttributes(t *testing.T) {
},
{
[]attribute.KeyValue{
attribute.Array("bool array to bool array", []bool{true, false}),
attribute.Array("int array to int64 array", []int{1, 2, 3}),
attribute.Array("int64 array to int64 array", []int64{1, 2, 3}),
attribute.Array("float64 array to double array", []float64{1.11, 2.22, 3.33}),
attribute.Array("string array to string array", []string{"foo", "bar", "baz"}),
attribute.BoolSlice("bool slice to bool array", []bool{true, false}),
attribute.IntSlice("int slice to int64 array", []int{1, 2, 3}),
attribute.Int64Slice("int64 slice to int64 array", []int64{1, 2, 3}),
attribute.Float64Slice("float64 slice to double array", []float64{1.11, 2.22, 3.33}),
attribute.StringSlice("string slice to string array", []string{"foo", "bar", "baz"}),
},
[]*commonpb.KeyValue{
newOTelBoolArray("bool array to bool array", []bool{true, false}),
newOTelIntArray("int array to int64 array", []int64{1, 2, 3}),
newOTelIntArray("int64 array to int64 array", []int64{1, 2, 3}),
newOTelDoubleArray("float64 array to double array", []float64{1.11, 2.22, 3.33}),
newOTelStringArray("string array to string array", []string{"foo", "bar", "baz"}),
newOTelBoolArray("bool slice to bool array", []bool{true, false}),
newOTelIntArray("int slice to int64 array", []int64{1, 2, 3}),
newOTelIntArray("int64 slice to int64 array", []int64{1, 2, 3}),
newOTelDoubleArray("float64 slice to double array", []float64{1.11, 2.22, 3.33}),
newOTelStringArray("string slice to string array", []string{"foo", "bar", "baz"}),
},
},
} {

View File

@ -181,9 +181,18 @@ func toZipkinTags(data tracesdk.ReadOnlySpan) map[string]string {
m := make(map[string]string, len(attr)+len(extraZipkinTags))
for _, kv := range attr {
switch kv.Value.Type() {
// For array attributes, serialize as JSON list string.
case attribute.ARRAY:
json, _ := json.Marshal(kv.Value.AsArray())
// For slice attributes, serialize as JSON list string.
case attribute.BOOLSLICE:
json, _ := json.Marshal(kv.Value.AsBoolSlice())
m[(string)(kv.Key)] = (string)(json)
case attribute.INT64SLICE:
json, _ := json.Marshal(kv.Value.AsInt64Slice())
m[(string)(kv.Key)] = (string)(json)
case attribute.FLOAT64SLICE:
json, _ := json.Marshal(kv.Value.AsFloat64Slice())
m[(string)(kv.Key)] = (string)(json)
case attribute.STRINGSLICE:
json, _ := json.Marshal(kv.Value.AsStringSlice())
m[(string)(kv.Key)] = (string)(json)
default:
m[(string)(kv.Key)] = kv.Value.Emit()

View File

@ -59,7 +59,7 @@ func TestModelConversion(t *testing.T) {
Attributes: []attribute.KeyValue{
attribute.Int64("attr1", 42),
attribute.String("attr2", "bar"),
attribute.Array("attr3", []int{0, 1, 2}),
attribute.IntSlice("attr3", []int{0, 1, 2}),
},
Events: []tracesdk.Event{
{

View File

@ -33,9 +33,8 @@ func TestTraceStateFromKeyValues(t *testing.T) {
attribute.Bool("key1", true),
attribute.Int64("key2", 1),
attribute.Float64("key3", 1.1),
attribute.Array("key4", []int{1, 1}),
)
require.NoError(t, err)
expected := "key0=string,key1=true,key2=1,key3=1.1,key4=[1 1]"
expected := "key0=string,key1=true,key2=1,key3=1.1"
assert.Equal(t, expected, ts.String())
}

View File

@ -138,7 +138,7 @@ func (processExecutablePathDetector) Detect(ctx context.Context) (*Resource, err
// Detect returns a *Resource that describes all the command arguments as received
// by the process.
func (processCommandArgsDetector) Detect(ctx context.Context) (*Resource, error) {
return NewWithAttributes(semconv.SchemaURL, semconv.ProcessCommandArgsKey.Array(commandArgs())), nil
return NewWithAttributes(semconv.SchemaURL, semconv.ProcessCommandArgsKey.StringSlice(commandArgs())), nil
}
// Detect returns a *Resource that describes the username of the user that owns the