1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2026-06-03 18:35:08 +02:00

Support BYTESLICE attributes across trace and exporter paths (#8153)

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

Supersedes #8042 which does not address the `trace` or `zipkin`
packages, and has unrelated support changes.

Add end-to-end handling for `attribute.BYTESLICE` in the remaining trace
and exporter paths that still dropped, invalidated, or stringified byte
slice attributes.

This change:

- preserves byte slice attributes in `trace/auto`
- encodes byte slice attributes as OTLP `AnyValue_BytesValue` in trace,
log, and metric transforms
- serializes Zipkin byte slice attributes as JSON arrays of byte values
- adds regression tests for each updated path

## Problem

`attribute.BYTESLICE` is public, but several downstream conversions
still did not handle it correctly:

- `trace/auto` dropped byte slice attributes during conversion
- OTLP trace, log, and metric transforms fell through to their invalid
default handling
- Zipkin fell back to `Value.Emit()`, which produced a base64 string
rather than an explicit byte-array representation

That made `BYTESLICE` unusable or inconsistent depending on the export
path.

## Changes

### Trace

- Handle `attribute.BYTESLICE` in `trace/auto` by converting it to an
internal telemetry bytes value.
- Add a regression test covering byte slice conversion.

### OTLP

- Handle `attribute.BYTESLICE` in:
  - trace attribute transform
  - log gRPC attribute transform
  - log HTTP attribute transform
  - metric HTTP attribute transform
  - metric gRPC attribute transform
- Update the shared log and metric transform templates so generated
outputs stay aligned.
- Add regression tests for the trace transform, both log transform
outputs, and both metric transform outputs.

### Zipkin

- Handle `attribute.BYTESLICE` explicitly in Zipkin tag serialization.
- Serialize byte slices as JSON arrays of byte values instead of base64
text.
- Add a regression test for Zipkin byte slice serialization.

---------

Co-authored-by: Robert Pająk <pellared@hotmail.com>
This commit is contained in:
Tyler Yahn
2026-04-09 14:04:03 -07:00
committed by GitHub
parent 112bed7fc0
commit b1284dbfaa
19 changed files with 130 additions and 0 deletions
@@ -25,6 +25,7 @@ var (
attrFloat64 = attribute.Float64("float64", 1)
attrFloat64Slice = attribute.Float64Slice("float64 slice", []float64{-1, 1})
attrString = attribute.String("string", "o")
attrBytes = attribute.ByteSlice("bytes", []byte("otlp"))
attrStringSlice = attribute.StringSlice("string slice", []string{"o", "n"})
attrEmpty = attribute.KeyValue{
Key: attribute.Key("empty"),
@@ -53,6 +54,7 @@ var (
},
}}
valStrO = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "o"}}
valAttrBytes = &cpb.AnyValue{Value: &cpb.AnyValue_BytesValue{BytesValue: []byte("otlp")}}
valStrN = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "n"}}
valStrSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -69,6 +71,7 @@ var (
kvFloat64 = &cpb.KeyValue{Key: "float64", Value: valDblOne}
kvFloat64Slice = &cpb.KeyValue{Key: "float64 slice", Value: valDblSlice}
kvString = &cpb.KeyValue{Key: "string", Value: valStrO}
kvAttrBytes = &cpb.KeyValue{Key: "bytes", Value: valAttrBytes}
kvStringSlice = &cpb.KeyValue{Key: "string slice", Value: valStrSlice}
kvEmpty = &cpb.KeyValue{Key: "empty", Value: &cpb.AnyValue{}}
)
@@ -133,6 +136,11 @@ func TestAttrTransforms(t *testing.T) {
[]attribute.KeyValue{attrString},
[]*cpb.KeyValue{kvString},
},
{
"bytes",
[]attribute.KeyValue{attrBytes},
[]*cpb.KeyValue{kvAttrBytes},
},
{
"string slice",
[]attribute.KeyValue{attrStringSlice},
@@ -150,6 +158,7 @@ func TestAttrTransforms(t *testing.T) {
attrFloat64,
attrFloat64Slice,
attrString,
attrBytes,
attrStringSlice,
attrEmpty,
},
@@ -163,6 +172,7 @@ func TestAttrTransforms(t *testing.T) {
kvFloat64,
kvFloat64Slice,
kvString,
kvAttrBytes,
kvStringSlice,
kvEmpty,
},
@@ -195,6 +195,10 @@ func AttrValue(v attribute.Value) *cpb.AnyValue {
av.Value = &cpb.AnyValue_StringValue{
StringValue: v.AsString(),
}
case attribute.BYTESLICE:
av.Value = &cpb.AnyValue_BytesValue{
BytesValue: v.AsByteSlice(),
}
case attribute.STRINGSLICE:
av.Value = &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -81,6 +81,10 @@ func Value(v attribute.Value) *cpb.AnyValue {
av.Value = &cpb.AnyValue_StringValue{
StringValue: v.AsString(),
}
case attribute.BYTESLICE:
av.Value = &cpb.AnyValue_BytesValue{
BytesValue: v.AsByteSlice(),
}
case attribute.STRINGSLICE:
av.Value = &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -25,6 +25,7 @@ var (
attrFloat64 = attribute.Float64("float64", 1)
attrFloat64Slice = attribute.Float64Slice("float64 slice", []float64{-1, 1})
attrString = attribute.String("string", "o")
attrBytes = attribute.ByteSlice("bytes", []byte("otlp"))
attrStringSlice = attribute.StringSlice("string slice", []string{"o", "n"})
attrEmpty = attribute.KeyValue{
Key: attribute.Key("empty"),
@@ -53,6 +54,7 @@ var (
},
}}
valStrO = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "o"}}
valBytes = &cpb.AnyValue{Value: &cpb.AnyValue_BytesValue{BytesValue: []byte("otlp")}}
valStrN = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "n"}}
valStrSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
@@ -69,6 +71,7 @@ var (
kvFloat64 = &cpb.KeyValue{Key: "float64", Value: valDblOne}
kvFloat64Slice = &cpb.KeyValue{Key: "float64 slice", Value: valDblSlice}
kvString = &cpb.KeyValue{Key: "string", Value: valStrO}
kvBytes = &cpb.KeyValue{Key: "bytes", Value: valBytes}
kvStringSlice = &cpb.KeyValue{Key: "string slice", Value: valStrSlice}
kvEmpty = &cpb.KeyValue{Key: "empty", Value: &cpb.AnyValue{}}
)
@@ -133,6 +136,11 @@ func TestAttributeTransforms(t *testing.T) {
[]attribute.KeyValue{attrString},
[]*cpb.KeyValue{kvString},
},
{
"bytes",
[]attribute.KeyValue{attrBytes},
[]*cpb.KeyValue{kvBytes},
},
{
"string slice",
[]attribute.KeyValue{attrStringSlice},
@@ -150,6 +158,7 @@ func TestAttributeTransforms(t *testing.T) {
attrFloat64,
attrFloat64Slice,
attrString,
attrBytes,
attrStringSlice,
attrEmpty,
},
@@ -163,6 +172,7 @@ func TestAttributeTransforms(t *testing.T) {
kvFloat64,
kvFloat64Slice,
kvString,
kvBytes,
kvStringSlice,
kvEmpty,
},