1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2026-06-19 21:45:50 +02:00

exporters: support MAP attributes (#8453)

Fixes https://github.com/open-telemetry/opentelemetry-go/issues/8163

Add end-to-end exporter handling for `attribute.SLICE` in the remaining
paths that still treated it as invalid or relied on fallback formatting.

Changes:

- encode `attribute.MAP` as OTLP `AnyValue_AnyValue_KvlistValue` for
trace, log, and metric transforms
- serialize Zipkin `MAP` attributes using the non-OTLP AnyValue string
representation
This commit is contained in:
Robert Pająk
2026-06-16 21:25:29 +02:00
committed by GitHub
parent e7087816b8
commit dcfc16cebd
17 changed files with 504 additions and 8 deletions
+4
View File
@@ -11,6 +11,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Added
- Add `Map` and `MapValue` functions for new `MAP` attribute type in `go.opentelemetry.io/otel/attribute`. (#8445)
- Support `MAP` attributes in `go.opentelemetry.io/otel/exporters/otlp/otlptrace`. (#8453)
- Support `MAP` attributes in `go.opentelemetry.io/otel/exporters/otlp/otlplog`. (#8453)
- Support `MAP` attributes in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric`. (#8453)
- Support `MAP` attributes in `go.opentelemetry.io/otel/exporters/zipkin`. (#8453)
- Apply `AttributeValueLengthLimit` to `attribute.MAP` type attribute values in `go.opentelemetry.io/otel/sdk/trace`, recursively truncating contained values. (#8454)
- Add `WithUnsafeAttributes` to `go.opentelemetry.io/otel/metric/x` as an experimental no-copy attribute option intended for future performance work. This is a work in progress. (#8251)
@@ -33,6 +33,18 @@ var (
attribute.ByteSliceValue([]byte("otlp")),
attribute.SliceValue(attribute.IntValue(2), attribute.Value{}),
)
attrMap = attribute.Map("map",
attribute.String("string", "o"),
attribute.Int("number", 2),
attribute.ByteSlice("bytes", []byte("otlp")),
attribute.Slice(
"slice",
attribute.BoolValue(true),
attribute.MapValue(attribute.String("inner", "value")),
),
attribute.Map("nested", attribute.Bool("ok", true)),
attribute.KeyValue{Key: "empty"},
)
attrStringSlice = attribute.StringSlice("string slice", []string{"o", "n"})
attrEmpty = attribute.KeyValue{
Key: attribute.Key("empty"),
@@ -61,7 +73,10 @@ var (
Values: []*cpb.AnyValue{valDblNOne, valDblOne},
},
}}
valStrO = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "o"}}
valStrO = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "o"}}
valStrValue = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{
StringValue: "value",
}}
valAttrBytes = &cpb.AnyValue{Value: &cpb.AnyValue_BytesValue{BytesValue: []byte("otlp")}}
valSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -76,6 +91,37 @@ var (
},
},
}}
valAttrMap = &cpb.AnyValue{Value: &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: []*cpb.KeyValue{
{Key: "bytes", Value: valAttrBytes},
{Key: "empty", Value: &cpb.AnyValue{}},
{Key: "nested", Value: &cpb.AnyValue{Value: &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: []*cpb.KeyValue{
{Key: "ok", Value: valBoolTrue},
},
},
}}},
{Key: "number", Value: valIntTwo},
{Key: "slice", Value: &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
Values: []*cpb.AnyValue{
valBoolTrue,
{Value: &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: []*cpb.KeyValue{
{Key: "inner", Value: valStrValue},
},
},
}},
},
},
}}},
{Key: "string", Value: valStrO},
},
},
}}
valStrN = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "n"}}
valStrSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -94,6 +140,7 @@ var (
kvString = &cpb.KeyValue{Key: "string", Value: valStrO}
kvAttrBytes = &cpb.KeyValue{Key: "bytes", Value: valAttrBytes}
kvAttrSlice = &cpb.KeyValue{Key: "slice", Value: valSlice}
kvAttrMap = &cpb.KeyValue{Key: "map", Value: valAttrMap}
kvStringSlice = &cpb.KeyValue{Key: "string slice", Value: valStrSlice}
kvEmpty = &cpb.KeyValue{Key: "empty", Value: &cpb.AnyValue{}}
)
@@ -168,6 +215,11 @@ func TestAttrTransforms(t *testing.T) {
[]attribute.KeyValue{attrSlice},
[]*cpb.KeyValue{kvAttrSlice},
},
{
"map",
[]attribute.KeyValue{attrMap},
[]*cpb.KeyValue{kvAttrMap},
},
{
"string slice",
[]attribute.KeyValue{attrStringSlice},
@@ -187,6 +239,7 @@ func TestAttrTransforms(t *testing.T) {
attrString,
attrBytes,
attrSlice,
attrMap,
attrStringSlice,
attrEmpty,
},
@@ -202,6 +255,7 @@ func TestAttrTransforms(t *testing.T) {
kvString,
kvAttrBytes,
kvAttrSlice,
kvAttrMap,
kvStringSlice,
kvEmpty,
},
@@ -205,6 +205,12 @@ func AttrValue(v attribute.Value) *cpb.AnyValue {
Values: attrValues(v.AsSlice()),
},
}
case attribute.MAP:
av.Value = &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: Attrs(v.AsMap()),
},
}
case attribute.STRINGSLICE:
av.Value = &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -33,6 +33,18 @@ var (
attribute.ByteSliceValue([]byte("otlp")),
attribute.SliceValue(attribute.IntValue(2), attribute.Value{}),
)
attrMap = attribute.Map("map",
attribute.String("string", "o"),
attribute.Int("number", 2),
attribute.ByteSlice("bytes", []byte("otlp")),
attribute.Slice(
"slice",
attribute.BoolValue(true),
attribute.MapValue(attribute.String("inner", "value")),
),
attribute.Map("nested", attribute.Bool("ok", true)),
attribute.KeyValue{Key: "empty"},
)
attrStringSlice = attribute.StringSlice("string slice", []string{"o", "n"})
attrEmpty = attribute.KeyValue{
Key: attribute.Key("empty"),
@@ -61,7 +73,10 @@ var (
Values: []*cpb.AnyValue{valDblNOne, valDblOne},
},
}}
valStrO = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "o"}}
valStrO = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "o"}}
valStrValue = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{
StringValue: "value",
}}
valAttrBytes = &cpb.AnyValue{Value: &cpb.AnyValue_BytesValue{BytesValue: []byte("otlp")}}
valSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -76,6 +91,37 @@ var (
},
},
}}
valAttrMap = &cpb.AnyValue{Value: &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: []*cpb.KeyValue{
{Key: "bytes", Value: valAttrBytes},
{Key: "empty", Value: &cpb.AnyValue{}},
{Key: "nested", Value: &cpb.AnyValue{Value: &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: []*cpb.KeyValue{
{Key: "ok", Value: valBoolTrue},
},
},
}}},
{Key: "number", Value: valIntTwo},
{Key: "slice", Value: &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
Values: []*cpb.AnyValue{
valBoolTrue,
{Value: &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: []*cpb.KeyValue{
{Key: "inner", Value: valStrValue},
},
},
}},
},
},
}}},
{Key: "string", Value: valStrO},
},
},
}}
valStrN = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "n"}}
valStrSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -94,6 +140,7 @@ var (
kvString = &cpb.KeyValue{Key: "string", Value: valStrO}
kvAttrBytes = &cpb.KeyValue{Key: "bytes", Value: valAttrBytes}
kvAttrSlice = &cpb.KeyValue{Key: "slice", Value: valSlice}
kvAttrMap = &cpb.KeyValue{Key: "map", Value: valAttrMap}
kvStringSlice = &cpb.KeyValue{Key: "string slice", Value: valStrSlice}
kvEmpty = &cpb.KeyValue{Key: "empty", Value: &cpb.AnyValue{}}
)
@@ -168,6 +215,11 @@ func TestAttrTransforms(t *testing.T) {
[]attribute.KeyValue{attrSlice},
[]*cpb.KeyValue{kvAttrSlice},
},
{
"map",
[]attribute.KeyValue{attrMap},
[]*cpb.KeyValue{kvAttrMap},
},
{
"string slice",
[]attribute.KeyValue{attrStringSlice},
@@ -187,6 +239,7 @@ func TestAttrTransforms(t *testing.T) {
attrString,
attrBytes,
attrSlice,
attrMap,
attrStringSlice,
attrEmpty,
},
@@ -202,6 +255,7 @@ func TestAttrTransforms(t *testing.T) {
kvString,
kvAttrBytes,
kvAttrSlice,
kvAttrMap,
kvStringSlice,
kvEmpty,
},
@@ -205,6 +205,12 @@ func AttrValue(v attribute.Value) *cpb.AnyValue {
Values: attrValues(v.AsSlice()),
},
}
case attribute.MAP:
av.Value = &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: Attrs(v.AsMap()),
},
}
case attribute.STRINGSLICE:
av.Value = &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -91,6 +91,12 @@ func Value(v attribute.Value) *cpb.AnyValue {
Values: attrValues(v.AsSlice()),
},
}
case attribute.MAP:
av.Value = &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: KeyValues(v.AsMap()),
},
}
case attribute.STRINGSLICE:
av.Value = &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -33,6 +33,18 @@ var (
attribute.ByteSliceValue([]byte("otlp")),
attribute.SliceValue(attribute.IntValue(2), attribute.Value{}),
)
attrMap = attribute.Map("map",
attribute.String("string", "o"),
attribute.Int("number", 2),
attribute.ByteSlice("bytes", []byte("otlp")),
attribute.Slice(
"slice",
attribute.BoolValue(true),
attribute.MapValue(attribute.String("inner", "value")),
),
attribute.Map("nested", attribute.Bool("ok", true)),
attribute.KeyValue{Key: "empty"},
)
attrStringSlice = attribute.StringSlice("string slice", []string{"o", "n"})
attrEmpty = attribute.KeyValue{
Key: attribute.Key("empty"),
@@ -61,7 +73,10 @@ var (
Values: []*cpb.AnyValue{valDblNOne, valDblOne},
},
}}
valStrO = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "o"}}
valStrO = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "o"}}
valStrValue = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{
StringValue: "value",
}}
valBytes = &cpb.AnyValue{Value: &cpb.AnyValue_BytesValue{BytesValue: []byte("otlp")}}
valSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -76,6 +91,37 @@ var (
},
},
}}
valMap = &cpb.AnyValue{Value: &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: []*cpb.KeyValue{
{Key: "bytes", Value: valBytes},
{Key: "empty", Value: &cpb.AnyValue{}},
{Key: "nested", Value: &cpb.AnyValue{Value: &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: []*cpb.KeyValue{
{Key: "ok", Value: valBoolTrue},
},
},
}}},
{Key: "number", Value: valIntTwo},
{Key: "slice", Value: &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
Values: []*cpb.AnyValue{
valBoolTrue,
{Value: &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: []*cpb.KeyValue{
{Key: "inner", Value: valStrValue},
},
},
}},
},
},
}}},
{Key: "string", Value: valStrO},
},
},
}}
valStrN = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "n"}}
valStrSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -94,6 +140,7 @@ var (
kvString = &cpb.KeyValue{Key: "string", Value: valStrO}
kvBytes = &cpb.KeyValue{Key: "bytes", Value: valBytes}
kvSlice = &cpb.KeyValue{Key: "slice", Value: valSlice}
kvMap = &cpb.KeyValue{Key: "map", Value: valMap}
kvStringSlice = &cpb.KeyValue{Key: "string slice", Value: valStrSlice}
kvEmpty = &cpb.KeyValue{Key: "empty", Value: &cpb.AnyValue{}}
)
@@ -168,6 +215,11 @@ func TestAttributeTransforms(t *testing.T) {
[]attribute.KeyValue{attrSlice},
[]*cpb.KeyValue{kvSlice},
},
{
"map",
[]attribute.KeyValue{attrMap},
[]*cpb.KeyValue{kvMap},
},
{
"string slice",
[]attribute.KeyValue{attrStringSlice},
@@ -187,6 +239,7 @@ func TestAttributeTransforms(t *testing.T) {
attrString,
attrBytes,
attrSlice,
attrMap,
attrStringSlice,
attrEmpty,
},
@@ -202,6 +255,7 @@ func TestAttributeTransforms(t *testing.T) {
kvString,
kvBytes,
kvSlice,
kvMap,
kvStringSlice,
kvEmpty,
},
@@ -91,6 +91,12 @@ func Value(v attribute.Value) *cpb.AnyValue {
Values: attrValues(v.AsSlice()),
},
}
case attribute.MAP:
av.Value = &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: KeyValues(v.AsMap()),
},
}
case attribute.STRINGSLICE:
av.Value = &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -33,6 +33,18 @@ var (
attribute.ByteSliceValue([]byte("otlp")),
attribute.SliceValue(attribute.IntValue(2), attribute.Value{}),
)
attrMap = attribute.Map("map",
attribute.String("string", "o"),
attribute.Int("number", 2),
attribute.ByteSlice("bytes", []byte("otlp")),
attribute.Slice(
"slice",
attribute.BoolValue(true),
attribute.MapValue(attribute.String("inner", "value")),
),
attribute.Map("nested", attribute.Bool("ok", true)),
attribute.KeyValue{Key: "empty"},
)
attrStringSlice = attribute.StringSlice("string slice", []string{"o", "n"})
attrEmpty = attribute.KeyValue{
Key: attribute.Key("empty"),
@@ -61,7 +73,10 @@ var (
Values: []*cpb.AnyValue{valDblNOne, valDblOne},
},
}}
valStrO = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "o"}}
valStrO = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "o"}}
valStrValue = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{
StringValue: "value",
}}
valBytes = &cpb.AnyValue{Value: &cpb.AnyValue_BytesValue{BytesValue: []byte("otlp")}}
valSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -76,6 +91,37 @@ var (
},
},
}}
valMap = &cpb.AnyValue{Value: &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: []*cpb.KeyValue{
{Key: "bytes", Value: valBytes},
{Key: "empty", Value: &cpb.AnyValue{}},
{Key: "nested", Value: &cpb.AnyValue{Value: &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: []*cpb.KeyValue{
{Key: "ok", Value: valBoolTrue},
},
},
}}},
{Key: "number", Value: valIntTwo},
{Key: "slice", Value: &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
Values: []*cpb.AnyValue{
valBoolTrue,
{Value: &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: []*cpb.KeyValue{
{Key: "inner", Value: valStrValue},
},
},
}},
},
},
}}},
{Key: "string", Value: valStrO},
},
},
}}
valStrN = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "n"}}
valStrSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -94,6 +140,7 @@ var (
kvString = &cpb.KeyValue{Key: "string", Value: valStrO}
kvBytes = &cpb.KeyValue{Key: "bytes", Value: valBytes}
kvSlice = &cpb.KeyValue{Key: "slice", Value: valSlice}
kvMap = &cpb.KeyValue{Key: "map", Value: valMap}
kvStringSlice = &cpb.KeyValue{Key: "string slice", Value: valStrSlice}
kvEmpty = &cpb.KeyValue{Key: "empty", Value: &cpb.AnyValue{}}
)
@@ -168,6 +215,11 @@ func TestAttributeTransforms(t *testing.T) {
[]attribute.KeyValue{attrSlice},
[]*cpb.KeyValue{kvSlice},
},
{
"map",
[]attribute.KeyValue{attrMap},
[]*cpb.KeyValue{kvMap},
},
{
"string slice",
[]attribute.KeyValue{attrStringSlice},
@@ -187,6 +239,7 @@ func TestAttributeTransforms(t *testing.T) {
attrString,
attrBytes,
attrSlice,
attrMap,
attrStringSlice,
attrEmpty,
},
@@ -202,6 +255,7 @@ func TestAttributeTransforms(t *testing.T) {
kvString,
kvBytes,
kvSlice,
kvMap,
kvStringSlice,
kvEmpty,
},
@@ -97,6 +97,12 @@ func Value(v attribute.Value) *commonpb.AnyValue {
Values: values(v.AsSlice()),
},
}
case attribute.MAP:
av.Value = &commonpb.AnyValue_KvlistValue{
KvlistValue: &commonpb.KeyValueList{
Values: KeyValues(v.AsMap()),
},
}
case attribute.STRINGSLICE:
av.Value = &commonpb.AnyValue_ArrayValue{
ArrayValue: &commonpb.ArrayValue{
@@ -33,6 +33,19 @@ func TestAttributes(t *testing.T) {
attribute.ByteSliceValue([]byte("bytes")),
attribute.SliceValue(attribute.IntValue(2), attribute.Value{}),
),
attribute.Map(
"map to kvlist",
attribute.String("string", "string"),
attribute.Int("number", 2),
attribute.ByteSlice("bytes", []byte("bytes")),
attribute.Slice(
"slice",
attribute.BoolValue(true),
attribute.MapValue(attribute.String("inner", "value")),
),
attribute.Map("nested", attribute.Bool("ok", true)),
attribute.KeyValue{Key: "empty"},
),
attribute.Bool("bool to bool", true),
{Key: "empty to empty"},
},
@@ -112,6 +125,96 @@ func TestAttributes(t *testing.T) {
},
},
},
{
Key: "map to kvlist",
Value: &commonpb.AnyValue{
Value: &commonpb.AnyValue_KvlistValue{
KvlistValue: &commonpb.KeyValueList{
Values: []*commonpb.KeyValue{
{
Key: "bytes",
Value: &commonpb.AnyValue{
Value: &commonpb.AnyValue_BytesValue{
BytesValue: []byte("bytes"),
},
},
},
{
Key: "empty",
Value: &commonpb.AnyValue{},
},
{
Key: "nested",
Value: &commonpb.AnyValue{
Value: &commonpb.AnyValue_KvlistValue{
KvlistValue: &commonpb.KeyValueList{
Values: []*commonpb.KeyValue{
{
Key: "ok",
Value: &commonpb.AnyValue{
Value: &commonpb.AnyValue_BoolValue{
BoolValue: true,
},
},
},
},
},
},
},
},
{
Key: "number",
Value: &commonpb.AnyValue{
Value: &commonpb.AnyValue_IntValue{
IntValue: 2,
},
},
},
{
Key: "slice",
Value: &commonpb.AnyValue{
Value: &commonpb.AnyValue_ArrayValue{
ArrayValue: &commonpb.ArrayValue{
Values: []*commonpb.AnyValue{
{
Value: &commonpb.AnyValue_BoolValue{
BoolValue: true,
},
},
{
Value: &commonpb.AnyValue_KvlistValue{
KvlistValue: &commonpb.KeyValueList{
Values: []*commonpb.KeyValue{
{
Key: "inner",
Value: &commonpb.AnyValue{
Value: &commonpb.AnyValue_StringValue{
StringValue: "value",
},
},
},
},
},
},
},
},
},
},
},
},
{
Key: "string",
Value: &commonpb.AnyValue{
Value: &commonpb.AnyValue_StringValue{
StringValue: "string",
},
},
},
},
},
},
},
},
{
Key: "bool to bool",
Value: &commonpb.AnyValue{
+2 -2
View File
@@ -182,10 +182,10 @@ func attributeToStringPair(kv attribute.KeyValue) (string, string) {
}
encoded, _ := json.Marshal(data)
return string(kv.Key), string(encoded)
case attribute.SLICE:
case attribute.SLICE, attribute.MAP:
// Note that this is a best effort support as this exporter is already deprecated.
// Yet, we want to preserve other existing behavior as much as possible.
// Emit the slice using the non-OTLP AnyValue string representation.
// Emit the value using the non-OTLP AnyValue string representation.
// Some values will be emitted differently than the specific types above.
return string(kv.Key), kv.Value.String()
default:
+23
View File
@@ -1003,6 +1003,29 @@ func TestAttributeToStringPairSlice(t *testing.T) {
assert.Equal(t, `[true,"Ymlu",[2,null]]`, v)
}
func TestAttributeToStringPairMap(t *testing.T) {
k, v := attributeToStringPair(attribute.Map(
"map",
attribute.String("string", "o"),
attribute.Int("number", 2),
attribute.ByteSlice("bytes", []byte("bin")),
attribute.Slice(
"slice",
attribute.BoolValue(true),
attribute.MapValue(attribute.String("inner", "value")),
),
attribute.Map("nested", attribute.Bool("ok", true)),
attribute.KeyValue{Key: "empty"},
))
assert.Equal(t, "map", k)
assert.JSONEq(
t,
`{"bytes":"Ymlu","empty":null,"nested":{"ok":true},"number":2,"slice":[true,{"inner":"value"}],"string":"o"}`,
v,
)
}
func zkmodelIDPtr(n uint64) *zkmodel.ID {
id := zkmodel.ID(n)
return &id
@@ -33,6 +33,18 @@ var (
attribute.ByteSliceValue([]byte("otlp")),
attribute.SliceValue(attribute.IntValue(2), attribute.Value{}),
)
attrMap = attribute.Map("map",
attribute.String("string", "o"),
attribute.Int("number", 2),
attribute.ByteSlice("bytes", []byte("otlp")),
attribute.Slice(
"slice",
attribute.BoolValue(true),
attribute.MapValue(attribute.String("inner", "value")),
),
attribute.Map("nested", attribute.Bool("ok", true)),
attribute.KeyValue{Key: "empty"},
)
attrStringSlice = attribute.StringSlice("string slice", []string{"o", "n"})
attrEmpty = attribute.KeyValue{
Key: attribute.Key("empty"),
@@ -61,7 +73,10 @@ var (
Values: []*cpb.AnyValue{valDblNOne, valDblOne},
},
}}
valStrO = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "o"}}
valStrO = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "o"}}
valStrValue = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{
StringValue: "value",
}}
valAttrBytes = &cpb.AnyValue{Value: &cpb.AnyValue_BytesValue{BytesValue: []byte("otlp")}}
valSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -76,6 +91,37 @@ var (
},
},
}}
valAttrMap = &cpb.AnyValue{Value: &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: []*cpb.KeyValue{
{Key: "bytes", Value: valAttrBytes},
{Key: "empty", Value: &cpb.AnyValue{}},
{Key: "nested", Value: &cpb.AnyValue{Value: &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: []*cpb.KeyValue{
{Key: "ok", Value: valBoolTrue},
},
},
}}},
{Key: "number", Value: valIntTwo},
{Key: "slice", Value: &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
Values: []*cpb.AnyValue{
valBoolTrue,
{Value: &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: []*cpb.KeyValue{
{Key: "inner", Value: valStrValue},
},
},
}},
},
},
}}},
{Key: "string", Value: valStrO},
},
},
}}
valStrN = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "n"}}
valStrSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -94,6 +140,7 @@ var (
kvString = &cpb.KeyValue{Key: "string", Value: valStrO}
kvAttrBytes = &cpb.KeyValue{Key: "bytes", Value: valAttrBytes}
kvAttrSlice = &cpb.KeyValue{Key: "slice", Value: valSlice}
kvAttrMap = &cpb.KeyValue{Key: "map", Value: valAttrMap}
kvStringSlice = &cpb.KeyValue{Key: "string slice", Value: valStrSlice}
kvEmpty = &cpb.KeyValue{Key: "empty", Value: &cpb.AnyValue{}}
)
@@ -168,6 +215,11 @@ func TestAttrTransforms(t *testing.T) {
[]attribute.KeyValue{attrSlice},
[]*cpb.KeyValue{kvAttrSlice},
},
{
"map",
[]attribute.KeyValue{attrMap},
[]*cpb.KeyValue{kvAttrMap},
},
{
"string slice",
[]attribute.KeyValue{attrStringSlice},
@@ -187,6 +239,7 @@ func TestAttrTransforms(t *testing.T) {
attrString,
attrBytes,
attrSlice,
attrMap,
attrStringSlice,
attrEmpty,
},
@@ -202,6 +255,7 @@ func TestAttrTransforms(t *testing.T) {
kvString,
kvAttrBytes,
kvAttrSlice,
kvAttrMap,
kvStringSlice,
kvEmpty,
},
@@ -205,6 +205,12 @@ func AttrValue(v attribute.Value) *cpb.AnyValue {
Values: attrValues(v.AsSlice()),
},
}
case attribute.MAP:
av.Value = &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: Attrs(v.AsMap()),
},
}
case attribute.STRINGSLICE:
av.Value = &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -91,6 +91,12 @@ func Value(v attribute.Value) *cpb.AnyValue {
Values: attrValues(v.AsSlice()),
},
}
case attribute.MAP:
av.Value = &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: KeyValues(v.AsMap()),
},
}
case attribute.STRINGSLICE:
av.Value = &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -33,6 +33,18 @@ var (
attribute.ByteSliceValue([]byte("otlp")),
attribute.SliceValue(attribute.IntValue(2), attribute.Value{}),
)
attrMap = attribute.Map("map",
attribute.String("string", "o"),
attribute.Int("number", 2),
attribute.ByteSlice("bytes", []byte("otlp")),
attribute.Slice(
"slice",
attribute.BoolValue(true),
attribute.MapValue(attribute.String("inner", "value")),
),
attribute.Map("nested", attribute.Bool("ok", true)),
attribute.KeyValue{Key: "empty"},
)
attrStringSlice = attribute.StringSlice("string slice", []string{"o", "n"})
attrEmpty = attribute.KeyValue{
Key: attribute.Key("empty"),
@@ -61,7 +73,10 @@ var (
Values: []*cpb.AnyValue{valDblNOne, valDblOne},
},
}}
valStrO = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "o"}}
valStrO = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "o"}}
valStrValue = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{
StringValue: "value",
}}
valBytes = &cpb.AnyValue{Value: &cpb.AnyValue_BytesValue{BytesValue: []byte("otlp")}}
valSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -76,6 +91,37 @@ var (
},
},
}}
valMap = &cpb.AnyValue{Value: &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: []*cpb.KeyValue{
{Key: "bytes", Value: valBytes},
{Key: "empty", Value: &cpb.AnyValue{}},
{Key: "nested", Value: &cpb.AnyValue{Value: &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: []*cpb.KeyValue{
{Key: "ok", Value: valBoolTrue},
},
},
}}},
{Key: "number", Value: valIntTwo},
{Key: "slice", Value: &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
Values: []*cpb.AnyValue{
valBoolTrue,
{Value: &cpb.AnyValue_KvlistValue{
KvlistValue: &cpb.KeyValueList{
Values: []*cpb.KeyValue{
{Key: "inner", Value: valStrValue},
},
},
}},
},
},
}}},
{Key: "string", Value: valStrO},
},
},
}}
valStrN = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "n"}}
valStrSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -94,6 +140,7 @@ var (
kvString = &cpb.KeyValue{Key: "string", Value: valStrO}
kvBytes = &cpb.KeyValue{Key: "bytes", Value: valBytes}
kvSlice = &cpb.KeyValue{Key: "slice", Value: valSlice}
kvMap = &cpb.KeyValue{Key: "map", Value: valMap}
kvStringSlice = &cpb.KeyValue{Key: "string slice", Value: valStrSlice}
kvEmpty = &cpb.KeyValue{Key: "empty", Value: &cpb.AnyValue{}}
)
@@ -168,6 +215,11 @@ func TestAttributeTransforms(t *testing.T) {
[]attribute.KeyValue{attrSlice},
[]*cpb.KeyValue{kvSlice},
},
{
"map",
[]attribute.KeyValue{attrMap},
[]*cpb.KeyValue{kvMap},
},
{
"string slice",
[]attribute.KeyValue{attrStringSlice},
@@ -187,6 +239,7 @@ func TestAttributeTransforms(t *testing.T) {
attrString,
attrBytes,
attrSlice,
attrMap,
attrStringSlice,
attrEmpty,
},
@@ -202,6 +255,7 @@ func TestAttributeTransforms(t *testing.T) {
kvString,
kvBytes,
kvSlice,
kvMap,
kvStringSlice,
kvEmpty,
},