From 4659f5dc62cd03ede13d5ac5804e543ed9fe4abc Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Fri, 20 Sep 2024 16:56:06 -0700 Subject: [PATCH 1/8] Add support for .NET 9 Advice API --- .../Metrics/MetricStreamIdentity.cs | 48 ++++++- .../Metrics/MetricViewTests.cs | 124 ++++++++++++++++++ 2 files changed, 171 insertions(+), 1 deletion(-) diff --git a/src/OpenTelemetry/Metrics/MetricStreamIdentity.cs b/src/OpenTelemetry/Metrics/MetricStreamIdentity.cs index 8ac0e8e10cf..a9918fb6eaa 100644 --- a/src/OpenTelemetry/Metrics/MetricStreamIdentity.cs +++ b/src/OpenTelemetry/Metrics/MetricStreamIdentity.cs @@ -22,7 +22,7 @@ public MetricStreamIdentity(Instrument instrument, MetricStreamConfiguration? me this.ViewId = metricStreamConfiguration?.ViewId; this.MetricStreamName = $"{this.MeterName}.{this.MeterVersion}.{this.InstrumentName}"; this.TagKeys = metricStreamConfiguration?.CopiedTagKeys; - this.HistogramBucketBounds = (metricStreamConfiguration as ExplicitBucketHistogramConfiguration)?.CopiedBoundaries; + this.HistogramBucketBounds = GetExplicitBucketHistogramBounds(instrument, metricStreamConfiguration); this.ExponentialHistogramMaxSize = (metricStreamConfiguration as Base2ExponentialBucketHistogramConfiguration)?.MaxSize ?? 0; this.ExponentialHistogramMaxScale = (metricStreamConfiguration as Base2ExponentialBucketHistogramConfiguration)?.MaxScale ?? 0; this.HistogramRecordMinMax = (metricStreamConfiguration as HistogramConfiguration)?.RecordMinMax ?? true; @@ -150,6 +150,52 @@ public bool Equals(MetricStreamIdentity other) public override readonly int GetHashCode() => this.hashCode; + private static double[]? GetExplicitBucketHistogramBounds(Instrument instrument, MetricStreamConfiguration? metricStreamConfiguration) + { + if (metricStreamConfiguration is ExplicitBucketHistogramConfiguration explicitBucketHistogramConfiguration + && explicitBucketHistogramConfiguration.CopiedBoundaries != null) + { + return explicitBucketHistogramConfiguration.CopiedBoundaries; + } + + return instrument switch + { + Histogram longHistogram => GetExplicitBucketHistogramBoundsFromAdvice(longHistogram), + Histogram intHistogram => GetExplicitBucketHistogramBoundsFromAdvice(intHistogram), + Histogram shortHistogram => GetExplicitBucketHistogramBoundsFromAdvice(shortHistogram), + Histogram byteHistogram => GetExplicitBucketHistogramBoundsFromAdvice(byteHistogram), + Histogram floatHistogram => GetExplicitBucketHistogramBoundsFromAdvice(floatHistogram), + Histogram doubleHistogram => GetExplicitBucketHistogramBoundsFromAdvice(doubleHistogram), + _ => null, + }; + } + + private static double[]? GetExplicitBucketHistogramBoundsFromAdvice(Histogram histogram) + where T : struct + { + var adviceExplicitBucketBoundaries = histogram.Advice?.HistogramBucketBoundaries; + if (adviceExplicitBucketBoundaries == null) + { + return null; + } + + if (typeof(T) == typeof(double)) + { + return ((IReadOnlyList)adviceExplicitBucketBoundaries).ToArray(); + } + else + { + double[] explicitBucketBoundaries = new double[adviceExplicitBucketBoundaries.Count]; + + for (int i = 0; i < adviceExplicitBucketBoundaries.Count; i++) + { + explicitBucketBoundaries[i] = Convert.ToDouble(adviceExplicitBucketBoundaries[i]); + } + + return explicitBucketBoundaries; + } + } + private static bool HistogramBoundsEqual(double[]? bounds1, double[]? bounds2) { if (ReferenceEquals(bounds1, bounds2)) diff --git a/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs b/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs index c1a0fca281b..590026364aa 100644 --- a/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs +++ b/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs @@ -570,6 +570,130 @@ public void ViewToProduceCustomHistogramBound() Assert.Equal(boundaries.Length + 1, actualCount); } + [Fact] + public void AdviceToProduceCustomHistogramBound() + { + using var meter = new Meter(Utils.GetCurrentMethodName()); + var exportedItems = new List(); + IReadOnlyList boundaries = new List() { 10, 20 }; + + using var container = this.BuildMeterProvider(out var meterProvider, builder => builder + .AddMeter(meter.Name) + .AddInMemoryExporter(exportedItems)); + + var histogram = meter.CreateHistogram( + "MyHistogram", + unit: null, + description: null, + tags: null, + new() + { + HistogramBucketBoundaries = boundaries, + }); + histogram.Record(-10); + histogram.Record(0); + histogram.Record(1); + histogram.Record(9); + histogram.Record(10); + histogram.Record(11); + histogram.Record(19); + meterProvider.ForceFlush(MaxTimeToAllowForFlush); + Assert.Single(exportedItems); + var metricCustom = exportedItems[0]; + + Assert.Equal("MyHistogram", metricCustom.Name); + + List metricPointsCustom = new List(); + foreach (ref readonly var mp in metricCustom.GetMetricPoints()) + { + metricPointsCustom.Add(mp); + } + + Assert.Single(metricPointsCustom); + var histogramPoint = metricPointsCustom[0]; + + var count = histogramPoint.GetHistogramCount(); + var sum = histogramPoint.GetHistogramSum(); + + Assert.Equal(40, sum); + Assert.Equal(7, count); + + var index = 0; + var actualCount = 0; + var expectedBucketCounts = new long[] { 5, 2, 0 }; + foreach (var histogramMeasurement in histogramPoint.GetHistogramBuckets()) + { + Assert.Equal(expectedBucketCounts[index], histogramMeasurement.BucketCount); + index++; + actualCount++; + } + + Assert.Equal(boundaries.Count + 1, actualCount); + } + + [Fact] + public void ViewTakesPrecedenceOverAdviceToProduceCustomHistogramBound() + { + using var meter = new Meter(Utils.GetCurrentMethodName()); + var exportedItems = new List(); + IReadOnlyList adviceBoundaries = new List() { 5, 10, 20 }; + var viewBoundaries = new double[] { 10, 20 }; + + using var container = this.BuildMeterProvider(out var meterProvider, builder => builder + .AddMeter(meter.Name) + .AddView("MyHistogram", new ExplicitBucketHistogramConfiguration() { Boundaries = viewBoundaries }) + .AddInMemoryExporter(exportedItems)); + + var histogram = meter.CreateHistogram( + "MyHistogram", + unit: null, + description: null, + tags: null, + new() + { + HistogramBucketBoundaries = adviceBoundaries, + }); + histogram.Record(-10); + histogram.Record(0); + histogram.Record(1); + histogram.Record(9); + histogram.Record(10); + histogram.Record(11); + histogram.Record(19); + meterProvider.ForceFlush(MaxTimeToAllowForFlush); + Assert.Single(exportedItems); + var metricCustom = exportedItems[0]; + + Assert.Equal("MyHistogram", metricCustom.Name); + + List metricPointsCustom = new List(); + foreach (ref readonly var mp in metricCustom.GetMetricPoints()) + { + metricPointsCustom.Add(mp); + } + + Assert.Single(metricPointsCustom); + var histogramPoint = metricPointsCustom[0]; + + var count = histogramPoint.GetHistogramCount(); + var sum = histogramPoint.GetHistogramSum(); + + Assert.Equal(40, sum); + Assert.Equal(7, count); + + var index = 0; + var actualCount = 0; + var expectedBucketCounts = new long[] { 5, 2, 0 }; + foreach (var histogramMeasurement in histogramPoint.GetHistogramBuckets()) + { + Assert.Equal(expectedBucketCounts[index], histogramMeasurement.BucketCount); + index++; + actualCount++; + } + + Assert.Equal(viewBoundaries.Length + 1, actualCount); + } + [Fact] public void ViewToProduceExponentialHistogram() { From 6c337a5a1d02ee5703fe2a3810dedca20d5609fd Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Fri, 20 Sep 2024 18:26:18 -0700 Subject: [PATCH 2/8] fix lint --- test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs b/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs index 590026364aa..9f1588dbe57 100644 --- a/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs +++ b/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs @@ -587,7 +587,7 @@ public void AdviceToProduceCustomHistogramBound() description: null, tags: null, new() - { + { HistogramBucketBoundaries = boundaries, }); histogram.Record(-10); From 862764328bcc9afba0327b708a8fa15e0c7a6812 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Mon, 23 Sep 2024 12:30:29 -0700 Subject: [PATCH 3/8] test changes --- .../Metrics/MetricViewTests.cs | 287 ++++++++++-------- 1 file changed, 167 insertions(+), 120 deletions(-) diff --git a/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs b/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs index 9f1588dbe57..15285fd2983 100644 --- a/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs +++ b/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs @@ -570,128 +570,175 @@ public void ViewToProduceCustomHistogramBound() Assert.Equal(boundaries.Length + 1, actualCount); } - [Fact] - public void AdviceToProduceCustomHistogramBound() - { - using var meter = new Meter(Utils.GetCurrentMethodName()); - var exportedItems = new List(); - IReadOnlyList boundaries = new List() { 10, 20 }; - - using var container = this.BuildMeterProvider(out var meterProvider, builder => builder - .AddMeter(meter.Name) - .AddInMemoryExporter(exportedItems)); - - var histogram = meter.CreateHistogram( - "MyHistogram", - unit: null, - description: null, - tags: null, - new() - { - HistogramBucketBoundaries = boundaries, - }); - histogram.Record(-10); - histogram.Record(0); - histogram.Record(1); - histogram.Record(9); - histogram.Record(10); - histogram.Record(11); - histogram.Record(19); - meterProvider.ForceFlush(MaxTimeToAllowForFlush); - Assert.Single(exportedItems); - var metricCustom = exportedItems[0]; - - Assert.Equal("MyHistogram", metricCustom.Name); - - List metricPointsCustom = new List(); - foreach (ref readonly var mp in metricCustom.GetMetricPoints()) - { - metricPointsCustom.Add(mp); - } - - Assert.Single(metricPointsCustom); - var histogramPoint = metricPointsCustom[0]; - - var count = histogramPoint.GetHistogramCount(); - var sum = histogramPoint.GetHistogramSum(); - - Assert.Equal(40, sum); - Assert.Equal(7, count); - - var index = 0; - var actualCount = 0; - var expectedBucketCounts = new long[] { 5, 2, 0 }; - foreach (var histogramMeasurement in histogramPoint.GetHistogramBuckets()) - { - Assert.Equal(expectedBucketCounts[index], histogramMeasurement.BucketCount); - index++; - actualCount++; - } - - Assert.Equal(boundaries.Count + 1, actualCount); + [Fact] + public void HistogramWithAdviceBoundaries_HandlesAllTypes() + { + using var meter = new Meter(Utils.GetCurrentMethodName()); + var exportedItems = new List(); + int counter = 0; + + using var container = this.BuildMeterProvider(out var meterProvider, builder => + { + builder.AddMeter(meter.Name); + builder.AddInMemoryExporter(exportedItems); + }); + + // Test cases for different histogram types + var histograms = new Instrument[] + { + meter.CreateHistogram("longHistogram", unit: null, description: null, tags: null, new() { HistogramBucketBoundaries = new List() { 10, 20 } }), + meter.CreateHistogram("intHistogram", unit: null, description: null, tags: null, new() { HistogramBucketBoundaries = new List() { 10, 20 } }), + meter.CreateHistogram("shortHistogram", unit: null, description: null, tags: null, new() { HistogramBucketBoundaries = new List() { 10, 20 } }), + meter.CreateHistogram("floatHistogram", unit: null, description: null, tags: null, new() { HistogramBucketBoundaries = new List() { 10.0F, 20.0F } }), + meter.CreateHistogram("doubleHistogram", unit: null, description: null, tags: null, new() { HistogramBucketBoundaries = new List() { 10.0, 20.0 } }), + }; + + foreach (var histogram in histograms) + { + exportedItems.Clear(); + + if (histogram is Histogram longHistogram) + { + longHistogram.Record(-10); + longHistogram.Record(9); + longHistogram.Record(19); + } + else if (histogram is Histogram intHistogram) + { + intHistogram.Record(-10); + intHistogram.Record(9); + intHistogram.Record(19); + counter++; + } + else if (histogram is Histogram shortHistogram) + { + shortHistogram.Record(-10); + shortHistogram.Record(9); + shortHistogram.Record(19); + counter++; + } + else if (histogram is Histogram floatHistogram) + { + floatHistogram.Record(-10.0F); + floatHistogram.Record(9.0F); + floatHistogram.Record(19.0F); + counter++; + } + else if (histogram is Histogram doubleHistogram) + { + doubleHistogram.Record(-10.0); + doubleHistogram.Record(9.0); + doubleHistogram.Record(19.0); + counter++; + } + + meterProvider.ForceFlush(MaxTimeToAllowForFlush); + var metricCustom = exportedItems[counter]; + + List metricPointsCustom = new List(); + foreach (ref readonly var mp in metricCustom.GetMetricPoints()) + { + metricPointsCustom.Add(mp); + } + + Assert.Single(metricPointsCustom); + var histogramPoint = metricPointsCustom[0]; + + var count = histogramPoint.GetHistogramCount(); + var sum = histogramPoint.GetHistogramSum(); + + Assert.Equal(18, sum); + Assert.Equal(3, count); + + var index = 0; + var actualCount = 0; + long[] expectedBucketCounts = new long[] { 2, 1, 0 }; + + foreach (var histogramMeasurement in histogramPoint.GetHistogramBuckets()) + { + Assert.Equal(expectedBucketCounts[index], histogramMeasurement.BucketCount); + index++; + actualCount++; + } + + Assert.Equal(3, actualCount); + } } - [Fact] - public void ViewTakesPrecedenceOverAdviceToProduceCustomHistogramBound() - { - using var meter = new Meter(Utils.GetCurrentMethodName()); - var exportedItems = new List(); - IReadOnlyList adviceBoundaries = new List() { 5, 10, 20 }; - var viewBoundaries = new double[] { 10, 20 }; - - using var container = this.BuildMeterProvider(out var meterProvider, builder => builder - .AddMeter(meter.Name) - .AddView("MyHistogram", new ExplicitBucketHistogramConfiguration() { Boundaries = viewBoundaries }) - .AddInMemoryExporter(exportedItems)); - - var histogram = meter.CreateHistogram( - "MyHistogram", - unit: null, - description: null, - tags: null, - new() - { - HistogramBucketBoundaries = adviceBoundaries, - }); - histogram.Record(-10); - histogram.Record(0); - histogram.Record(1); - histogram.Record(9); - histogram.Record(10); - histogram.Record(11); - histogram.Record(19); - meterProvider.ForceFlush(MaxTimeToAllowForFlush); - Assert.Single(exportedItems); - var metricCustom = exportedItems[0]; - - Assert.Equal("MyHistogram", metricCustom.Name); - - List metricPointsCustom = new List(); - foreach (ref readonly var mp in metricCustom.GetMetricPoints()) - { - metricPointsCustom.Add(mp); - } - - Assert.Single(metricPointsCustom); - var histogramPoint = metricPointsCustom[0]; - - var count = histogramPoint.GetHistogramCount(); - var sum = histogramPoint.GetHistogramSum(); - - Assert.Equal(40, sum); - Assert.Equal(7, count); - - var index = 0; - var actualCount = 0; - var expectedBucketCounts = new long[] { 5, 2, 0 }; - foreach (var histogramMeasurement in histogramPoint.GetHistogramBuckets()) - { - Assert.Equal(expectedBucketCounts[index], histogramMeasurement.BucketCount); - index++; - actualCount++; - } - - Assert.Equal(viewBoundaries.Length + 1, actualCount); + [Theory] + [InlineData(false)] + [InlineData(true)] + public void HistogramWithAdviceBoundariesSpecifiedTests(bool useViewToOverride) + { + using var meter = new Meter(Utils.GetCurrentMethodName()); + var exportedItems = new List(); + IReadOnlyList adviceBoundaries = new List() { 5, 10, 20 }; + double[] viewBoundaries = new double[] { 10, 20 }; + + using var container = this.BuildMeterProvider(out var meterProvider, builder => + { + builder.AddMeter(meter.Name); + + if (useViewToOverride) + { + builder.AddView("MyHistogram", new ExplicitBucketHistogramConfiguration { Boundaries = viewBoundaries }); + } + + builder.AddInMemoryExporter(exportedItems); + }); + + var histogram = meter.CreateHistogram( + "MyHistogram", + unit: null, + description: null, + tags: null, + new() + { + HistogramBucketBoundaries = adviceBoundaries, + }); + + histogram.Record(-10); + histogram.Record(0); + histogram.Record(1); + histogram.Record(9); + histogram.Record(10); + histogram.Record(11); + histogram.Record(19); + histogram.Record(22); + + meterProvider.ForceFlush(MaxTimeToAllowForFlush); + Assert.Single(exportedItems); + var metricCustom = exportedItems[0]; + + Assert.Equal("MyHistogram", metricCustom.Name); + + List metricPointsCustom = new List(); + foreach (ref readonly var mp in metricCustom.GetMetricPoints()) + { + metricPointsCustom.Add(mp); + } + + Assert.Single(metricPointsCustom); + var histogramPoint = metricPointsCustom[0]; + + var count = histogramPoint.GetHistogramCount(); + var sum = histogramPoint.GetHistogramSum(); + + Assert.Equal(62, sum); + Assert.Equal(8, count); + + var index = 0; + var actualCount = 0; + long[] expectedBucketCounts = useViewToOverride ? new long[] { 5, 2, 1 } : new long[] { 3, 2, 2, 1 }; + + foreach (var histogramMeasurement in histogramPoint.GetHistogramBuckets()) + { + Assert.Equal(expectedBucketCounts[index], histogramMeasurement.BucketCount); + index++; + actualCount++; + } + + Assert.Equal(useViewToOverride ? viewBoundaries.Length + 1 : adviceBoundaries.Count + 1, actualCount); } [Fact] From fc0f7a23afa3e978e502dcf993ae50f21b76d25d Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Mon, 23 Sep 2024 12:33:19 -0700 Subject: [PATCH 4/8] lint fix --- .../Metrics/MetricViewTests.cs | 334 +++++++++--------- 1 file changed, 167 insertions(+), 167 deletions(-) diff --git a/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs b/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs index 15285fd2983..11c048512ef 100644 --- a/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs +++ b/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs @@ -570,175 +570,175 @@ public void ViewToProduceCustomHistogramBound() Assert.Equal(boundaries.Length + 1, actualCount); } - [Fact] - public void HistogramWithAdviceBoundaries_HandlesAllTypes() - { - using var meter = new Meter(Utils.GetCurrentMethodName()); - var exportedItems = new List(); - int counter = 0; - - using var container = this.BuildMeterProvider(out var meterProvider, builder => - { - builder.AddMeter(meter.Name); - builder.AddInMemoryExporter(exportedItems); - }); - - // Test cases for different histogram types - var histograms = new Instrument[] - { - meter.CreateHistogram("longHistogram", unit: null, description: null, tags: null, new() { HistogramBucketBoundaries = new List() { 10, 20 } }), - meter.CreateHistogram("intHistogram", unit: null, description: null, tags: null, new() { HistogramBucketBoundaries = new List() { 10, 20 } }), - meter.CreateHistogram("shortHistogram", unit: null, description: null, tags: null, new() { HistogramBucketBoundaries = new List() { 10, 20 } }), - meter.CreateHistogram("floatHistogram", unit: null, description: null, tags: null, new() { HistogramBucketBoundaries = new List() { 10.0F, 20.0F } }), - meter.CreateHistogram("doubleHistogram", unit: null, description: null, tags: null, new() { HistogramBucketBoundaries = new List() { 10.0, 20.0 } }), - }; - - foreach (var histogram in histograms) - { - exportedItems.Clear(); - - if (histogram is Histogram longHistogram) - { - longHistogram.Record(-10); - longHistogram.Record(9); - longHistogram.Record(19); - } - else if (histogram is Histogram intHistogram) - { - intHistogram.Record(-10); - intHistogram.Record(9); - intHistogram.Record(19); - counter++; - } - else if (histogram is Histogram shortHistogram) - { - shortHistogram.Record(-10); - shortHistogram.Record(9); - shortHistogram.Record(19); - counter++; - } - else if (histogram is Histogram floatHistogram) - { - floatHistogram.Record(-10.0F); - floatHistogram.Record(9.0F); - floatHistogram.Record(19.0F); - counter++; - } - else if (histogram is Histogram doubleHistogram) - { - doubleHistogram.Record(-10.0); - doubleHistogram.Record(9.0); - doubleHistogram.Record(19.0); - counter++; - } - - meterProvider.ForceFlush(MaxTimeToAllowForFlush); - var metricCustom = exportedItems[counter]; - - List metricPointsCustom = new List(); - foreach (ref readonly var mp in metricCustom.GetMetricPoints()) - { - metricPointsCustom.Add(mp); - } - - Assert.Single(metricPointsCustom); - var histogramPoint = metricPointsCustom[0]; - - var count = histogramPoint.GetHistogramCount(); - var sum = histogramPoint.GetHistogramSum(); - - Assert.Equal(18, sum); - Assert.Equal(3, count); - - var index = 0; - var actualCount = 0; - long[] expectedBucketCounts = new long[] { 2, 1, 0 }; - - foreach (var histogramMeasurement in histogramPoint.GetHistogramBuckets()) - { - Assert.Equal(expectedBucketCounts[index], histogramMeasurement.BucketCount); - index++; - actualCount++; - } - - Assert.Equal(3, actualCount); - } + [Fact] + public void HistogramWithAdviceBoundaries_HandlesAllTypes() + { + using var meter = new Meter(Utils.GetCurrentMethodName()); + var exportedItems = new List(); + int counter = 0; + + using var container = this.BuildMeterProvider(out var meterProvider, builder => + { + builder.AddMeter(meter.Name); + builder.AddInMemoryExporter(exportedItems); + }); + + // Test cases for different histogram types + var histograms = new Instrument[] + { + meter.CreateHistogram("longHistogram", unit: null, description: null, tags: null, new() { HistogramBucketBoundaries = new List() { 10, 20 } }), + meter.CreateHistogram("intHistogram", unit: null, description: null, tags: null, new() { HistogramBucketBoundaries = new List() { 10, 20 } }), + meter.CreateHistogram("shortHistogram", unit: null, description: null, tags: null, new() { HistogramBucketBoundaries = new List() { 10, 20 } }), + meter.CreateHistogram("floatHistogram", unit: null, description: null, tags: null, new() { HistogramBucketBoundaries = new List() { 10.0F, 20.0F } }), + meter.CreateHistogram("doubleHistogram", unit: null, description: null, tags: null, new() { HistogramBucketBoundaries = new List() { 10.0, 20.0 } }), + }; + + foreach (var histogram in histograms) + { + exportedItems.Clear(); + + if (histogram is Histogram longHistogram) + { + longHistogram.Record(-10); + longHistogram.Record(9); + longHistogram.Record(19); + } + else if (histogram is Histogram intHistogram) + { + intHistogram.Record(-10); + intHistogram.Record(9); + intHistogram.Record(19); + counter++; + } + else if (histogram is Histogram shortHistogram) + { + shortHistogram.Record(-10); + shortHistogram.Record(9); + shortHistogram.Record(19); + counter++; + } + else if (histogram is Histogram floatHistogram) + { + floatHistogram.Record(-10.0F); + floatHistogram.Record(9.0F); + floatHistogram.Record(19.0F); + counter++; + } + else if (histogram is Histogram doubleHistogram) + { + doubleHistogram.Record(-10.0); + doubleHistogram.Record(9.0); + doubleHistogram.Record(19.0); + counter++; + } + + meterProvider.ForceFlush(MaxTimeToAllowForFlush); + var metricCustom = exportedItems[counter]; + + List metricPointsCustom = new List(); + foreach (ref readonly var mp in metricCustom.GetMetricPoints()) + { + metricPointsCustom.Add(mp); + } + + Assert.Single(metricPointsCustom); + var histogramPoint = metricPointsCustom[0]; + + var count = histogramPoint.GetHistogramCount(); + var sum = histogramPoint.GetHistogramSum(); + + Assert.Equal(18, sum); + Assert.Equal(3, count); + + var index = 0; + var actualCount = 0; + long[] expectedBucketCounts = new long[] { 2, 1, 0 }; + + foreach (var histogramMeasurement in histogramPoint.GetHistogramBuckets()) + { + Assert.Equal(expectedBucketCounts[index], histogramMeasurement.BucketCount); + index++; + actualCount++; + } + + Assert.Equal(3, actualCount); + } } - [Theory] - [InlineData(false)] - [InlineData(true)] - public void HistogramWithAdviceBoundariesSpecifiedTests(bool useViewToOverride) - { - using var meter = new Meter(Utils.GetCurrentMethodName()); - var exportedItems = new List(); - IReadOnlyList adviceBoundaries = new List() { 5, 10, 20 }; - double[] viewBoundaries = new double[] { 10, 20 }; - - using var container = this.BuildMeterProvider(out var meterProvider, builder => - { - builder.AddMeter(meter.Name); - - if (useViewToOverride) - { - builder.AddView("MyHistogram", new ExplicitBucketHistogramConfiguration { Boundaries = viewBoundaries }); - } - - builder.AddInMemoryExporter(exportedItems); - }); - - var histogram = meter.CreateHistogram( - "MyHistogram", - unit: null, - description: null, - tags: null, - new() - { - HistogramBucketBoundaries = adviceBoundaries, - }); - - histogram.Record(-10); - histogram.Record(0); - histogram.Record(1); - histogram.Record(9); - histogram.Record(10); - histogram.Record(11); - histogram.Record(19); - histogram.Record(22); - - meterProvider.ForceFlush(MaxTimeToAllowForFlush); - Assert.Single(exportedItems); - var metricCustom = exportedItems[0]; - - Assert.Equal("MyHistogram", metricCustom.Name); - - List metricPointsCustom = new List(); - foreach (ref readonly var mp in metricCustom.GetMetricPoints()) - { - metricPointsCustom.Add(mp); - } - - Assert.Single(metricPointsCustom); - var histogramPoint = metricPointsCustom[0]; - - var count = histogramPoint.GetHistogramCount(); - var sum = histogramPoint.GetHistogramSum(); - - Assert.Equal(62, sum); - Assert.Equal(8, count); - - var index = 0; - var actualCount = 0; - long[] expectedBucketCounts = useViewToOverride ? new long[] { 5, 2, 1 } : new long[] { 3, 2, 2, 1 }; - - foreach (var histogramMeasurement in histogramPoint.GetHistogramBuckets()) - { - Assert.Equal(expectedBucketCounts[index], histogramMeasurement.BucketCount); - index++; - actualCount++; - } - - Assert.Equal(useViewToOverride ? viewBoundaries.Length + 1 : adviceBoundaries.Count + 1, actualCount); + [Theory] + [InlineData(false)] + [InlineData(true)] + public void HistogramWithAdviceBoundariesSpecifiedTests(bool useViewToOverride) + { + using var meter = new Meter(Utils.GetCurrentMethodName()); + var exportedItems = new List(); + IReadOnlyList adviceBoundaries = new List() { 5, 10, 20 }; + double[] viewBoundaries = new double[] { 10, 20 }; + + using var container = this.BuildMeterProvider(out var meterProvider, builder => + { + builder.AddMeter(meter.Name); + + if (useViewToOverride) + { + builder.AddView("MyHistogram", new ExplicitBucketHistogramConfiguration { Boundaries = viewBoundaries }); + } + + builder.AddInMemoryExporter(exportedItems); + }); + + var histogram = meter.CreateHistogram( + "MyHistogram", + unit: null, + description: null, + tags: null, + new() + { + HistogramBucketBoundaries = adviceBoundaries, + }); + + histogram.Record(-10); + histogram.Record(0); + histogram.Record(1); + histogram.Record(9); + histogram.Record(10); + histogram.Record(11); + histogram.Record(19); + histogram.Record(22); + + meterProvider.ForceFlush(MaxTimeToAllowForFlush); + Assert.Single(exportedItems); + var metricCustom = exportedItems[0]; + + Assert.Equal("MyHistogram", metricCustom.Name); + + List metricPointsCustom = new List(); + foreach (ref readonly var mp in metricCustom.GetMetricPoints()) + { + metricPointsCustom.Add(mp); + } + + Assert.Single(metricPointsCustom); + var histogramPoint = metricPointsCustom[0]; + + var count = histogramPoint.GetHistogramCount(); + var sum = histogramPoint.GetHistogramSum(); + + Assert.Equal(62, sum); + Assert.Equal(8, count); + + var index = 0; + var actualCount = 0; + long[] expectedBucketCounts = useViewToOverride ? new long[] { 5, 2, 1 } : new long[] { 3, 2, 2, 1 }; + + foreach (var histogramMeasurement in histogramPoint.GetHistogramBuckets()) + { + Assert.Equal(expectedBucketCounts[index], histogramMeasurement.BucketCount); + index++; + actualCount++; + } + + Assert.Equal(useViewToOverride ? viewBoundaries.Length + 1 : adviceBoundaries.Count + 1, actualCount); } [Fact] From 32a93ead3c5ce8f121db29a66a772690b3ea576a Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Mon, 23 Sep 2024 13:49:01 -0700 Subject: [PATCH 5/8] Update changelog --- src/OpenTelemetry/CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index 1e802f55fd1..98dd4600688 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -16,6 +16,15 @@ Notes](../../RELEASENOTES.md). `Microsoft.Extensions.Diagnostics.Abstractions` packages version to `9.0.0-rc.1.24431.7`. ([#5853](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5853)) + +* Added support in metrics for histogram bucket boundaries set via the .NET 9 + [InstrumentAdvice](https://learn.microsoft.com/dotnet/api/system.diagnostics.metrics.instrumentadvice-1) + API. Explicit bucket histogram boundary resolution applies in the following + order: + 1. View API + 2. Advice API + 3. SDK defaults. + ([#5854](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5854)) ## 1.9.0 From 90747e9f28f04006cc7687163191454b49a361c5 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 23 Sep 2024 13:52:45 -0700 Subject: [PATCH 6/8] Update src/OpenTelemetry/CHANGELOG.md --- src/OpenTelemetry/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index 98dd4600688..670da258e6b 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -18,7 +18,7 @@ Notes](../../RELEASENOTES.md). ([#5853](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5853)) * Added support in metrics for histogram bucket boundaries set via the .NET 9 - [InstrumentAdvice](https://learn.microsoft.com/dotnet/api/system.diagnostics.metrics.instrumentadvice-1) + [InstrumentAdvice<T>](https://learn.microsoft.com/dotnet/api/system.diagnostics.metrics.instrumentadvice-1) API. Explicit bucket histogram boundary resolution applies in the following order: 1. View API From 40747b9f18bbb65c6643970efff2fa8de003200c Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Mon, 23 Sep 2024 14:04:30 -0700 Subject: [PATCH 7/8] Update src/OpenTelemetry/CHANGELOG.md --- src/OpenTelemetry/CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index 670da258e6b..2060e1ad9d5 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -16,7 +16,6 @@ Notes](../../RELEASENOTES.md). `Microsoft.Extensions.Diagnostics.Abstractions` packages version to `9.0.0-rc.1.24431.7`. ([#5853](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5853)) - * Added support in metrics for histogram bucket boundaries set via the .NET 9 [InstrumentAdvice<T>](https://learn.microsoft.com/dotnet/api/system.diagnostics.metrics.instrumentadvice-1) API. Explicit bucket histogram boundary resolution applies in the following From 0dec04c35784b3e3a375d529f54ab95d475d2b43 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 23 Sep 2024 15:21:46 -0700 Subject: [PATCH 8/8] CHANGELOG tweaks. --- src/OpenTelemetry/CHANGELOG.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index 2060e1ad9d5..47b6536a5e5 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -16,14 +16,20 @@ Notes](../../RELEASENOTES.md). `Microsoft.Extensions.Diagnostics.Abstractions` packages version to `9.0.0-rc.1.24431.7`. ([#5853](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5853)) + * Added support in metrics for histogram bucket boundaries set via the .NET 9 [InstrumentAdvice<T>](https://learn.microsoft.com/dotnet/api/system.diagnostics.metrics.instrumentadvice-1) - API. Explicit bucket histogram boundary resolution applies in the following - order: + API. + + Note: With this change explicit bucket histogram boundary resolution will + apply in the following order: + 1. View API 2. Advice API - 3. SDK defaults. - ([#5854](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5854)) + 3. SDK defaults + + See [#5854](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5854) + for details. ## 1.9.0