Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
fb90431
[OpenTelemetry] Drop non-finite histogram values
martincostello Sep 15, 2026
a3ef8ad
[OpenTelemetry] Update CHANGELOG
martincostello Sep 15, 2026
47c5453
Merge branch 'main' into drop-nonfinite-from-views
martincostello Sep 16, 2026
7256ade
[OpenTelemetry] Address feedback
martincostello Sep 17, 2026
c8a54e9
[OpenTelemetry] Address feedback
martincostello Sep 17, 2026
09de2b6
Merge branch 'main' into drop-nonfinite-from-views
martincostello Sep 18, 2026
95b5cd3
Merge branch 'main' into drop-nonfinite-from-views
martincostello Oct 2, 2026
6a129f5
Merge branch 'main' into drop-nonfinite-from-views
martincostello Oct 7, 2026
fe7b759
Merge branch 'main' into drop-nonfinite-from-views
matt-hensley Oct 7, 2026
7baf43c
Merge branch 'main' into drop-nonfinite-from-views
martincostello Oct 20, 2026
013c29e
[OpenTelemetry] Update CHANGELOG
martincostello Oct 20, 2026
967485a
Merge branch 'main' into drop-nonfinite-from-views
martincostello Oct 21, 2026
c8178a7
Merge branch 'main' into drop-nonfinite-from-views
martincostello Oct 22, 2026
5c54bc9
Merge branch 'main' into drop-nonfinite-from-views
martincostello Oct 23, 2026
be996ce
Merge branch 'main' into drop-nonfinite-from-views
martincostello Oct 23, 2026
61704c6
Merge branch 'main' into drop-nonfinite-from-views
martincostello Oct 23, 2026
e5d2e6d
Merge branch 'main' into drop-nonfinite-from-views
martincostello Oct 26, 2026
3519e6d
Merge branch 'main' into drop-nonfinite-from-views
martincostello Oct 28, 2026
460fde0
Merge branch 'main' into drop-nonfinite-from-views
martincostello Oct 28, 2026
8a02a6f
[OpenTelemetry] Update CHANGELOG
martincostello Oct 28, 2026
e8cd0c4
Merge branch 'main' into drop-nonfinite-from-views
martincostello Oct 30, 2026
6372ee2
Merge branch 'main' into drop-nonfinite-from-views
martincostello Oct 30, 2026
8ff8ddf
[OpenTelemetry] Address feedback.
martincostello Nov 1, 2026
c6671f5
Merge branch 'main' into drop-nonfinite-from-views
martincostello Nov 2, 2026
5d18da4
Merge branch 'main' into drop-nonfinite-from-views
martincostello Nov 3, 2026
3a74d3c
Merge branch 'main' into drop-nonfinite-from-views
martincostello Nov 9, 2026
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
6 changes: 6 additions & 0 deletions src/OpenTelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ Notes](../../RELEASENOTES.md).
reset does not result in an unnecessary scale reduction.
([#7749](https://github.com/open-telemetry/opentelemetry-dotnet/pull/7749))

* Non-finite histogram measurements (`NaN`, `+Inf`, `-Inf`) are now dropped
rather than recorded, so they can no longer permanently corrupt the sum,
min, and max of a cumulative histogram stream. This aligns the explicit-bucket
and exponential histograms with the metrics SDK specification.
([#7726](https://github.com/open-telemetry/opentelemetry-dotnet/pull/7726))
Comment thread
martincostello marked this conversation as resolved.
Resolved

## 1.18.0

Released 2026-Aug-21
Expand Down
38 changes: 36 additions & 2 deletions src/OpenTelemetry/Metrics/MetricPoint/MetricPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,13 @@ internal void NullifyMetricPointState()

private void UpdateHistogram(double number, ReadOnlySpan<KeyValuePair<string, object?>> tags = default, bool offerExemplar = false)
{
if (double.IsNaN(number) || double.IsInfinity(number))
{
// Non-finite values would poison RunningSum for the lifetime of a cumulative stream.
this.CompleteUpdateWithoutMeasurement();
return;
Comment thread
martincostello marked this conversation as resolved.
Resolved
}

var histogramBuckets = this.mpComponents!.HistogramBuckets!;

this.mpComponents.AcquireLock();
Expand All @@ -966,6 +973,14 @@ private void UpdateHistogram(double number, ReadOnlySpan<KeyValuePair<string, ob

private void UpdateHistogramWithMinMax(double number, ReadOnlySpan<KeyValuePair<string, object?>> tags = default, bool offerExemplar = false)
{
if (double.IsNaN(number) || double.IsInfinity(number))
{
// Non-finite values would poison RunningSum/RunningMin/RunningMax for the lifetime of
// a cumulative stream.
this.CompleteUpdateWithoutMeasurement();
return;
}

var histogramBuckets = this.mpComponents!.HistogramBuckets!;

this.mpComponents.AcquireLock();
Expand All @@ -988,6 +1003,13 @@ private void UpdateHistogramWithMinMax(double number, ReadOnlySpan<KeyValuePair<

private void UpdateHistogramWithBuckets(double number, ReadOnlySpan<KeyValuePair<string, object?>> tags = default, bool offerExemplar = false)
{
if (double.IsNaN(number) || double.IsInfinity(number))
{
// Non-finite values would poison RunningSum for the lifetime of a cumulative stream.
this.CompleteUpdateWithoutMeasurement();
return;
}

var histogramBuckets = this.mpComponents!.HistogramBuckets!;

var bucketIndex = histogramBuckets.FindBucketIndex(number);
Expand All @@ -1010,6 +1032,14 @@ private void UpdateHistogramWithBuckets(double number, ReadOnlySpan<KeyValuePair

private void UpdateHistogramWithBucketsAndMinMax(double number, ReadOnlySpan<KeyValuePair<string, object?>> tags = default, bool offerExemplar = false)
{
if (double.IsNaN(number) || double.IsInfinity(number))
{
// Non-finite values would poison RunningSum/RunningMin/RunningMax for the lifetime of
// a cumulative stream.
this.CompleteUpdateWithoutMeasurement();
return;
}

var histogramBuckets = this.mpComponents!.HistogramBuckets!;

var bucketIndex = histogramBuckets.FindBucketIndex(number);
Expand All @@ -1035,8 +1065,10 @@ private void UpdateHistogramWithBucketsAndMinMax(double number, ReadOnlySpan<Key

private void UpdateBase2ExponentialHistogram(double number, ReadOnlySpan<KeyValuePair<string, object?>> tags = default, bool offerExemplar = false)
{
if (number < 0)
if (number < 0 || double.IsNaN(number) || double.IsInfinity(number))
{
// Negatives are undefined for exponential histograms; non-finite values would also
// poison RunningSum/RunningMin/RunningMax for the lifetime of a cumulative stream.
Comment thread
martincostello marked this conversation as resolved.
Resolved
this.CompleteUpdateWithoutMeasurement();
return;
}
Expand All @@ -1061,8 +1093,10 @@ private void UpdateBase2ExponentialHistogram(double number, ReadOnlySpan<KeyValu

private void UpdateBase2ExponentialHistogramWithMinMax(double number, ReadOnlySpan<KeyValuePair<string, object?>> tags = default, bool offerExemplar = false)
{
if (number < 0)
if (number < 0 || double.IsNaN(number) || double.IsInfinity(number))
{
// Negatives are undefined for exponential histograms; non-finite values would also
// poison RunningSum/RunningMin/RunningMax for the lifetime of a cumulative stream.
this.CompleteUpdateWithoutMeasurement();
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ test_histogram_bucket{otel_scope_name="HistogramInfinities",le="2500"} 1
test_histogram_bucket{otel_scope_name="HistogramInfinities",le="5000"} 1
test_histogram_bucket{otel_scope_name="HistogramInfinities",le="7500"} 1
test_histogram_bucket{otel_scope_name="HistogramInfinities",le="10000"} 1
test_histogram_bucket{otel_scope_name="HistogramInfinities",le="+Inf"} 3
test_histogram_sum{otel_scope_name="HistogramInfinities"} +Inf
test_histogram_count{otel_scope_name="HistogramInfinities"} 3
test_histogram_bucket{otel_scope_name="HistogramInfinities",le="+Inf"} 1
test_histogram_sum{otel_scope_name="HistogramInfinities"} 18
test_histogram_count{otel_scope_name="HistogramInfinities"} 1
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ test_histogram_bucket{otel_scope_name="HistogramNaN",le="2500"} 1
test_histogram_bucket{otel_scope_name="HistogramNaN",le="5000"} 1
test_histogram_bucket{otel_scope_name="HistogramNaN",le="7500"} 1
test_histogram_bucket{otel_scope_name="HistogramNaN",le="10000"} 1
test_histogram_bucket{otel_scope_name="HistogramNaN",le="+Inf"} 3
test_histogram_sum{otel_scope_name="HistogramNaN"} NaN
test_histogram_count{otel_scope_name="HistogramNaN"} 3
test_histogram_bucket{otel_scope_name="HistogramNaN",le="+Inf"} 1
test_histogram_sum{otel_scope_name="HistogramNaN"} 18
test_histogram_count{otel_scope_name="HistogramNaN"} 1
24 changes: 24 additions & 0 deletions test/OpenTelemetry.Tests/Metrics/AggregatorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,30 @@ public AggregatorTests()

public static TheoryData<HistogramBoundaryTestCase> HistogramInfinityBoundariesTestCases => HistogramBoundaryTestCase.HistogramInfinityBoundariesTestCases();

[Theory]
[InlineData(double.NaN)]
[InlineData(double.PositiveInfinity)]
[InlineData(double.NegativeInfinity)]
public void NonFiniteMeasurementIsIgnoredByCumulativeHistogram(double nonFinite)
{
var boundaries = new HistogramExplicitBounds(Metric.DefaultHistogramBounds);
var histogramPoint = new MetricPoint(this.aggregatorStore, AggregationType.HistogramWithBuckets, null, boundaries, Metric.DefaultExponentialHistogramMaxBuckets, Metric.DefaultExponentialHistogramMaxScale);
Comment thread
Copilot marked this conversation as resolved.
Resolved

histogramPoint.Update(1);
histogramPoint.Update(nonFinite);
histogramPoint.Update(2);
histogramPoint.Update(3);

// Cumulative snapshot (outputDelta: false) retains the running values.
histogramPoint.TakeSnapshot(false);

// The non-finite measurement is dropped: only the three finite values are counted...
Assert.Equal(3, histogramPoint.GetHistogramCount());

// ...and the sum is exactly 1 + 2 + 3, not poisoned to NaN/Infinity.
Assert.Equal(6, histogramPoint.GetHistogramSum());
}

[Fact]
public void HistogramDistributeToAllBucketsDefault()
{
Expand Down
3 changes: 1 addition & 2 deletions test/OpenTelemetry.Tests/Metrics/MetricTestData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ internal static class MetricTestData
public static TheoryData<double[], HistogramConfiguration, double, double> ValidHistogramMinMax => new()
{
{ [-10.0, 0.0, 1.0, 9.0, 10.0, 11.0, 19.0], new HistogramConfiguration(), -10.0, 19.0 },
{ [double.NegativeInfinity], new HistogramConfiguration(), double.NegativeInfinity, double.NegativeInfinity },
{ [double.NegativeInfinity, 0.0, double.PositiveInfinity], new HistogramConfiguration(), double.NegativeInfinity, double.PositiveInfinity },
{ [double.NegativeInfinity, 0.0, double.PositiveInfinity], new HistogramConfiguration(), 0.0, 0.0 },
Comment thread
martincostello marked this conversation as resolved.
Resolved
{ [1.0], new HistogramConfiguration(), 1.0, 1.0 },
{ [5.0, 100.0, 4.0, 101.0, -2.0, 97.0], new ExplicitBucketHistogramConfiguration { Boundaries = [10.0, 20.0] }, -2.0, 101.0 },
{ [5.0, 100.0, 4.0, 101.0, -2.0, 97.0], new Base2ExponentialBucketHistogramConfiguration(), 4.0, 101.0 },
Expand Down