You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2026-06-03 18:35:08 +02:00
Exponential Histogram Datatypes (#4165)
* Adds the Exponential histogram data type. * Changelog * Updated comments * Apply suggestions from code review Co-authored-by: Robert Pająk <pellared@hotmail.com> Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com> * Split Exponential Buckets into it's own type --------- Co-authored-by: Robert Pająk <pellared@hotmail.com> Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
This commit is contained in:
@@ -137,6 +137,74 @@ type HistogramDataPoint[N int64 | float64] struct {
|
||||
Exemplars []Exemplar[N] `json:",omitempty"`
|
||||
}
|
||||
|
||||
// ExponentialHistogram represents the histogram of all measurements of values from an instrument.
|
||||
type ExponentialHistogram[N int64 | float64] struct {
|
||||
// DataPoints are the individual aggregated measurements with unique
|
||||
// attributes.
|
||||
DataPoints []ExponentialHistogramDataPoint[N]
|
||||
// Temporality describes if the aggregation is reported as the change from the
|
||||
// last report time, or the cumulative changes since a fixed start time.
|
||||
Temporality Temporality
|
||||
}
|
||||
|
||||
func (ExponentialHistogram[N]) privateAggregation() {}
|
||||
|
||||
// ExponentialHistogramDataPoint is a single exponential histogram data point in a timeseries.
|
||||
type ExponentialHistogramDataPoint[N int64 | float64] struct {
|
||||
// Attributes is the set of key value pairs that uniquely identify the
|
||||
// timeseries.
|
||||
Attributes attribute.Set
|
||||
// StartTime is when the timeseries was started.
|
||||
StartTime time.Time
|
||||
// Time is the time when the timeseries was recorded.
|
||||
Time time.Time
|
||||
|
||||
// Count is the number of updates this histogram has been calculated with.
|
||||
Count uint64
|
||||
// Min is the minimum value recorded. (optional)
|
||||
Min Extrema[N]
|
||||
// Max is the maximum value recorded. (optional)
|
||||
Max Extrema[N]
|
||||
// Sum is the sum of the values recorded.
|
||||
Sum N
|
||||
|
||||
// Scale describes the resolution of the histogram. Boundaries are
|
||||
// located at powers of the base, where:
|
||||
//
|
||||
// base = 2 ^ (2 ^ -Scale)
|
||||
Scale int32
|
||||
// ZeroCount is the number of values whose absolute value
|
||||
// is less than or equal to [ZeroThreshold].
|
||||
// When ZeroThreshold is 0, this is the number of values that
|
||||
// cannot be expressed using the standard exponential formula
|
||||
// as well as values that have been rounded to zero.
|
||||
// ZeroCount represents the special zero count bucket.
|
||||
ZeroCount uint64
|
||||
|
||||
// PositiveBucket is range of positive value bucket counts.
|
||||
PositiveBucket ExponentialBucket
|
||||
// NegativeBucket is range of negative value bucket counts.
|
||||
NegativeBucket ExponentialBucket
|
||||
|
||||
// ZeroThreshold is the width of the zero region. Where the zero region is
|
||||
// defined as the closed interval [-ZeroThreshold, ZeroThreshold].
|
||||
ZeroThreshold float64
|
||||
|
||||
// Exemplars is the sampled Exemplars collected during the timeseries.
|
||||
Exemplars []Exemplar[N] `json:",omitempty"`
|
||||
}
|
||||
|
||||
// ExponentialBucket are a set of bucket counts, encoded in a contiguous array
|
||||
// of counts.
|
||||
type ExponentialBucket struct {
|
||||
// Offset is the bucket index of the first entry in the Counts slice.
|
||||
Offset int32
|
||||
// Counts is an slice where Counts[i] carries the count of the bucket at
|
||||
// index (Offset+i). Counts[i] is the count of values greater than
|
||||
// base^(Offset+i) and less than or equal to base^(Offset+i+1).
|
||||
Counts []uint64
|
||||
}
|
||||
|
||||
// Extrema is the minimum or maximum value of a dataset.
|
||||
type Extrema[N int64 | float64] struct {
|
||||
value N
|
||||
|
||||
@@ -42,7 +42,12 @@ type Datatypes interface {
|
||||
metricdata.Sum[float64] |
|
||||
metricdata.Sum[int64] |
|
||||
metricdata.Exemplar[float64] |
|
||||
metricdata.Exemplar[int64]
|
||||
metricdata.Exemplar[int64] |
|
||||
metricdata.ExponentialHistogram[float64] |
|
||||
metricdata.ExponentialHistogram[int64] |
|
||||
metricdata.ExponentialHistogramDataPoint[float64] |
|
||||
metricdata.ExponentialHistogramDataPoint[int64] |
|
||||
metricdata.ExponentialBucket
|
||||
|
||||
// Interface types are not allowed in union types, therefore the
|
||||
// Aggregation and Value type from metricdata are not included here.
|
||||
@@ -134,6 +139,16 @@ func AssertEqual[T Datatypes](t *testing.T, expected, actual T, opts ...Option)
|
||||
r = equalSums(e, aIface.(metricdata.Sum[int64]), cfg)
|
||||
case metricdata.Sum[float64]:
|
||||
r = equalSums(e, aIface.(metricdata.Sum[float64]), cfg)
|
||||
case metricdata.ExponentialHistogram[float64]:
|
||||
r = equalExponentialHistograms(e, aIface.(metricdata.ExponentialHistogram[float64]), cfg)
|
||||
case metricdata.ExponentialHistogram[int64]:
|
||||
r = equalExponentialHistograms(e, aIface.(metricdata.ExponentialHistogram[int64]), cfg)
|
||||
case metricdata.ExponentialHistogramDataPoint[float64]:
|
||||
r = equalExponentialHistogramDataPoints(e, aIface.(metricdata.ExponentialHistogramDataPoint[float64]), cfg)
|
||||
case metricdata.ExponentialHistogramDataPoint[int64]:
|
||||
r = equalExponentialHistogramDataPoints(e, aIface.(metricdata.ExponentialHistogramDataPoint[int64]), cfg)
|
||||
case metricdata.ExponentialBucket:
|
||||
r = equalExponentialBuckets(e, aIface.(metricdata.ExponentialBucket), cfg)
|
||||
default:
|
||||
// We control all types passed to this, panic to signal developers
|
||||
// early they changed things in an incompatible way.
|
||||
@@ -198,6 +213,16 @@ func AssertHasAttributes[T Datatypes](t *testing.T, actual T, attrs ...attribute
|
||||
reasons = hasAttributesScopeMetrics(e, attrs...)
|
||||
case metricdata.ResourceMetrics:
|
||||
reasons = hasAttributesResourceMetrics(e, attrs...)
|
||||
case metricdata.ExponentialHistogram[int64]:
|
||||
reasons = hasAttributesExponentialHistogram(e, attrs...)
|
||||
case metricdata.ExponentialHistogram[float64]:
|
||||
reasons = hasAttributesExponentialHistogram(e, attrs...)
|
||||
case metricdata.ExponentialHistogramDataPoint[int64]:
|
||||
reasons = hasAttributesExponentialHistogramDataPoints(e, attrs...)
|
||||
case metricdata.ExponentialHistogramDataPoint[float64]:
|
||||
reasons = hasAttributesExponentialHistogramDataPoints(e, attrs...)
|
||||
case metricdata.ExponentialBucket:
|
||||
// Nothing to check.
|
||||
default:
|
||||
// We control all types passed to this, panic to signal developers
|
||||
// early they changed things in an incompatible way.
|
||||
|
||||
@@ -205,6 +205,103 @@ var (
|
||||
Exemplars: []metricdata.Exemplar[float64]{exemplarFloat64C},
|
||||
}
|
||||
|
||||
exponentialBucket2 = metricdata.ExponentialBucket{
|
||||
Offset: 2,
|
||||
Counts: []uint64{1, 1},
|
||||
}
|
||||
exponentialBucket3 = metricdata.ExponentialBucket{
|
||||
Offset: 3,
|
||||
Counts: []uint64{1, 1},
|
||||
}
|
||||
exponentialBucket4 = metricdata.ExponentialBucket{
|
||||
Offset: 4,
|
||||
Counts: []uint64{1, 1, 1},
|
||||
}
|
||||
exponentialBucket5 = metricdata.ExponentialBucket{
|
||||
Offset: 5,
|
||||
Counts: []uint64{1, 1, 1},
|
||||
}
|
||||
exponentialHistogramDataPointInt64A = metricdata.ExponentialHistogramDataPoint[int64]{
|
||||
Attributes: attrA,
|
||||
StartTime: startA,
|
||||
Time: endA,
|
||||
Count: 5,
|
||||
Min: minInt64A,
|
||||
Sum: 2,
|
||||
Scale: 1,
|
||||
ZeroCount: 1,
|
||||
PositiveBucket: exponentialBucket3,
|
||||
NegativeBucket: exponentialBucket2,
|
||||
Exemplars: []metricdata.Exemplar[int64]{exemplarInt64A},
|
||||
}
|
||||
exponentialHistogramDataPointFloat64A = metricdata.ExponentialHistogramDataPoint[float64]{
|
||||
Attributes: attrA,
|
||||
StartTime: startA,
|
||||
Time: endA,
|
||||
Count: 5,
|
||||
Min: minFloat64A,
|
||||
Sum: 2,
|
||||
Scale: 1,
|
||||
ZeroCount: 1,
|
||||
PositiveBucket: exponentialBucket3,
|
||||
NegativeBucket: exponentialBucket2,
|
||||
Exemplars: []metricdata.Exemplar[float64]{exemplarFloat64A},
|
||||
}
|
||||
exponentialHistogramDataPointInt64B = metricdata.ExponentialHistogramDataPoint[int64]{
|
||||
Attributes: attrB,
|
||||
StartTime: startB,
|
||||
Time: endB,
|
||||
Count: 6,
|
||||
Min: minInt64B,
|
||||
Max: maxInt64B,
|
||||
Sum: 3,
|
||||
Scale: 2,
|
||||
ZeroCount: 3,
|
||||
PositiveBucket: exponentialBucket4,
|
||||
NegativeBucket: exponentialBucket5,
|
||||
Exemplars: []metricdata.Exemplar[int64]{exemplarInt64B},
|
||||
}
|
||||
exponentialHistogramDataPointFloat64B = metricdata.ExponentialHistogramDataPoint[float64]{
|
||||
Attributes: attrB,
|
||||
StartTime: startB,
|
||||
Time: endB,
|
||||
Count: 6,
|
||||
Min: minFloat64B,
|
||||
Max: maxFloat64B,
|
||||
Sum: 3,
|
||||
Scale: 2,
|
||||
ZeroCount: 3,
|
||||
PositiveBucket: exponentialBucket4,
|
||||
NegativeBucket: exponentialBucket5,
|
||||
Exemplars: []metricdata.Exemplar[float64]{exemplarFloat64B},
|
||||
}
|
||||
exponentialHistogramDataPointInt64C = metricdata.ExponentialHistogramDataPoint[int64]{
|
||||
Attributes: attrA,
|
||||
StartTime: startB,
|
||||
Time: endB,
|
||||
Count: 5,
|
||||
Min: minInt64C,
|
||||
Sum: 2,
|
||||
Scale: 1,
|
||||
ZeroCount: 1,
|
||||
PositiveBucket: exponentialBucket3,
|
||||
NegativeBucket: exponentialBucket2,
|
||||
Exemplars: []metricdata.Exemplar[int64]{exemplarInt64C},
|
||||
}
|
||||
exponentialHistogramDataPointFloat64C = metricdata.ExponentialHistogramDataPoint[float64]{
|
||||
Attributes: attrA,
|
||||
StartTime: startB,
|
||||
Time: endB,
|
||||
Count: 5,
|
||||
Min: minFloat64A,
|
||||
Sum: 2,
|
||||
Scale: 1,
|
||||
ZeroCount: 1,
|
||||
PositiveBucket: exponentialBucket3,
|
||||
NegativeBucket: exponentialBucket2,
|
||||
Exemplars: []metricdata.Exemplar[float64]{exemplarFloat64C},
|
||||
}
|
||||
|
||||
gaugeInt64A = metricdata.Gauge[int64]{
|
||||
DataPoints: []metricdata.DataPoint[int64]{dataPointInt64A},
|
||||
}
|
||||
@@ -280,6 +377,31 @@ var (
|
||||
DataPoints: []metricdata.HistogramDataPoint[float64]{histogramDataPointFloat64C},
|
||||
}
|
||||
|
||||
exponentialHistogramInt64A = metricdata.ExponentialHistogram[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.ExponentialHistogramDataPoint[int64]{exponentialHistogramDataPointInt64A},
|
||||
}
|
||||
exponentialHistogramFloat64A = metricdata.ExponentialHistogram[float64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.ExponentialHistogramDataPoint[float64]{exponentialHistogramDataPointFloat64A},
|
||||
}
|
||||
exponentialHistogramInt64B = metricdata.ExponentialHistogram[int64]{
|
||||
Temporality: metricdata.DeltaTemporality,
|
||||
DataPoints: []metricdata.ExponentialHistogramDataPoint[int64]{exponentialHistogramDataPointInt64B},
|
||||
}
|
||||
exponentialHistogramFloat64B = metricdata.ExponentialHistogram[float64]{
|
||||
Temporality: metricdata.DeltaTemporality,
|
||||
DataPoints: []metricdata.ExponentialHistogramDataPoint[float64]{exponentialHistogramDataPointFloat64B},
|
||||
}
|
||||
exponentialHistogramInt64C = metricdata.ExponentialHistogram[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.ExponentialHistogramDataPoint[int64]{exponentialHistogramDataPointInt64C},
|
||||
}
|
||||
exponentialHistogramFloat64C = metricdata.ExponentialHistogram[float64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
DataPoints: []metricdata.ExponentialHistogramDataPoint[float64]{exponentialHistogramDataPointFloat64C},
|
||||
}
|
||||
|
||||
metricsA = metricdata.Metrics{
|
||||
Name: "A",
|
||||
Description: "A desc",
|
||||
@@ -378,6 +500,11 @@ func TestAssertEqual(t *testing.T) {
|
||||
t.Run("ExtremaFloat64", testDatatype(minFloat64A, minFloat64B, equalExtrema[float64]))
|
||||
t.Run("ExemplarInt64", testDatatype(exemplarInt64A, exemplarInt64B, equalExemplars[int64]))
|
||||
t.Run("ExemplarFloat64", testDatatype(exemplarFloat64A, exemplarFloat64B, equalExemplars[float64]))
|
||||
t.Run("ExponentialHistogramInt64", testDatatype(exponentialHistogramInt64A, exponentialHistogramInt64B, equalExponentialHistograms[int64]))
|
||||
t.Run("ExponentialHistogramFloat64", testDatatype(exponentialHistogramFloat64A, exponentialHistogramFloat64B, equalExponentialHistograms[float64]))
|
||||
t.Run("ExponentialHistogramDataPointInt64", testDatatype(exponentialHistogramDataPointInt64A, exponentialHistogramDataPointInt64B, equalExponentialHistogramDataPoints[int64]))
|
||||
t.Run("ExponentialHistogramDataPointFloat64", testDatatype(exponentialHistogramDataPointFloat64A, exponentialHistogramDataPointFloat64B, equalExponentialHistogramDataPoints[float64]))
|
||||
t.Run("ExponentialBuckets", testDatatype(exponentialBucket2, exponentialBucket3, equalExponentialBuckets))
|
||||
}
|
||||
|
||||
func TestAssertEqualIgnoreTime(t *testing.T) {
|
||||
@@ -398,6 +525,10 @@ func TestAssertEqualIgnoreTime(t *testing.T) {
|
||||
t.Run("ExtremaFloat64", testDatatypeIgnoreTime(minFloat64A, minFloat64C, equalExtrema[float64]))
|
||||
t.Run("ExemplarInt64", testDatatypeIgnoreTime(exemplarInt64A, exemplarInt64C, equalExemplars[int64]))
|
||||
t.Run("ExemplarFloat64", testDatatypeIgnoreTime(exemplarFloat64A, exemplarFloat64C, equalExemplars[float64]))
|
||||
t.Run("ExponentialHistogramInt64", testDatatypeIgnoreTime(exponentialHistogramInt64A, exponentialHistogramInt64C, equalExponentialHistograms[int64]))
|
||||
t.Run("ExponentialHistogramFloat64", testDatatypeIgnoreTime(exponentialHistogramFloat64A, exponentialHistogramFloat64C, equalExponentialHistograms[float64]))
|
||||
t.Run("ExponentialHistogramDataPointInt64", testDatatypeIgnoreTime(exponentialHistogramDataPointInt64A, exponentialHistogramDataPointInt64C, equalExponentialHistogramDataPoints[int64]))
|
||||
t.Run("ExponentialHistogramDataPointFloat64", testDatatypeIgnoreTime(exponentialHistogramDataPointFloat64A, exponentialHistogramDataPointFloat64C, equalExponentialHistogramDataPoints[float64]))
|
||||
}
|
||||
|
||||
func TestAssertEqualIgnoreExemplars(t *testing.T) {
|
||||
@@ -416,6 +547,14 @@ func TestAssertEqualIgnoreExemplars(t *testing.T) {
|
||||
dpFloat64 := dataPointFloat64A
|
||||
dpFloat64.Exemplars = []metricdata.Exemplar[float64]{exemplarFloat64B}
|
||||
t.Run("DataPointFloat64", testDatatypeIgnoreExemplars(dataPointFloat64A, dpFloat64, equalDataPoints[float64]))
|
||||
|
||||
ehdpInt64 := exponentialHistogramDataPointInt64A
|
||||
ehdpInt64.Exemplars = []metricdata.Exemplar[int64]{exemplarInt64B}
|
||||
t.Run("ExponentialHistogramDataPointInt64", testDatatypeIgnoreExemplars(exponentialHistogramDataPointInt64A, ehdpInt64, equalExponentialHistogramDataPoints[int64]))
|
||||
|
||||
ehdpFloat64 := exponentialHistogramDataPointFloat64A
|
||||
ehdpFloat64.Exemplars = []metricdata.Exemplar[float64]{exemplarFloat64B}
|
||||
t.Run("ExponentialHistogramDataPointFloat64", testDatatypeIgnoreExemplars(exponentialHistogramDataPointFloat64A, ehdpFloat64, equalExponentialHistogramDataPoints[float64]))
|
||||
}
|
||||
|
||||
type unknownAggregation struct {
|
||||
@@ -430,6 +569,8 @@ func TestAssertAggregationsEqual(t *testing.T) {
|
||||
AssertAggregationsEqual(t, gaugeFloat64A, gaugeFloat64A)
|
||||
AssertAggregationsEqual(t, histogramInt64A, histogramInt64A)
|
||||
AssertAggregationsEqual(t, histogramFloat64A, histogramFloat64A)
|
||||
AssertAggregationsEqual(t, exponentialHistogramInt64A, exponentialHistogramInt64A)
|
||||
AssertAggregationsEqual(t, exponentialHistogramFloat64A, exponentialHistogramFloat64A)
|
||||
|
||||
r := equalAggregations(sumInt64A, nil, config{})
|
||||
assert.Len(t, r, 1, "should return nil comparison mismatch only")
|
||||
@@ -475,6 +616,18 @@ func TestAssertAggregationsEqual(t *testing.T) {
|
||||
|
||||
r = equalAggregations(histogramFloat64A, histogramFloat64C, config{ignoreTimestamp: true})
|
||||
assert.Len(t, r, 0, "histograms should be equal: %v", r)
|
||||
|
||||
r = equalAggregations(exponentialHistogramInt64A, exponentialHistogramInt64B, config{})
|
||||
assert.Greaterf(t, len(r), 0, "exponential histograms should not be equal: %v == %v", exponentialHistogramInt64A, exponentialHistogramInt64B)
|
||||
|
||||
r = equalAggregations(exponentialHistogramInt64A, exponentialHistogramInt64C, config{ignoreTimestamp: true})
|
||||
assert.Len(t, r, 0, "exponential histograms should be equal: %v", r)
|
||||
|
||||
r = equalAggregations(exponentialHistogramFloat64A, exponentialHistogramFloat64B, config{})
|
||||
assert.Greaterf(t, len(r), 0, "exponential histograms should not be equal: %v == %v", exponentialHistogramFloat64A, exponentialHistogramFloat64B)
|
||||
|
||||
r = equalAggregations(exponentialHistogramFloat64A, exponentialHistogramFloat64C, config{ignoreTimestamp: true})
|
||||
assert.Len(t, r, 0, "exponential histograms should be equal: %v", r)
|
||||
}
|
||||
|
||||
func TestAssertAttributes(t *testing.T) {
|
||||
@@ -494,6 +647,11 @@ func TestAssertAttributes(t *testing.T) {
|
||||
AssertHasAttributes(t, metricsA, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, scopeMetricsA, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, resourceMetricsA, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, exponentialHistogramDataPointInt64A, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, exponentialHistogramDataPointFloat64A, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, exponentialHistogramInt64A, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, exponentialHistogramFloat64A, attribute.Bool("A", true))
|
||||
AssertHasAttributes(t, exponentialBucket2, attribute.Bool("A", true)) // No-op, always pass.
|
||||
|
||||
r := hasAttributesAggregation(gaugeInt64A, attribute.Bool("A", true))
|
||||
assert.Equal(t, len(r), 0, "gaugeInt64A has A=True")
|
||||
@@ -507,6 +665,10 @@ func TestAssertAttributes(t *testing.T) {
|
||||
assert.Equal(t, len(r), 0, "histogramInt64A has A=True")
|
||||
r = hasAttributesAggregation(histogramFloat64A, attribute.Bool("A", true))
|
||||
assert.Equal(t, len(r), 0, "histogramFloat64A has A=True")
|
||||
r = hasAttributesAggregation(exponentialHistogramInt64A, attribute.Bool("A", true))
|
||||
assert.Equal(t, len(r), 0, "exponentialHistogramInt64A has A=True")
|
||||
r = hasAttributesAggregation(exponentialHistogramFloat64A, attribute.Bool("A", true))
|
||||
assert.Equal(t, len(r), 0, "exponentialHistogramFloat64A has A=True")
|
||||
|
||||
r = hasAttributesAggregation(gaugeInt64A, attribute.Bool("A", false))
|
||||
assert.Greater(t, len(r), 0, "gaugeInt64A does not have A=False")
|
||||
@@ -520,6 +682,10 @@ func TestAssertAttributes(t *testing.T) {
|
||||
assert.Greater(t, len(r), 0, "histogramInt64A does not have A=False")
|
||||
r = hasAttributesAggregation(histogramFloat64A, attribute.Bool("A", false))
|
||||
assert.Greater(t, len(r), 0, "histogramFloat64A does not have A=False")
|
||||
r = hasAttributesAggregation(exponentialHistogramInt64A, attribute.Bool("A", false))
|
||||
assert.Greater(t, len(r), 0, "exponentialHistogramInt64A does not have A=False")
|
||||
r = hasAttributesAggregation(exponentialHistogramFloat64A, attribute.Bool("A", false))
|
||||
assert.Greater(t, len(r), 0, "exponentialHistogramFloat64A does not have A=False")
|
||||
|
||||
r = hasAttributesAggregation(gaugeInt64A, attribute.Bool("B", true))
|
||||
assert.Greater(t, len(r), 0, "gaugeInt64A does not have Attribute B")
|
||||
@@ -533,6 +699,10 @@ func TestAssertAttributes(t *testing.T) {
|
||||
assert.Greater(t, len(r), 0, "histogramIntA does not have Attribute B")
|
||||
r = hasAttributesAggregation(histogramFloat64A, attribute.Bool("B", true))
|
||||
assert.Greater(t, len(r), 0, "histogramFloatA does not have Attribute B")
|
||||
r = hasAttributesAggregation(exponentialHistogramInt64A, attribute.Bool("B", true))
|
||||
assert.Greater(t, len(r), 0, "exponentialHistogramIntA does not have Attribute B")
|
||||
r = hasAttributesAggregation(exponentialHistogramFloat64A, attribute.Bool("B", true))
|
||||
assert.Greater(t, len(r), 0, "exponentialHistogramFloatA does not have Attribute B")
|
||||
}
|
||||
|
||||
func TestAssertAttributesFail(t *testing.T) {
|
||||
@@ -553,6 +723,10 @@ func TestAssertAttributesFail(t *testing.T) {
|
||||
assert.False(t, AssertHasAttributes(fakeT, metricsA, attribute.Bool("B", true)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, resourceMetricsA, attribute.Bool("A", false)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, resourceMetricsA, attribute.Bool("B", true)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, exponentialHistogramDataPointInt64A, attribute.Bool("A", false)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, exponentialHistogramDataPointFloat64A, attribute.Bool("B", true)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, exponentialHistogramInt64A, attribute.Bool("A", false)))
|
||||
assert.False(t, AssertHasAttributes(fakeT, exponentialHistogramFloat64A, attribute.Bool("B", true)))
|
||||
|
||||
sum := metricdata.Sum[int64]{
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
|
||||
@@ -143,6 +143,18 @@ func equalAggregations(a, b metricdata.Aggregation, cfg config) (reasons []strin
|
||||
reasons = append(reasons, "Histogram not equal:")
|
||||
reasons = append(reasons, r...)
|
||||
}
|
||||
case metricdata.ExponentialHistogram[int64]:
|
||||
r := equalExponentialHistograms(v, b.(metricdata.ExponentialHistogram[int64]), cfg)
|
||||
if len(r) > 0 {
|
||||
reasons = append(reasons, "ExponentialHistogram not equal:")
|
||||
reasons = append(reasons, r...)
|
||||
}
|
||||
case metricdata.ExponentialHistogram[float64]:
|
||||
r := equalExponentialHistograms(v, b.(metricdata.ExponentialHistogram[float64]), cfg)
|
||||
if len(r) > 0 {
|
||||
reasons = append(reasons, "ExponentialHistogram not equal:")
|
||||
reasons = append(reasons, r...)
|
||||
}
|
||||
default:
|
||||
reasons = append(reasons, fmt.Sprintf("Aggregation of unknown types %T", a))
|
||||
}
|
||||
@@ -312,6 +324,103 @@ func equalHistogramDataPoints[N int64 | float64](a, b metricdata.HistogramDataPo
|
||||
return reasons
|
||||
}
|
||||
|
||||
// equalExponentialHistograms returns reasons exponential Histograms are not equal. If they are
|
||||
// equal, the returned reasons will be empty.
|
||||
//
|
||||
// The DataPoints each Histogram contains are compared based on containing the
|
||||
// same HistogramDataPoint, not the order they are stored in.
|
||||
func equalExponentialHistograms[N int64 | float64](a, b metricdata.ExponentialHistogram[N], cfg config) (reasons []string) {
|
||||
if a.Temporality != b.Temporality {
|
||||
reasons = append(reasons, notEqualStr("Temporality", a.Temporality, b.Temporality))
|
||||
}
|
||||
|
||||
r := compareDiff(diffSlices(
|
||||
a.DataPoints,
|
||||
b.DataPoints,
|
||||
func(a, b metricdata.ExponentialHistogramDataPoint[N]) bool {
|
||||
r := equalExponentialHistogramDataPoints(a, b, cfg)
|
||||
return len(r) == 0
|
||||
},
|
||||
))
|
||||
if r != "" {
|
||||
reasons = append(reasons, fmt.Sprintf("Histogram DataPoints not equal:\n%s", r))
|
||||
}
|
||||
return reasons
|
||||
}
|
||||
|
||||
// equalExponentialHistogramDataPoints returns reasons HistogramDataPoints are not equal.
|
||||
// If they are equal, the returned reasons will be empty.
|
||||
func equalExponentialHistogramDataPoints[N int64 | float64](a, b metricdata.ExponentialHistogramDataPoint[N], cfg config) (reasons []string) { // nolint: revive // Intentional internal control flag
|
||||
if !a.Attributes.Equals(&b.Attributes) {
|
||||
reasons = append(reasons, notEqualStr(
|
||||
"Attributes",
|
||||
a.Attributes.Encoded(attribute.DefaultEncoder()),
|
||||
b.Attributes.Encoded(attribute.DefaultEncoder()),
|
||||
))
|
||||
}
|
||||
if !cfg.ignoreTimestamp {
|
||||
if !a.StartTime.Equal(b.StartTime) {
|
||||
reasons = append(reasons, notEqualStr("StartTime", a.StartTime.UnixNano(), b.StartTime.UnixNano()))
|
||||
}
|
||||
if !a.Time.Equal(b.Time) {
|
||||
reasons = append(reasons, notEqualStr("Time", a.Time.UnixNano(), b.Time.UnixNano()))
|
||||
}
|
||||
}
|
||||
if a.Count != b.Count {
|
||||
reasons = append(reasons, notEqualStr("Count", a.Count, b.Count))
|
||||
}
|
||||
if !eqExtrema(a.Min, b.Min) {
|
||||
reasons = append(reasons, notEqualStr("Min", a.Min, b.Min))
|
||||
}
|
||||
if !eqExtrema(a.Max, b.Max) {
|
||||
reasons = append(reasons, notEqualStr("Max", a.Max, b.Max))
|
||||
}
|
||||
if a.Sum != b.Sum {
|
||||
reasons = append(reasons, notEqualStr("Sum", a.Sum, b.Sum))
|
||||
}
|
||||
|
||||
if a.Scale != b.Scale {
|
||||
reasons = append(reasons, notEqualStr("Scale", a.Scale, b.Scale))
|
||||
}
|
||||
if a.ZeroCount != b.ZeroCount {
|
||||
reasons = append(reasons, notEqualStr("ZeroCount", a.ZeroCount, b.ZeroCount))
|
||||
}
|
||||
|
||||
r := equalExponentialBuckets(a.PositiveBucket, b.PositiveBucket, cfg)
|
||||
if len(r) > 0 {
|
||||
reasons = append(reasons, r...)
|
||||
}
|
||||
r = equalExponentialBuckets(a.NegativeBucket, b.NegativeBucket, cfg)
|
||||
if len(r) > 0 {
|
||||
reasons = append(reasons, r...)
|
||||
}
|
||||
|
||||
if !cfg.ignoreExemplars {
|
||||
r := compareDiff(diffSlices(
|
||||
a.Exemplars,
|
||||
b.Exemplars,
|
||||
func(a, b metricdata.Exemplar[N]) bool {
|
||||
r := equalExemplars(a, b, cfg)
|
||||
return len(r) == 0
|
||||
},
|
||||
))
|
||||
if r != "" {
|
||||
reasons = append(reasons, fmt.Sprintf("Exemplars not equal:\n%s", r))
|
||||
}
|
||||
}
|
||||
return reasons
|
||||
}
|
||||
|
||||
func equalExponentialBuckets(a, b metricdata.ExponentialBucket, _ config) (reasons []string) {
|
||||
if a.Offset != b.Offset {
|
||||
reasons = append(reasons, notEqualStr("Offset", a.Offset, b.Offset))
|
||||
}
|
||||
if !equalSlices(a.Counts, b.Counts) {
|
||||
reasons = append(reasons, notEqualStr("Counts", a.Counts, b.Counts))
|
||||
}
|
||||
return reasons
|
||||
}
|
||||
|
||||
func notEqualStr(prefix string, expected, actual interface{}) string {
|
||||
return fmt.Sprintf("%s not equal:\nexpected: %v\nactual: %v", prefix, expected, actual)
|
||||
}
|
||||
@@ -557,6 +666,31 @@ func hasAttributesHistogram[T int64 | float64](histogram metricdata.Histogram[T]
|
||||
return reasons
|
||||
}
|
||||
|
||||
func hasAttributesExponentialHistogramDataPoints[T int64 | float64](dp metricdata.ExponentialHistogramDataPoint[T], attrs ...attribute.KeyValue) (reasons []string) {
|
||||
for _, attr := range attrs {
|
||||
val, ok := dp.Attributes.Value(attr.Key)
|
||||
if !ok {
|
||||
reasons = append(reasons, missingAttrStr(string(attr.Key)))
|
||||
continue
|
||||
}
|
||||
if val != attr.Value {
|
||||
reasons = append(reasons, notEqualStr(string(attr.Key), attr.Value.Emit(), val.Emit()))
|
||||
}
|
||||
}
|
||||
return reasons
|
||||
}
|
||||
|
||||
func hasAttributesExponentialHistogram[T int64 | float64](histogram metricdata.ExponentialHistogram[T], attrs ...attribute.KeyValue) (reasons []string) {
|
||||
for n, dp := range histogram.DataPoints {
|
||||
reas := hasAttributesExponentialHistogramDataPoints(dp, attrs...)
|
||||
if len(reas) > 0 {
|
||||
reasons = append(reasons, fmt.Sprintf("histogram datapoint %d attributes:\n", n))
|
||||
reasons = append(reasons, reas...)
|
||||
}
|
||||
}
|
||||
return reasons
|
||||
}
|
||||
|
||||
func hasAttributesAggregation(agg metricdata.Aggregation, attrs ...attribute.KeyValue) (reasons []string) {
|
||||
switch agg := agg.(type) {
|
||||
case metricdata.Gauge[int64]:
|
||||
@@ -571,6 +705,10 @@ func hasAttributesAggregation(agg metricdata.Aggregation, attrs ...attribute.Key
|
||||
reasons = hasAttributesHistogram(agg, attrs...)
|
||||
case metricdata.Histogram[float64]:
|
||||
reasons = hasAttributesHistogram(agg, attrs...)
|
||||
case metricdata.ExponentialHistogram[int64]:
|
||||
reasons = hasAttributesExponentialHistogram(agg, attrs...)
|
||||
case metricdata.ExponentialHistogram[float64]:
|
||||
reasons = hasAttributesExponentialHistogram(agg, attrs...)
|
||||
default:
|
||||
reasons = []string{fmt.Sprintf("unknown aggregation %T", agg)}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user