Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exponential histogram aggregator & export support #2393

Closed
wants to merge 64 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
9f610ba
Working draft of expo histogram with variable scale
Sep 24, 2021
c6070fb
test circular buffer forward
Sep 25, 2021
23c7129
up to downscale logic
Sep 26, 2021
7823119
partial downscale
Sep 26, 2021
2e4e8b5
rotate, downscale, moveBucket
Sep 27, 2021
3fb9afb
comment and re-order implementation methods
Sep 27, 2021
ec33c8e
Merge branch 'main' of github.com:open-telemetry/opentelemetry-go int…
Sep 27, 2021
af40924
Update NewDescriptor calls.
Sep 27, 2021
a03c191
improve test
Sep 28, 2021
0a3a472
add a test
Sep 29, 2021
7044ef2
typo
Sep 29, 2021
83df720
Merge branch 'main' of github.com:open-telemetry/opentelemetry-go int…
Oct 5, 2021
06629d3
remove histo
Oct 5, 2021
b05fe9f
Merge branch 'main' of github.com:open-telemetry/opentelemetry-go int…
Oct 8, 2021
c489c48
Merge branch 'main' of github.com:open-telemetry/opentelemetry-go int…
Nov 9, 2021
96cffe5
Merge branch 'main' of github.com:open-telemetry/opentelemetry-go int…
Nov 9, 2021
8438c54
add a test for aggregation kind
Nov 9, 2021
c93ede0
import from contrib, it's difficult to keep the exporter and impl in …
Nov 10, 2021
d47e56f
update test
Nov 10, 2021
3f4b34c
add merge test
Nov 10, 2021
3db13d4
pass test
Nov 15, 2021
1a4cd0d
Merge branch 'main' of github.com:open-telemetry/opentelemetry-go int…
Nov 16, 2021
afb52e4
add logarithm test
Nov 16, 2021
49bd8f6
fix benchmarks
Nov 16, 2021
28c0a23
tests with UpdateByIncr()
Nov 17, 2021
d1de2ea
more test coverage
Nov 18, 2021
7aaffef
move internal
Nov 18, 2021
cbae649
remove scalb
Nov 19, 2021
60ea13d
test logarithm boundary cases
Nov 19, 2021
2782ae3
explain min/max values
Nov 19, 2021
ab4044d
remove kind field
Nov 20, 2021
ad23cd5
add MinScale to exponent mapping
Dec 1, 2021
8123fbc
test logarithm mapping underflow
Dec 1, 2021
b4d9afa
test fixed scale logic
Dec 1, 2021
45ce7f6
fix broken tests related to sum a number.Number
Dec 1, 2021
d8e9870
test integer merge
Dec 1, 2021
17385de
Merge branch 'main' of github.com:open-telemetry/opentelemetry-go int…
Dec 1, 2021
bbe1c1a
fix build
Dec 2, 2021
e6d9346
lint
Dec 3, 2021
9d0ec85
test expohisto to OTLP
Dec 3, 2021
b82c9a0
test integer expohisto logic
Dec 3, 2021
3cfa061
lint
Dec 3, 2021
b5c599f
Add a README
Dec 4, 2021
41bae95
simple selector
Dec 7, 2021
ff33a6e
hard-code the last boundary value because the formulas misbehave near…
Dec 7, 2021
dd5e722
map negative scale to correct subnormals in _reverse_ lookup
Dec 7, 2021
4c38c21
full range test
Dec 8, 2021
63b78d0
Merge branch 'main' of github.com:open-telemetry/opentelemetry-go int…
Dec 8, 2021
c612753
changelog
Dec 8, 2021
e37e5d8
document the tests, a bit more testing
Dec 8, 2021
bbf190e
Add comments, benchmarks
Dec 8, 2021
1e7ff6e
Lint
Dec 8, 2021
ca5b38d
Comment on subnormals
Dec 9, 2021
8318ba7
edit readme
Dec 9, 2021
64f7dea
explain max scale
Dec 9, 2021
224ad8f
comments & more readme
Dec 10, 2021
a02bf9b
Merge branch 'main' of github.com:open-telemetry/opentelemetry-go int…
Jan 5, 2022
a1dc12f
Merge branch 'main' of github.com:open-telemetry/opentelemetry-go int…
Jan 6, 2022
b2989ea
remove new test from legacy sdk/export/metric
Jan 6, 2022
e1399d9
lint
Jan 6, 2022
ef19ae0
vanity
Jan 6, 2022
62a88db
markdown lint
Jan 6, 2022
9f9de49
move the benchmark
Jan 7, 2022
77d01c9
comments
Jan 7, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Added

- Support `OTEL_EXPORTER_ZIPKIN_ENDPOINT` env to specify zipkin collector endpoint (#2490)
- Add an exponential histogram aggregator, a corresponding aggregation.Kind, and OTLP exporter support. (TODO)

### Changed

Expand Down
79 changes: 79 additions & 0 deletions exporters/otlp/otlpmetric/internal/metrictransform/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,13 @@ func Record(temporalitySelector aggregation.TemporalitySelector, r export.Record
}
return histogramPoint(r, temporalitySelector.TemporalityFor(r.Descriptor(), aggregation.HistogramKind), h)

case aggregation.ExponentialHistogramKind:
h, ok := agg.(aggregation.ExponentialHistogram)
if !ok {
return nil, fmt.Errorf("%w: %T", ErrIncompatibleAgg, agg)
}
return exponentialHistogramPoint(r, temporalitySelector.TemporalityFor(r.Descriptor(), aggregation.ExponentialHistogramKind), h)

case aggregation.SumKind:
s, ok := agg.(aggregation.Sum)
if !ok {
Expand Down Expand Up @@ -438,3 +445,75 @@ func histogramPoint(record export.Record, temporality aggregation.Temporality, a
}
return m, nil
}

// exponentialHistogramPoint transforms an ExponentialHistogram Aggregator into an OTLP Metric.
func exponentialHistogramPoint(record export.Record, temporality aggregation.Temporality, a aggregation.ExponentialHistogram) (*metricpb.Metric, error) {
desc := record.Descriptor()
labels := record.Labels()

count, err := a.Count()
if err != nil {
return nil, err
}

sum, err := a.Sum()
if err != nil {
return nil, err
}

zeros, err := a.ZeroCount()
if err != nil {
return nil, err
}

scale, err := a.Scale()
if err != nil {
return nil, err
}

point := &metricpb.ExponentialHistogramDataPoint{
Attributes: Iterator(labels.Iter()),
StartTimeUnixNano: toNanos(record.StartTime()),
TimeUnixNano: toNanos(record.EndTime()),
Count: count,
Sum: sum.CoerceToFloat64(desc.NumberKind()),
ZeroCount: zeros,
Scale: scale, // Note: will be zero if all zeros.
}

if pos, err := a.Positive(); pos != nil && err == nil && pos.Len() != 0 {
point.Positive = exponentialHistogramBucket(pos)
} else if err != nil {
return nil, err
}

if neg, err := a.Negative(); neg != nil && err == nil && neg.Len() != 0 {
point.Negative = exponentialHistogramBucket(neg)
} else if err != nil {
return nil, err
}

m := &metricpb.Metric{
Name: desc.Name(),
Description: desc.Description(),
Unit: string(desc.Unit()),
Data: &metricpb.Metric_ExponentialHistogram{
ExponentialHistogram: &metricpb.ExponentialHistogram{
AggregationTemporality: sdkTemporalityToTemporality(temporality),
DataPoints: []*metricpb.ExponentialHistogramDataPoint{point},
},
},
}
return m, nil
}

func exponentialHistogramBucket(b aggregation.ExponentialBuckets) *metricpb.ExponentialHistogramDataPoint_Buckets {
cnts := make([]uint64, b.Len())
for i := range cnts {
cnts[i] = b.At(uint32(i))
}
return &metricpb.ExponentialHistogramDataPoint_Buckets{
Offset: b.Offset(),
BucketCounts: cnts,
}
}
165 changes: 165 additions & 0 deletions exporters/otlp/otlpmetric/internal/metrictransform/metric_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/aggregator/exponential"
"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
"go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
"go.opentelemetry.io/otel/sdk/metric/export"
Expand Down Expand Up @@ -321,3 +322,167 @@ func TestRecordAggregatorUnexpectedErrors(t *testing.T) {
require.Nil(t, mpb)
require.True(t, errors.Is(err, errEx))
}

func TestExponentialHistogramDataPoints(t *testing.T) {
type testCase struct {
name string
values []float64
temporality aggregation.Temporality
numberKind number.Kind
expect *metricpb.ExponentialHistogram
}
useAttrs := []attribute.KeyValue{
attribute.String("one", "1"),
}
expectAttrs := []*commonpb.KeyValue{
{Key: "one", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_StringValue{StringValue: "1"}}},
}

for _, test := range []testCase{
{
"empty",
[]float64{},
aggregation.DeltaTemporality,
number.Float64Kind,
&metricpb.ExponentialHistogram{
AggregationTemporality: otelDelta,
DataPoints: []*metricpb.ExponentialHistogramDataPoint{{
Attributes: expectAttrs,
StartTimeUnixNano: uint64(intervalStart.UnixNano()),
TimeUnixNano: uint64(intervalEnd.UnixNano()),
Count: 0,
ZeroCount: 0,
Sum: 0,
}},
},
},
{
"positive",
[]float64{1, 2, 4, 8},
aggregation.DeltaTemporality,
number.Float64Kind,
&metricpb.ExponentialHistogram{
AggregationTemporality: otelDelta,
DataPoints: []*metricpb.ExponentialHistogramDataPoint{{
Attributes: expectAttrs,
StartTimeUnixNano: uint64(intervalStart.UnixNano()),
TimeUnixNano: uint64(intervalEnd.UnixNano()),

// 1..8 spans 3 orders of magnitide, max-size 2, thus scale=-1
Scale: -1,
Count: 4,
ZeroCount: 0,
Sum: 15,
Positive: &metricpb.ExponentialHistogramDataPoint_Buckets{
Offset: 0,
BucketCounts: []uint64{2, 2},
},
}},
},
},
{
"positive_and_negative",
[]float64{2, 3, -100},
aggregation.DeltaTemporality,
number.Float64Kind,
&metricpb.ExponentialHistogram{
AggregationTemporality: otelDelta,
DataPoints: []*metricpb.ExponentialHistogramDataPoint{{
Attributes: expectAttrs,
StartTimeUnixNano: uint64(intervalStart.UnixNano()),
TimeUnixNano: uint64(intervalEnd.UnixNano()),

// Scale 1 has boundaries at 1, sqrt(2), 2, 2*sqrt(2), ...
Scale: 1,
Count: 3,
ZeroCount: 0,
Sum: -95,
Positive: &metricpb.ExponentialHistogramDataPoint_Buckets{
// Index 2 => 2, Index 3 => 2*sqrt(2)
Offset: 2,
BucketCounts: []uint64{1, 1},
},
Negative: &metricpb.ExponentialHistogramDataPoint_Buckets{
// Index 13 = 2^floor(13/2) * sqrt(2) ~= 90
Offset: 13,
BucketCounts: []uint64{1},
},
}},
},
},
{
// Note: (2**(2**-10))**-100 = 0.9345
// Note: (2**(2**-10))**-101 = 0.9339
// Note: (2**(2**-10))**-102 = 0.9333
"negative and zero",
[]float64{-0.9343, -0.9342, -0.9341, -0.9338, -0.9337, -0.9336, 0, 0, 0, 0},
aggregation.DeltaTemporality,
number.Float64Kind,
&metricpb.ExponentialHistogram{
AggregationTemporality: otelDelta,
DataPoints: []*metricpb.ExponentialHistogramDataPoint{{
Attributes: expectAttrs,
StartTimeUnixNano: uint64(intervalStart.UnixNano()),
TimeUnixNano: uint64(intervalEnd.UnixNano()),

// Scale 1 has boundaries at 1, sqrt(2), 2, 2*sqrt(2), ...
Scale: 10,
Count: 10,
ZeroCount: 4,
Sum: -0.9343 + -0.9342 + -0.9341 + -0.9338 + -0.9337 + -0.9336,
Negative: &metricpb.ExponentialHistogramDataPoint_Buckets{
Offset: -102,
BucketCounts: []uint64{3, 3},
},
}},
},
},
{
"integers cumulative",
// Scale=-1 has base 4,
// index 0 holds values [1, 4), has count 2
// index 1 holds values [4, 15), has count 12
[]float64{2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
aggregation.CumulativeTemporality,
number.Float64Kind,
&metricpb.ExponentialHistogram{
AggregationTemporality: otelCumulative,
DataPoints: []*metricpb.ExponentialHistogramDataPoint{{
Attributes: expectAttrs,
StartTimeUnixNano: uint64(intervalStart.UnixNano()),
TimeUnixNano: uint64(intervalEnd.UnixNano()),
Count: 14,
ZeroCount: 0,
Sum: 119,
Scale: -1,
Positive: &metricpb.ExponentialHistogramDataPoint_Buckets{
Offset: 0,
BucketCounts: []uint64{2, 12},
},
}},
},
},
} {
t.Run(test.name, func(t *testing.T) {
desc := metrictest.NewDescriptor("ignore", sdkapi.HistogramInstrumentKind, test.numberKind)
labels := attribute.NewSet(useAttrs...)
agg := &exponential.New(1, &desc, exponential.WithMaxSize(2))[0]

for _, value := range test.values {
var num number.Number
if test.numberKind == number.Float64Kind {
num = number.NewFloat64Number(value)
} else {
num = number.NewInt64Number(int64(value))
}
assert.NoError(t, agg.Update(context.Background(), num, &desc))
}

record := export.NewRecord(&desc, &labels, agg, intervalStart, intervalEnd)

if m, err := exponentialHistogramPoint(record, test.temporality, agg); assert.NoError(t, err) {
assert.Equal(t, test.expect, m.GetExponentialHistogram())
}
})
}
}
27 changes: 27 additions & 0 deletions sdk/export/metric/aggregation/aggregation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package aggregation

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestAggregationKind(t *testing.T) {
require.Equal(t, "Sum", SumKind.String())
require.Equal(t, "Lastvalue", LastValueKind.String())
require.Equal(t, "Histogram", HistogramKind.String())
}
1 change: 1 addition & 0 deletions sdk/export/metric/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ replace go.opentelemetry.io/otel/sdk/metric => ../../metric
replace go.opentelemetry.io/otel/trace => ../../../trace

require (
github.com/stretchr/testify v1.7.0
go.opentelemetry.io/otel v1.3.0
go.opentelemetry.io/otel/metric v0.26.0
go.opentelemetry.io/otel/sdk/metric v0.0.0-00010101000000-000000000000
Expand Down
1 change: 1 addition & 0 deletions sdk/export/metric/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading