-
Notifications
You must be signed in to change notification settings - Fork 777
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
Add exponential histogram tests and in-memory exporter support #4303
Changes from all commits
b4ebf52
bf41884
2a86f96
4b85dae
2722955
e3c3a2d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,7 +20,6 @@ namespace OpenTelemetry.Metrics; | |
|
||
public sealed class ExponentialHistogramBuckets | ||
{ | ||
private int size; | ||
private long[] buckets = Array.Empty<long>(); | ||
|
||
internal ExponentialHistogramBuckets() | ||
|
@@ -29,7 +28,7 @@ internal ExponentialHistogramBuckets() | |
|
||
public int Offset { get; private set; } | ||
|
||
public Enumerator GetEnumerator() => new(this.size, this.buckets); | ||
public Enumerator GetEnumerator() => new(this.buckets); | ||
|
||
internal void SnapshotBuckets(CircularBufferBuckets buckets) | ||
{ | ||
|
@@ -39,24 +38,30 @@ internal void SnapshotBuckets(CircularBufferBuckets buckets) | |
} | ||
|
||
this.Offset = buckets.Offset; | ||
this.size = buckets.Size; | ||
buckets.Copy(this.buckets); | ||
} | ||
|
||
internal ExponentialHistogramBuckets Copy() | ||
{ | ||
var copy = new ExponentialHistogramBuckets(); | ||
copy.Offset = this.Offset; | ||
copy.buckets = new long[this.buckets.Length]; | ||
Array.Copy(this.buckets, copy.buckets, this.buckets.Length); | ||
return copy; | ||
} | ||
|
||
/// <summary> | ||
/// Enumerates the bucket counts of an exponential histogram. | ||
/// </summary> | ||
// Note: Does not implement IEnumerator<> to prevent accidental boxing. | ||
public struct Enumerator | ||
{ | ||
private readonly int size; | ||
private readonly long[] buckets; | ||
private int index; | ||
|
||
internal Enumerator(int size, long[] buckets) | ||
internal Enumerator(long[] buckets) | ||
{ | ||
this.index = size; | ||
this.size = size; | ||
this.index = 0; | ||
this.buckets = buckets; | ||
this.Current = default; | ||
} | ||
|
@@ -76,7 +81,7 @@ internal Enumerator(int size, long[] buckets) | |
/// collection.</returns> | ||
public bool MoveNext() | ||
{ | ||
if (this.index < this.size) | ||
if (this.index < this.buckets.Length) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this be size? We are now always going enumerate over the full length of the |
||
{ | ||
this.Current = this.buckets[this.index++]; | ||
return true; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -163,5 +163,104 @@ public void VerifySnapshot_Histogram() | |
Assert.Equal(2, snapshot2.MetricPoints[0].GetHistogramCount()); | ||
Assert.Equal(15, snapshot2.MetricPoints[0].GetHistogramSum()); | ||
} | ||
|
||
[Fact] | ||
public void VerifySnapshot_ExponentialHistogram() | ||
{ | ||
var expectedHistogram = new Base2ExponentialBucketHistogram(); | ||
var exportedMetrics = new List<Metric>(); | ||
var exportedSnapshots = new List<MetricSnapshot>(); | ||
|
||
using var meter = new Meter(Utils.GetCurrentMethodName()); | ||
var histogram = meter.CreateHistogram<int>("histogram"); | ||
using var meterProvider = Sdk.CreateMeterProviderBuilder() | ||
.AddMeter(meter.Name) | ||
.AddView("histogram", new Base2ExponentialBucketHistogramConfiguration()) | ||
.AddInMemoryExporter(exportedMetrics) | ||
.AddInMemoryExporter(exportedSnapshots) | ||
.Build(); | ||
|
||
// FIRST EXPORT | ||
expectedHistogram.Record(10); | ||
histogram.Record(10); | ||
meterProvider.ForceFlush(); | ||
|
||
// Verify Metric 1 | ||
Assert.Single(exportedMetrics); | ||
var metric1 = exportedMetrics[0]; | ||
var metricPoints1Enumerator = metric1.GetMetricPoints().GetEnumerator(); | ||
Assert.True(metricPoints1Enumerator.MoveNext()); | ||
ref readonly var metricPoint1 = ref metricPoints1Enumerator.Current; | ||
Assert.Equal(1, metricPoint1.GetHistogramCount()); | ||
Assert.Equal(10, metricPoint1.GetHistogramSum()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test for min and max as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, should probably take care of this on this PR. I noted a bug on the explicit bounds histograms where min/max is actually not captured currently in the snapshot, was gonna fix in another PR, but might just do it here since probably small. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Min/max is now tested... gotta take off for the evening, so will fix explicit in a follow up |
||
metricPoint1.TryGetHistogramMinMaxValues(out var min, out var max); | ||
Assert.Equal(10, min); | ||
Assert.Equal(10, max); | ||
AggregatorTest.AssertExponentialBucketsAreCorrect(expectedHistogram, metricPoint1.GetExponentialHistogramData()); | ||
|
||
// Verify Snapshot 1 | ||
Assert.Single(exportedSnapshots); | ||
var snapshot1 = exportedSnapshots[0]; | ||
Assert.Single(snapshot1.MetricPoints); | ||
Assert.Equal(1, snapshot1.MetricPoints[0].GetHistogramCount()); | ||
Assert.Equal(10, snapshot1.MetricPoints[0].GetHistogramSum()); | ||
snapshot1.MetricPoints[0].TryGetHistogramMinMaxValues(out min, out max); | ||
Assert.Equal(10, min); | ||
Assert.Equal(10, max); | ||
AggregatorTest.AssertExponentialBucketsAreCorrect(expectedHistogram, snapshot1.MetricPoints[0].GetExponentialHistogramData()); | ||
|
||
// Verify Metric == Snapshot | ||
Assert.Equal(metric1.Name, snapshot1.Name); | ||
Assert.Equal(metric1.Description, snapshot1.Description); | ||
Assert.Equal(metric1.Unit, snapshot1.Unit); | ||
Assert.Equal(metric1.MeterName, snapshot1.MeterName); | ||
Assert.Equal(metric1.MetricType, snapshot1.MetricType); | ||
Assert.Equal(metric1.MeterVersion, snapshot1.MeterVersion); | ||
|
||
// SECOND EXPORT | ||
expectedHistogram.Record(5); | ||
histogram.Record(5); | ||
meterProvider.ForceFlush(); | ||
|
||
// Verify Metric 1 after second export | ||
// This value is expected to be updated. | ||
Assert.Equal(2, metricPoint1.GetHistogramCount()); | ||
Assert.Equal(15, metricPoint1.GetHistogramSum()); | ||
metricPoint1.TryGetHistogramMinMaxValues(out min, out max); | ||
Assert.Equal(5, min); | ||
Assert.Equal(10, max); | ||
|
||
// Verify Metric 2 | ||
Assert.Equal(2, exportedMetrics.Count); | ||
var metric2 = exportedMetrics[1]; | ||
var metricPoints2Enumerator = metric2.GetMetricPoints().GetEnumerator(); | ||
Assert.True(metricPoints2Enumerator.MoveNext()); | ||
ref readonly var metricPoint2 = ref metricPoints2Enumerator.Current; | ||
Assert.Equal(2, metricPoint2.GetHistogramCount()); | ||
Assert.Equal(15, metricPoint2.GetHistogramSum()); | ||
metricPoint1.TryGetHistogramMinMaxValues(out min, out max); | ||
Assert.Equal(5, min); | ||
Assert.Equal(10, max); | ||
AggregatorTest.AssertExponentialBucketsAreCorrect(expectedHistogram, metricPoint2.GetExponentialHistogramData()); | ||
|
||
// Verify Snapshot 1 after second export | ||
// This value is expected to be unchanged. | ||
Assert.Equal(1, snapshot1.MetricPoints[0].GetHistogramCount()); | ||
Assert.Equal(10, snapshot1.MetricPoints[0].GetHistogramSum()); | ||
snapshot1.MetricPoints[0].TryGetHistogramMinMaxValues(out min, out max); | ||
Assert.Equal(10, min); | ||
Assert.Equal(10, max); | ||
|
||
// Verify Snapshot 2 | ||
Assert.Equal(2, exportedSnapshots.Count); | ||
var snapshot2 = exportedSnapshots[1]; | ||
Assert.Single(snapshot2.MetricPoints); | ||
Assert.Equal(2, snapshot2.MetricPoints[0].GetHistogramCount()); | ||
Assert.Equal(15, snapshot2.MetricPoints[0].GetHistogramSum()); | ||
snapshot2.MetricPoints[0].TryGetHistogramMinMaxValues(out min, out max); | ||
Assert.Equal(5, min); | ||
Assert.Equal(10, max); | ||
AggregatorTest.AssertExponentialBucketsAreCorrect(expectedHistogram, snapshot2.MetricPoints[0].GetExponentialHistogramData()); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copy
size
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, will fix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turns out this was actually more insidious... I removed
size
altogether. Size was initialized wrong... this was never testing anything correctly.This was a result of a refactor in my prior PR where I changed
ExponentialHistogramBuckets
from having a handle an instance ofCircularBufferBuckets
to simply along[]
.