mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-02-05 13:15:41 +02:00
Add exemplar support to OpenCensus bridge (#4585)
* add exemplar support to OpenCensus bridge * expand set of translated exemplar attributes --------- Co-authored-by: Robert Pająk <pellared@hotmail.com>
This commit is contained in:
parent
0f5565af4f
commit
a2e3e463c0
@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
- Add `Version` function in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc`. (#4660)
|
||||
- Add `Version` function in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#4660)
|
||||
- Add Summary, SummaryDataPoint, and QuantileValue to `go.opentelemetry.io/sdk/metric/metricdata`. (#4622)
|
||||
- `go.opentelemetry.io/otel/bridge/opencensus.NewMetricProducer` now supports exemplars from OpenCensus. (#4585)
|
||||
|
||||
### Deprecated
|
||||
|
||||
|
@ -59,5 +59,4 @@
|
||||
// - Summary-typed metrics are dropped
|
||||
// - GaugeDistribution-typed metrics are dropped
|
||||
// - Histogram's SumOfSquaredDeviation field is dropped
|
||||
// - Exemplars on Histograms are dropped
|
||||
package opencensus // import "go.opentelemetry.io/otel/bridge/opencensus"
|
||||
|
@ -17,8 +17,13 @@ package internal // import "go.opentelemetry.io/otel/bridge/opencensus/internal/
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
ocmetricdata "go.opencensus.io/metric/metricdata"
|
||||
octrace "go.opencensus.io/trace"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||
@ -30,6 +35,7 @@ var (
|
||||
errNegativeDistributionCount = errors.New("distribution count is negative")
|
||||
errNegativeBucketCount = errors.New("distribution bucket count is negative")
|
||||
errMismatchedAttributeKeyValues = errors.New("mismatched number of attribute keys and values")
|
||||
errInvalidExemplarSpanContext = errors.New("span context exemplar attachment does not contain an OpenCensus SpanContext")
|
||||
)
|
||||
|
||||
// ConvertMetrics converts metric data from OpenCensus to OpenTelemetry.
|
||||
@ -134,7 +140,7 @@ func convertHistogram(labelKeys []ocmetricdata.LabelKey, ts []*ocmetricdata.Time
|
||||
err = errors.Join(err, fmt.Errorf("%w: %d", errMismatchedValueTypes, p.Value))
|
||||
continue
|
||||
}
|
||||
bucketCounts, bucketErr := convertBucketCounts(dist.Buckets)
|
||||
bucketCounts, exemplars, bucketErr := convertBuckets(dist.Buckets)
|
||||
if bucketErr != nil {
|
||||
err = errors.Join(err, bucketErr)
|
||||
continue
|
||||
@ -143,7 +149,6 @@ func convertHistogram(labelKeys []ocmetricdata.LabelKey, ts []*ocmetricdata.Time
|
||||
err = errors.Join(err, fmt.Errorf("%w: %d", errNegativeDistributionCount, dist.Count))
|
||||
continue
|
||||
}
|
||||
// TODO: handle exemplars
|
||||
points = append(points, metricdata.HistogramDataPoint[float64]{
|
||||
Attributes: attrs,
|
||||
StartTime: t.StartTime,
|
||||
@ -152,22 +157,187 @@ func convertHistogram(labelKeys []ocmetricdata.LabelKey, ts []*ocmetricdata.Time
|
||||
Sum: dist.Sum,
|
||||
Bounds: dist.BucketOptions.Bounds,
|
||||
BucketCounts: bucketCounts,
|
||||
Exemplars: exemplars,
|
||||
})
|
||||
}
|
||||
}
|
||||
return metricdata.Histogram[float64]{DataPoints: points, Temporality: metricdata.CumulativeTemporality}, err
|
||||
}
|
||||
|
||||
// convertBucketCounts converts from OpenCensus bucket counts to slice of uint64.
|
||||
func convertBucketCounts(buckets []ocmetricdata.Bucket) ([]uint64, error) {
|
||||
// convertBuckets converts from OpenCensus bucket counts to slice of uint64,
|
||||
// and converts OpenCensus exemplars to OpenTelemetry exemplars.
|
||||
func convertBuckets(buckets []ocmetricdata.Bucket) ([]uint64, []metricdata.Exemplar[float64], error) {
|
||||
bucketCounts := make([]uint64, len(buckets))
|
||||
exemplars := []metricdata.Exemplar[float64]{}
|
||||
var err error
|
||||
for i, bucket := range buckets {
|
||||
if bucket.Count < 0 {
|
||||
return nil, fmt.Errorf("%w: %q", errNegativeBucketCount, bucket.Count)
|
||||
err = errors.Join(err, fmt.Errorf("%w: %q", errNegativeBucketCount, bucket.Count))
|
||||
continue
|
||||
}
|
||||
bucketCounts[i] = uint64(bucket.Count)
|
||||
|
||||
if bucket.Exemplar != nil {
|
||||
exemplar, exemplarErr := convertExemplar(bucket.Exemplar)
|
||||
if exemplarErr != nil {
|
||||
err = errors.Join(err, exemplarErr)
|
||||
continue
|
||||
}
|
||||
exemplars = append(exemplars, exemplar)
|
||||
}
|
||||
}
|
||||
return bucketCounts, nil
|
||||
return bucketCounts, exemplars, err
|
||||
}
|
||||
|
||||
// convertExemplar converts an OpenCensus exemplar to an OpenTelemetry exemplar.
|
||||
func convertExemplar(ocExemplar *ocmetricdata.Exemplar) (metricdata.Exemplar[float64], error) {
|
||||
exemplar := metricdata.Exemplar[float64]{
|
||||
Value: ocExemplar.Value,
|
||||
Time: ocExemplar.Timestamp,
|
||||
}
|
||||
var err error
|
||||
for k, v := range ocExemplar.Attachments {
|
||||
switch {
|
||||
case k == ocmetricdata.AttachmentKeySpanContext:
|
||||
sc, ok := v.(octrace.SpanContext)
|
||||
if !ok {
|
||||
err = errors.Join(err, fmt.Errorf("%w; type: %v", errInvalidExemplarSpanContext, reflect.TypeOf(v)))
|
||||
continue
|
||||
}
|
||||
exemplar.SpanID = sc.SpanID[:]
|
||||
exemplar.TraceID = sc.TraceID[:]
|
||||
default:
|
||||
exemplar.FilteredAttributes = append(exemplar.FilteredAttributes, convertKV(k, v))
|
||||
}
|
||||
}
|
||||
sortable := attribute.Sortable(exemplar.FilteredAttributes)
|
||||
sort.Sort(&sortable)
|
||||
return exemplar, err
|
||||
}
|
||||
|
||||
// convertKV converts an OpenCensus Attachment to an OpenTelemetry KeyValue.
|
||||
func convertKV(key string, value any) attribute.KeyValue {
|
||||
switch typedVal := value.(type) {
|
||||
case bool:
|
||||
return attribute.Bool(key, typedVal)
|
||||
case int:
|
||||
return attribute.Int(key, typedVal)
|
||||
case int8:
|
||||
return attribute.Int(key, int(typedVal))
|
||||
case int16:
|
||||
return attribute.Int(key, int(typedVal))
|
||||
case int32:
|
||||
return attribute.Int(key, int(typedVal))
|
||||
case int64:
|
||||
return attribute.Int64(key, typedVal)
|
||||
case uint:
|
||||
return uintKV(key, typedVal)
|
||||
case uint8:
|
||||
return uintKV(key, uint(typedVal))
|
||||
case uint16:
|
||||
return uintKV(key, uint(typedVal))
|
||||
case uint32:
|
||||
return uintKV(key, uint(typedVal))
|
||||
case uintptr:
|
||||
return uint64KV(key, uint64(typedVal))
|
||||
case uint64:
|
||||
return uint64KV(key, uint64(typedVal))
|
||||
case float32:
|
||||
return attribute.Float64(key, float64(typedVal))
|
||||
case float64:
|
||||
return attribute.Float64(key, typedVal)
|
||||
case complex64:
|
||||
return attribute.String(key, complexToString(typedVal))
|
||||
case complex128:
|
||||
return attribute.String(key, complexToString(typedVal))
|
||||
case string:
|
||||
return attribute.String(key, typedVal)
|
||||
case []bool:
|
||||
return attribute.BoolSlice(key, typedVal)
|
||||
case []int:
|
||||
return attribute.IntSlice(key, typedVal)
|
||||
case []int8:
|
||||
return intSliceKV(key, typedVal)
|
||||
case []int16:
|
||||
return intSliceKV(key, typedVal)
|
||||
case []int32:
|
||||
return intSliceKV(key, typedVal)
|
||||
case []int64:
|
||||
return attribute.Int64Slice(key, typedVal)
|
||||
case []uint:
|
||||
return uintSliceKV(key, typedVal)
|
||||
case []uint8:
|
||||
return uintSliceKV(key, typedVal)
|
||||
case []uint16:
|
||||
return uintSliceKV(key, typedVal)
|
||||
case []uint32:
|
||||
return uintSliceKV(key, typedVal)
|
||||
case []uintptr:
|
||||
return uintSliceKV(key, typedVal)
|
||||
case []uint64:
|
||||
return uintSliceKV(key, typedVal)
|
||||
case []float32:
|
||||
floatSlice := make([]float64, len(typedVal))
|
||||
for i := range typedVal {
|
||||
floatSlice[i] = float64(typedVal[i])
|
||||
}
|
||||
return attribute.Float64Slice(key, floatSlice)
|
||||
case []float64:
|
||||
return attribute.Float64Slice(key, typedVal)
|
||||
case []complex64:
|
||||
return complexSliceKV(key, typedVal)
|
||||
case []complex128:
|
||||
return complexSliceKV(key, typedVal)
|
||||
case []string:
|
||||
return attribute.StringSlice(key, typedVal)
|
||||
case fmt.Stringer:
|
||||
return attribute.Stringer(key, typedVal)
|
||||
default:
|
||||
return attribute.String(key, fmt.Sprintf("unhandled attribute value: %+v", value))
|
||||
}
|
||||
}
|
||||
|
||||
func intSliceKV[N int8 | int16 | int32](key string, val []N) attribute.KeyValue {
|
||||
intSlice := make([]int, len(val))
|
||||
for i := range val {
|
||||
intSlice[i] = int(val[i])
|
||||
}
|
||||
return attribute.IntSlice(key, intSlice)
|
||||
}
|
||||
|
||||
func uintKV(key string, val uint) attribute.KeyValue {
|
||||
if val > uint(math.MaxInt) {
|
||||
return attribute.String(key, strconv.FormatUint(uint64(val), 10))
|
||||
}
|
||||
return attribute.Int(key, int(val))
|
||||
}
|
||||
|
||||
func uintSliceKV[N uint | uint8 | uint16 | uint32 | uint64 | uintptr](key string, val []N) attribute.KeyValue {
|
||||
strSlice := make([]string, len(val))
|
||||
for i := range val {
|
||||
strSlice[i] = strconv.FormatUint(uint64(val[i]), 10)
|
||||
}
|
||||
return attribute.StringSlice(key, strSlice)
|
||||
}
|
||||
|
||||
func uint64KV(key string, val uint64) attribute.KeyValue {
|
||||
const maxInt64 = ^uint64(0) >> 1
|
||||
if val > maxInt64 {
|
||||
return attribute.String(key, strconv.FormatUint(val, 10))
|
||||
}
|
||||
return attribute.Int64(key, int64(val))
|
||||
}
|
||||
|
||||
func complexSliceKV[N complex64 | complex128](key string, val []N) attribute.KeyValue {
|
||||
strSlice := make([]string, len(val))
|
||||
for i := range val {
|
||||
strSlice[i] = complexToString(val[i])
|
||||
}
|
||||
return attribute.StringSlice(key, strSlice)
|
||||
}
|
||||
|
||||
func complexToString[N complex64 | complex128](val N) string {
|
||||
return strconv.FormatComplex(complex128(val), 'f', -1, 64)
|
||||
}
|
||||
|
||||
// convertAttrs converts from OpenCensus attribute keys and values to an
|
||||
|
@ -16,10 +16,15 @@ package internal // import "go.opentelemetry.io/otel/bridge/opencensus/opencensu
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
ocmetricdata "go.opencensus.io/metric/metricdata"
|
||||
octrace "go.opencensus.io/trace"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||
@ -28,6 +33,7 @@ import (
|
||||
|
||||
func TestConvertMetrics(t *testing.T) {
|
||||
endTime1 := time.Now()
|
||||
exemplarTime := endTime1.Add(-10 * time.Second)
|
||||
endTime2 := endTime1.Add(-time.Millisecond)
|
||||
startTime := endTime2.Add(-time.Minute)
|
||||
for _, tc := range []struct {
|
||||
@ -73,9 +79,46 @@ func TestConvertMetrics(t *testing.T) {
|
||||
Bounds: []float64{1.0, 2.0, 3.0},
|
||||
},
|
||||
Buckets: []ocmetricdata.Bucket{
|
||||
{Count: 1},
|
||||
{Count: 2},
|
||||
{Count: 5},
|
||||
{
|
||||
Count: 1,
|
||||
Exemplar: &ocmetricdata.Exemplar{
|
||||
Value: 0.8,
|
||||
Timestamp: exemplarTime,
|
||||
Attachments: map[string]interface{}{
|
||||
ocmetricdata.AttachmentKeySpanContext: octrace.SpanContext{
|
||||
TraceID: octrace.TraceID([16]byte{1}),
|
||||
SpanID: octrace.SpanID([8]byte{2}),
|
||||
},
|
||||
"bool": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Count: 2,
|
||||
Exemplar: &ocmetricdata.Exemplar{
|
||||
Value: 1.5,
|
||||
Timestamp: exemplarTime,
|
||||
Attachments: map[string]interface{}{
|
||||
ocmetricdata.AttachmentKeySpanContext: octrace.SpanContext{
|
||||
TraceID: octrace.TraceID([16]byte{3}),
|
||||
SpanID: octrace.SpanID([8]byte{4}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Count: 5,
|
||||
Exemplar: &ocmetricdata.Exemplar{
|
||||
Value: 2.6,
|
||||
Timestamp: exemplarTime,
|
||||
Attachments: map[string]interface{}{
|
||||
ocmetricdata.AttachmentKeySpanContext: octrace.SpanContext{
|
||||
TraceID: octrace.TraceID([16]byte{5}),
|
||||
SpanID: octrace.SpanID([8]byte{6}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
ocmetricdata.NewDistributionPoint(endTime2, &ocmetricdata.Distribution{
|
||||
@ -85,9 +128,45 @@ func TestConvertMetrics(t *testing.T) {
|
||||
Bounds: []float64{1.0, 2.0, 3.0},
|
||||
},
|
||||
Buckets: []ocmetricdata.Bucket{
|
||||
{Count: 1},
|
||||
{Count: 4},
|
||||
{Count: 5},
|
||||
{
|
||||
Count: 1,
|
||||
Exemplar: &ocmetricdata.Exemplar{
|
||||
Value: 0.9,
|
||||
Timestamp: exemplarTime,
|
||||
Attachments: map[string]interface{}{
|
||||
ocmetricdata.AttachmentKeySpanContext: octrace.SpanContext{
|
||||
TraceID: octrace.TraceID([16]byte{7}),
|
||||
SpanID: octrace.SpanID([8]byte{8}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Count: 4,
|
||||
Exemplar: &ocmetricdata.Exemplar{
|
||||
Value: 1.1,
|
||||
Timestamp: exemplarTime,
|
||||
Attachments: map[string]interface{}{
|
||||
ocmetricdata.AttachmentKeySpanContext: octrace.SpanContext{
|
||||
TraceID: octrace.TraceID([16]byte{9}),
|
||||
SpanID: octrace.SpanID([8]byte{10}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Count: 5,
|
||||
Exemplar: &ocmetricdata.Exemplar{
|
||||
Value: 2.7,
|
||||
Timestamp: exemplarTime,
|
||||
Attachments: map[string]interface{}{
|
||||
ocmetricdata.AttachmentKeySpanContext: octrace.SpanContext{
|
||||
TraceID: octrace.TraceID([16]byte{11}),
|
||||
SpanID: octrace.SpanID([8]byte{12}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
@ -229,6 +308,29 @@ func TestConvertMetrics(t *testing.T) {
|
||||
Sum: 100.0,
|
||||
Bounds: []float64{1.0, 2.0, 3.0},
|
||||
BucketCounts: []uint64{1, 2, 5},
|
||||
Exemplars: []metricdata.Exemplar[float64]{
|
||||
{
|
||||
Time: exemplarTime,
|
||||
Value: 0.8,
|
||||
TraceID: []byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
SpanID: []byte{2, 0, 0, 0, 0, 0, 0, 0},
|
||||
FilteredAttributes: []attribute.KeyValue{
|
||||
attribute.Bool("bool", true),
|
||||
},
|
||||
},
|
||||
{
|
||||
Time: exemplarTime,
|
||||
Value: 1.5,
|
||||
TraceID: []byte{3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
SpanID: []byte{4, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
Time: exemplarTime,
|
||||
Value: 2.6,
|
||||
TraceID: []byte{5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
SpanID: []byte{6, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Attributes: attribute.NewSet(attribute.KeyValue{
|
||||
Key: attribute.Key("a"),
|
||||
@ -243,6 +345,26 @@ func TestConvertMetrics(t *testing.T) {
|
||||
Sum: 110.0,
|
||||
Bounds: []float64{1.0, 2.0, 3.0},
|
||||
BucketCounts: []uint64{1, 4, 5},
|
||||
Exemplars: []metricdata.Exemplar[float64]{
|
||||
{
|
||||
Time: exemplarTime,
|
||||
Value: 0.9,
|
||||
TraceID: []byte{7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
SpanID: []byte{8, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
Time: exemplarTime,
|
||||
Value: 1.1,
|
||||
TraceID: []byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
SpanID: []byte{10, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
Time: exemplarTime,
|
||||
Value: 2.7,
|
||||
TraceID: []byte{11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
SpanID: []byte{12, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
@ -516,6 +638,46 @@ func TestConvertMetrics(t *testing.T) {
|
||||
},
|
||||
expectedErr: errMismatchedValueTypes,
|
||||
},
|
||||
{
|
||||
desc: "histogram with invalid span context exemplar",
|
||||
input: []*ocmetricdata.Metric{
|
||||
{
|
||||
Descriptor: ocmetricdata.Descriptor{
|
||||
Name: "foo.com/histogram-a",
|
||||
Description: "a testing histogram",
|
||||
Unit: ocmetricdata.UnitDimensionless,
|
||||
Type: ocmetricdata.TypeCumulativeDistribution,
|
||||
},
|
||||
TimeSeries: []*ocmetricdata.TimeSeries{
|
||||
{
|
||||
Points: []ocmetricdata.Point{
|
||||
ocmetricdata.NewDistributionPoint(endTime1, &ocmetricdata.Distribution{
|
||||
Count: 8,
|
||||
Sum: 100.0,
|
||||
BucketOptions: &ocmetricdata.BucketOptions{
|
||||
Bounds: []float64{1.0, 2.0, 3.0},
|
||||
},
|
||||
Buckets: []ocmetricdata.Bucket{
|
||||
{
|
||||
Count: 1,
|
||||
Exemplar: &ocmetricdata.Exemplar{
|
||||
Value: 0.8,
|
||||
Timestamp: exemplarTime,
|
||||
Attachments: map[string]interface{}{
|
||||
ocmetricdata.AttachmentKeySpanContext: "notaspancontext",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
StartTime: startTime,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErr: errInvalidExemplarSpanContext,
|
||||
},
|
||||
{
|
||||
desc: "sum with non-sum datapoint type",
|
||||
input: []*ocmetricdata.Metric{
|
||||
@ -640,3 +802,176 @@ func TestConvertAttributes(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type fakeStringer string
|
||||
|
||||
func (f fakeStringer) String() string {
|
||||
return string(f)
|
||||
}
|
||||
|
||||
func TestConvertKV(t *testing.T) {
|
||||
key := "foo"
|
||||
for _, tt := range []struct {
|
||||
value any
|
||||
expected attribute.Value
|
||||
}{
|
||||
{
|
||||
value: bool(true),
|
||||
expected: attribute.BoolValue(true),
|
||||
},
|
||||
{
|
||||
value: []bool{true, false},
|
||||
expected: attribute.BoolSliceValue([]bool{true, false}),
|
||||
},
|
||||
{
|
||||
value: int(10),
|
||||
expected: attribute.IntValue(10),
|
||||
},
|
||||
{
|
||||
value: []int{10, 20},
|
||||
expected: attribute.IntSliceValue([]int{10, 20}),
|
||||
},
|
||||
{
|
||||
value: int8(10),
|
||||
expected: attribute.IntValue(10),
|
||||
},
|
||||
{
|
||||
value: []int8{10, 20},
|
||||
expected: attribute.IntSliceValue([]int{10, 20}),
|
||||
},
|
||||
{
|
||||
value: int16(10),
|
||||
expected: attribute.IntValue(10),
|
||||
},
|
||||
{
|
||||
value: []int16{10, 20},
|
||||
expected: attribute.IntSliceValue([]int{10, 20}),
|
||||
},
|
||||
{
|
||||
value: int32(10),
|
||||
expected: attribute.IntValue(10),
|
||||
},
|
||||
{
|
||||
value: []int32{10, 20},
|
||||
expected: attribute.IntSliceValue([]int{10, 20}),
|
||||
},
|
||||
{
|
||||
value: int64(10),
|
||||
expected: attribute.Int64Value(10),
|
||||
},
|
||||
{
|
||||
value: []int64{10, 20},
|
||||
expected: attribute.Int64SliceValue([]int64{10, 20}),
|
||||
},
|
||||
{
|
||||
value: uint(10),
|
||||
expected: attribute.IntValue(10),
|
||||
},
|
||||
{
|
||||
value: uint(math.MaxUint),
|
||||
expected: attribute.StringValue(fmt.Sprintf("%v", uint(math.MaxUint))),
|
||||
},
|
||||
{
|
||||
value: []uint{10, 20},
|
||||
expected: attribute.StringSliceValue([]string{"10", "20"}),
|
||||
},
|
||||
{
|
||||
value: uint8(10),
|
||||
expected: attribute.IntValue(10),
|
||||
},
|
||||
{
|
||||
value: []uint8{10, 20},
|
||||
expected: attribute.StringSliceValue([]string{"10", "20"}),
|
||||
},
|
||||
{
|
||||
value: uint16(10),
|
||||
expected: attribute.IntValue(10),
|
||||
},
|
||||
{
|
||||
value: []uint16{10, 20},
|
||||
expected: attribute.StringSliceValue([]string{"10", "20"}),
|
||||
},
|
||||
{
|
||||
value: uint32(10),
|
||||
expected: attribute.IntValue(10),
|
||||
},
|
||||
{
|
||||
value: []uint32{10, 20},
|
||||
expected: attribute.StringSliceValue([]string{"10", "20"}),
|
||||
},
|
||||
{
|
||||
value: uint64(10),
|
||||
expected: attribute.Int64Value(10),
|
||||
},
|
||||
{
|
||||
value: uint64(math.MaxUint64),
|
||||
expected: attribute.StringValue("18446744073709551615"),
|
||||
},
|
||||
{
|
||||
value: []uint64{10, 20},
|
||||
expected: attribute.StringSliceValue([]string{"10", "20"}),
|
||||
},
|
||||
{
|
||||
value: uintptr(10),
|
||||
expected: attribute.Int64Value(10),
|
||||
},
|
||||
{
|
||||
value: []uintptr{10, 20},
|
||||
expected: attribute.StringSliceValue([]string{"10", "20"}),
|
||||
},
|
||||
{
|
||||
value: float32(10),
|
||||
expected: attribute.Float64Value(10),
|
||||
},
|
||||
{
|
||||
value: []float32{10, 20},
|
||||
expected: attribute.Float64SliceValue([]float64{10, 20}),
|
||||
},
|
||||
{
|
||||
value: float64(10),
|
||||
expected: attribute.Float64Value(10),
|
||||
},
|
||||
{
|
||||
value: []float64{10, 20},
|
||||
expected: attribute.Float64SliceValue([]float64{10, 20}),
|
||||
},
|
||||
{
|
||||
value: complex64(10),
|
||||
expected: attribute.StringValue("(10+0i)"),
|
||||
},
|
||||
{
|
||||
value: []complex64{10, 20},
|
||||
expected: attribute.StringSliceValue([]string{"(10+0i)", "(20+0i)"}),
|
||||
},
|
||||
{
|
||||
value: complex128(10),
|
||||
expected: attribute.StringValue("(10+0i)"),
|
||||
},
|
||||
{
|
||||
value: []complex128{10, 20},
|
||||
expected: attribute.StringSliceValue([]string{"(10+0i)", "(20+0i)"}),
|
||||
},
|
||||
{
|
||||
value: "string",
|
||||
expected: attribute.StringValue("string"),
|
||||
},
|
||||
{
|
||||
value: []string{"string", "slice"},
|
||||
expected: attribute.StringSliceValue([]string{"string", "slice"}),
|
||||
},
|
||||
{
|
||||
value: fakeStringer("stringer"),
|
||||
expected: attribute.StringValue("stringer"),
|
||||
},
|
||||
{
|
||||
value: metricdata.Histogram[float64]{},
|
||||
expected: attribute.StringValue("unhandled attribute value: {DataPoints:[] Temporality:undefinedTemporality}"),
|
||||
},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("%v(%+v)", reflect.TypeOf(tt.value), tt.value), func(t *testing.T) {
|
||||
got := convertKV(key, tt.value)
|
||||
assert.Equal(t, key, string(got.Key))
|
||||
assert.Equal(t, tt.expected, got.Value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user