1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2026-06-03 18:35:08 +02:00
Files
opentelemetry-go/internal/shared/otlp/otlpmetric/transform/attribute_test.go.tmpl
T
Tyler Yahn b1284dbfaa 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>
2026-04-09 14:04:03 -07:00

192 lines
5.3 KiB
Cheetah

// Code generated by gotmpl. DO NOT MODIFY.
// source: internal/shared/otlp/otlpmetric/transform/attribute_test.go.tmpl
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package transform
import (
"testing"
"github.com/stretchr/testify/assert"
"go.opentelemetry.io/otel/attribute"
cpb "go.opentelemetry.io/proto/otlp/common/v1"
)
var (
attrBool = attribute.Bool("bool", true)
attrBoolSlice = attribute.BoolSlice("bool slice", []bool{true, false})
attrInt = attribute.Int("int", 1)
attrIntSlice = attribute.IntSlice("int slice", []int{-1, 1})
attrInt64 = attribute.Int64("int64", 1)
attrInt64Slice = attribute.Int64Slice("int64 slice", []int64{-1, 1})
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"),
Value: attribute.Value{},
}
valBoolTrue = &cpb.AnyValue{Value: &cpb.AnyValue_BoolValue{BoolValue: true}}
valBoolFalse = &cpb.AnyValue{Value: &cpb.AnyValue_BoolValue{BoolValue: false}}
valBoolSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
Values: []*cpb.AnyValue{valBoolTrue, valBoolFalse},
},
}}
valIntOne = &cpb.AnyValue{Value: &cpb.AnyValue_IntValue{IntValue: 1}}
valIntNOne = &cpb.AnyValue{Value: &cpb.AnyValue_IntValue{IntValue: -1}}
valIntSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
Values: []*cpb.AnyValue{valIntNOne, valIntOne},
},
}}
valDblOne = &cpb.AnyValue{Value: &cpb.AnyValue_DoubleValue{DoubleValue: 1}}
valDblNOne = &cpb.AnyValue{Value: &cpb.AnyValue_DoubleValue{DoubleValue: -1}}
valDblSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
ArrayValue: &cpb.ArrayValue{
Values: []*cpb.AnyValue{valDblNOne, valDblOne},
},
}}
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{
Values: []*cpb.AnyValue{valStrO, valStrN},
},
}}
kvBool = &cpb.KeyValue{Key: "bool", Value: valBoolTrue}
kvBoolSlice = &cpb.KeyValue{Key: "bool slice", Value: valBoolSlice}
kvInt = &cpb.KeyValue{Key: "int", Value: valIntOne}
kvIntSlice = &cpb.KeyValue{Key: "int slice", Value: valIntSlice}
kvInt64 = &cpb.KeyValue{Key: "int64", Value: valIntOne}
kvInt64Slice = &cpb.KeyValue{Key: "int64 slice", Value: valIntSlice}
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{}}
)
type attributeTest struct {
name string
in []attribute.KeyValue
want []*cpb.KeyValue
}
func TestAttributeTransforms(t *testing.T) {
for _, test := range []attributeTest{
{"nil", nil, nil},
{"empty", []attribute.KeyValue{}, nil},
{
"empty",
[]attribute.KeyValue{attrEmpty},
[]*cpb.KeyValue{kvEmpty},
},
{
"bool",
[]attribute.KeyValue{attrBool},
[]*cpb.KeyValue{kvBool},
},
{
"bool slice",
[]attribute.KeyValue{attrBoolSlice},
[]*cpb.KeyValue{kvBoolSlice},
},
{
"int",
[]attribute.KeyValue{attrInt},
[]*cpb.KeyValue{kvInt},
},
{
"int slice",
[]attribute.KeyValue{attrIntSlice},
[]*cpb.KeyValue{kvIntSlice},
},
{
"int64",
[]attribute.KeyValue{attrInt64},
[]*cpb.KeyValue{kvInt64},
},
{
"int64 slice",
[]attribute.KeyValue{attrInt64Slice},
[]*cpb.KeyValue{kvInt64Slice},
},
{
"float64",
[]attribute.KeyValue{attrFloat64},
[]*cpb.KeyValue{kvFloat64},
},
{
"float64 slice",
[]attribute.KeyValue{attrFloat64Slice},
[]*cpb.KeyValue{kvFloat64Slice},
},
{
"string",
[]attribute.KeyValue{attrString},
[]*cpb.KeyValue{kvString},
},
{
"bytes",
[]attribute.KeyValue{attrBytes},
[]*cpb.KeyValue{kvBytes},
},
{
"string slice",
[]attribute.KeyValue{attrStringSlice},
[]*cpb.KeyValue{kvStringSlice},
},
{
"all",
[]attribute.KeyValue{
attrBool,
attrBoolSlice,
attrInt,
attrIntSlice,
attrInt64,
attrInt64Slice,
attrFloat64,
attrFloat64Slice,
attrString,
attrBytes,
attrStringSlice,
attrEmpty,
},
[]*cpb.KeyValue{
kvBool,
kvBoolSlice,
kvInt,
kvIntSlice,
kvInt64,
kvInt64Slice,
kvFloat64,
kvFloat64Slice,
kvString,
kvBytes,
kvStringSlice,
kvEmpty,
},
},
} {
t.Run(test.name, func(t *testing.T) {
t.Run("KeyValues", func(t *testing.T) {
assert.ElementsMatch(t, test.want, KeyValues(test.in))
})
t.Run("AttrIter", func(t *testing.T) {
s := attribute.NewSet(test.in...)
assert.ElementsMatch(t, test.want, AttrIter(s.Iter()))
})
})
}
}