Skip to content

Commit

Permalink
Merge branch 'main' into mvg/otlpmetric-readme
Browse files Browse the repository at this point in the history
  • Loading branch information
pellared committed Nov 2, 2023
2 parents 7918ebf + e3d0c50 commit dc5a265
Show file tree
Hide file tree
Showing 82 changed files with 1,103 additions and 255 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ 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)
- Add support for `WithExplicitBucketBoundaries` in `go.opentelemetry.io/otel/sdk/metric`. (#4605)

### Deprecated

Expand All @@ -46,9 +48,16 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
See the "API Implementations" section of the `go.opentelemetry.io/otel/trace` package documentation for more informatoin about how to accomplish this. (#4620)
- `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` does no longer depend on `go.opentelemetry.io/otel/exporters/otlp/otlpmetric`. (#4660)
- `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` does no longer depend on `go.opentelemetry.io/otel/exporters/otlp/otlpmetric`. (#4660)
- Retry for `502 Bad Gateway` and `504 Gateway Timeout` HTTP statuses in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#4670)
- Retry for `502 Bad Gateway` and `504 Gateway Timeout` HTTP statuses in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#4670)
- Retry for `RESOURCE_EXHAUSTED` only if RetryInfo is returned in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc`. (#4669)
- Retry for `RESOURCE_EXHAUSTED` only if RetryInfo is returned in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`. (#4669)
- Retry temporary HTTP request failures in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#4679)
- Retry temporary HTTP request failures in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#4679)

### Fixed

- Fix improper parsing of characters such us `+`, `\` by `Parse` in `go.opentelemetry.io/otel/baggage` as they were rendered as a whitespace. (#4667)
- In `go.opentelemetry.op/otel/exporters/prometheus`, the exporter no longer `Collect`s metrics after `Shutdown` is invoked. (#4648)

## [1.19.0/0.42.0/0.0.7] 2023-09-28
Expand Down
4 changes: 2 additions & 2 deletions baggage/baggage.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ func NewMember(key, value string, props ...Property) (Member, error) {
if err := m.validate(); err != nil {
return newInvalidMember(), err
}
decodedValue, err := url.QueryUnescape(value)
decodedValue, err := url.PathUnescape(value)
if err != nil {
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, value)
}
Expand Down Expand Up @@ -301,7 +301,7 @@ func parseMember(member string) (Member, error) {
// when converting the header into a data structure."
key = strings.TrimSpace(k)
var err error
value, err = url.QueryUnescape(strings.TrimSpace(v))
value, err = url.PathUnescape(strings.TrimSpace(v))
if err != nil {
return newInvalidMember(), fmt.Errorf("%w: %q", err, value)
}
Expand Down
49 changes: 49 additions & 0 deletions baggage/baggage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,48 @@ func TestBaggageParse(t *testing.T) {
"foo": {Value: "1"},
},
},
{
name: "single member no properties plus",
in: "foo=1+1",
want: baggage.List{
"foo": {Value: "1+1"},
},
},
{
name: "single member no properties plus encoded",
in: "foo=1%2B1",
want: baggage.List{
"foo": {Value: "1+1"},
},
},
{
name: "single member no properties slash",
in: "foo=1/1",
want: baggage.List{
"foo": {Value: "1/1"},
},
},
{
name: "single member no properties slash encoded",
in: "foo=1%2F1",
want: baggage.List{
"foo": {Value: "1/1"},
},
},
{
name: "single member no properties equals",
in: "foo=1=1",
want: baggage.List{
"foo": {Value: "1=1"},
},
},
{
name: "single member no properties equals encoded",
in: "foo=1%3D1",
want: baggage.List{
"foo": {Value: "1=1"},
},
},
{
name: "single member with spaces",
in: " foo \t= 1\t\t ",
Expand Down Expand Up @@ -440,6 +482,13 @@ func TestBaggageString(t *testing.T) {
"foo": {Value: "1=1"},
},
},
{
name: "plus",
out: "foo=1%2B1",
baggage: baggage.List{
"foo": {Value: "1+1"},
},
},
{
name: "single member empty value with properties",
out: "foo=;red;state=on",
Expand Down
1 change: 0 additions & 1 deletion bridge/opencensus/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
2 changes: 1 addition & 1 deletion bridge/opencensus/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ require (

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/kr/pretty v0.1.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions bridge/opencensus/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
Expand Down
182 changes: 176 additions & 6 deletions bridge/opencensus/internal/ocmetric/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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, 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 bucketCounts, nil
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
Expand Down

0 comments on commit dc5a265

Please sign in to comment.