Skip to content

Commit

Permalink
support summaries in the OpenCensus bridge
Browse files Browse the repository at this point in the history
  • Loading branch information
dashpole committed Oct 9, 2023
1 parent 17729c5 commit 4b85a61
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 4 deletions.
1 change: 0 additions & 1 deletion bridge/opencensus/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
// implemented, and An error will be sent to the OpenTelemetry ErrorHandler.
//
// There are known limitations to the metric bridge:
// - Summary-typed metrics are dropped
// - GaugeDistribution-typed metrics are dropped
// - Histogram's SumOfSquaredDeviation field is dropped
// - Exemplars on Histograms are dropped
Expand Down
66 changes: 64 additions & 2 deletions bridge/opencensus/internal/ocmetric/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package internal // import "go.opentelemetry.io/otel/bridge/opencensus/internal/
import (
"errors"
"fmt"
"sort"

ocmetricdata "go.opencensus.io/metric/metricdata"

Expand All @@ -30,7 +31,7 @@ var (
errMismatchedValueTypes = errors.New("wrong value type for data point")
errNumberDataPoint = errors.New("converting a number data point")
errHistogramDataPoint = errors.New("converting a histogram data point")
errNegativeDistributionCount = errors.New("distribution count is negative")
errNegativeCount = errors.New("distribution or summary count is negative")
errNegativeBucketCount = errors.New("distribution bucket count is negative")
errMismatchedAttributeKeyValues = errors.New("mismatched number of attribute keys and values")
)
Expand Down Expand Up @@ -76,6 +77,8 @@ func convertAggregation(metric *ocmetricdata.Metric) (metricdata.Aggregation, er
return convertSum[float64](labelKeys, metric.TimeSeries)
case ocmetricdata.TypeCumulativeDistribution:
return convertHistogram(labelKeys, metric.TimeSeries)
case ocmetricdata.TypeSummary:
return convertSummary(labelKeys, metric.TimeSeries)
// TODO: Support summaries, once it is in the OTel data types.
}
return nil, fmt.Errorf("%w: %q", errAggregationType, metric.Descriptor.Type)
Expand Down Expand Up @@ -148,7 +151,7 @@ func convertHistogram(labelKeys []ocmetricdata.LabelKey, ts []*ocmetricdata.Time
continue
}
if dist.Count < 0 {
errInfo = append(errInfo, fmt.Sprintf("%v: %d", errNegativeDistributionCount, dist.Count))
errInfo = append(errInfo, fmt.Sprintf("%v: %d", errNegativeCount, dist.Count))
continue
}
// TODO: handle exemplars
Expand Down Expand Up @@ -182,6 +185,65 @@ func convertBucketCounts(buckets []ocmetricdata.Bucket) ([]uint64, error) {
return bucketCounts, nil
}

// convertSummary converts OpenCensus Summary timeseries to an
// OpenTelemetry Summary.
func convertSummary(labelKeys []ocmetricdata.LabelKey, ts []*ocmetricdata.TimeSeries) (metricdata.Summary, error) {
points := make([]metricdata.SummaryDataPoint, 0, len(ts))
var err error
for _, t := range ts {
attrs, attrErr := convertAttrs(labelKeys, t.LabelValues)
if attrErr != nil {
err = errors.Join(err, attrErr)
continue
}
for _, p := range t.Points {
summary, ok := p.Value.(*ocmetricdata.Summary)
if !ok {
err = errors.Join(err, fmt.Errorf("%w: %d", errMismatchedValueTypes, p.Value))
continue
}
if summary.Count < 0 {
err = errors.Join(err, fmt.Errorf("%w: %d", errNegativeCount, summary.Count))
continue
}
point := metricdata.SummaryDataPoint{
Attributes: attrs,
StartTime: t.StartTime,
Time: p.Time,
Count: uint64(summary.Count),
QuantileValues: convertQuantiles(summary.Snapshot),
}
if summary.HasCountAndSum {
point.Sum = &summary.Sum
}
points = append(points, point)
}
}
return metricdata.Summary{DataPoints: points}, err
}

// convertQuantiles converts an OpenCensus summary snapshot to
// OpenTelemetry quantiles.
func convertQuantiles(snapshot ocmetricdata.Snapshot) []metricdata.ValueAtQuantile {
quantileValues := make([]metricdata.ValueAtQuantile, 0, len(snapshot.Percentiles))
for quantile, value := range snapshot.Percentiles {
quantileValues = append(quantileValues, metricdata.ValueAtQuantile{
Quantile: quantile,
Value: value,
})
}
sort.Sort(byQuantile(quantileValues))
return quantileValues
}

// ByAge implements sort.Interface for []metricdata.ValueAtQuantile
// based on the Quantile field.
type byQuantile []metricdata.ValueAtQuantile

func (a byQuantile) Len() int { return len(a) }
func (a byQuantile) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byQuantile) Less(i, j int) bool { return a[i].Quantile < a[j].Quantile }

// convertAttrs converts from OpenCensus attribute keys and values to an
// OpenTelemetry attribute Set.
func convertAttrs(keys []ocmetricdata.LabelKey, values []ocmetricdata.LabelValue) (attribute.Set, error) {
Expand Down
112 changes: 111 additions & 1 deletion bridge/opencensus/internal/ocmetric/metric_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func TestConvertMetrics(t *testing.T) {
expected: []metricdata.Metrics{},
},
{
desc: "normal Histogram, gauges, and sums",
desc: "normal Histogram, summary, gauges, and sums",
input: []*ocmetricdata.Metric{
{
Descriptor: ocmetricdata.Descriptor{
Expand Down Expand Up @@ -207,6 +207,54 @@ func TestConvertMetrics(t *testing.T) {
},
},
},
}, {
Descriptor: ocmetricdata.Descriptor{
Name: "foo.com/summary-a",
Description: "a testing summary",
Unit: ocmetricdata.UnitMilliseconds,
Type: ocmetricdata.TypeSummary,
LabelKeys: []ocmetricdata.LabelKey{
{Key: "g"},
{Key: "h"},
},
},
TimeSeries: []*ocmetricdata.TimeSeries{
{
LabelValues: []ocmetricdata.LabelValue{
{
Value: "ding",
Present: true,
}, {
Value: "dong",
Present: true,
},
},
Points: []ocmetricdata.Point{
ocmetricdata.NewSummaryPoint(endTime1, &ocmetricdata.Summary{
Count: 10,
Sum: 13.2,
HasCountAndSum: true,
Snapshot: ocmetricdata.Snapshot{
Percentiles: map[float64]float64{
0.0: 0.1,
0.5: 1.0,
1.0: 10.4,
},
},
}),
ocmetricdata.NewSummaryPoint(endTime2, &ocmetricdata.Summary{
Count: 12,
Snapshot: ocmetricdata.Snapshot{
Percentiles: map[float64]float64{
0.0: 0.2,
0.5: 1.1,
1.0: 10.5,
},
},
}),
},
},
},
},
},
expected: []metricdata.Metrics{
Expand Down Expand Up @@ -368,6 +416,64 @@ func TestConvertMetrics(t *testing.T) {
},
},
},
}, {
Name: "foo.com/summary-a",
Description: "a testing summary",
Unit: "ms",
Data: metricdata.Summary{
DataPoints: []metricdata.SummaryDataPoint{
{
Attributes: attribute.NewSet(attribute.KeyValue{
Key: attribute.Key("g"),
Value: attribute.StringValue("ding"),
}, attribute.KeyValue{
Key: attribute.Key("h"),
Value: attribute.StringValue("dong"),
}),
Time: endTime1,
Count: 10,
Sum: pointerTo(13.2),
QuantileValues: []metricdata.ValueAtQuantile{
{
Quantile: 0.0,
Value: 0.1,
},
{
Quantile: 0.5,
Value: 1.0,
},
{
Quantile: 1.0,
Value: 10.4,
},
},
}, {
Attributes: attribute.NewSet(attribute.KeyValue{
Key: attribute.Key("g"),
Value: attribute.StringValue("ding"),
}, attribute.KeyValue{
Key: attribute.Key("h"),
Value: attribute.StringValue("dong"),
}),
Time: endTime2,
Count: 12,
QuantileValues: []metricdata.ValueAtQuantile{
{
Quantile: 0.0,
Value: 0.2,
},
{
Quantile: 0.5,
Value: 1.1,
},
{
Quantile: 1.0,
Value: 10.5,
},
},
},
},
},
},
},
}, {
Expand Down Expand Up @@ -632,3 +738,7 @@ func TestConvertAttributes(t *testing.T) {
})
}
}

func pointerTo(f float64) *float64 {
return &f
}

0 comments on commit 4b85a61

Please sign in to comment.