From a6d0fe9189c371c3fd0b4cf4c5f5a23237df43f8 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Tue, 24 Sep 2024 12:04:01 -0700 Subject: [PATCH 1/9] Support synchronous gauge --- src/OpenTelemetry/Metrics/Metric.cs | 14 ++++++++++++++ src/OpenTelemetry/Metrics/Reader/MetricReader.cs | 1 + 2 files changed, 15 insertions(+) diff --git a/src/OpenTelemetry/Metrics/Metric.cs b/src/OpenTelemetry/Metrics/Metric.cs index 7801c2dd452..3bad252331c 100644 --- a/src/OpenTelemetry/Metrics/Metric.cs +++ b/src/OpenTelemetry/Metrics/Metric.cs @@ -116,6 +116,12 @@ internal Metric( { aggType = AggregationType.DoubleGauge; this.MetricType = MetricType.DoubleGauge; + } + else if (instrumentIdentity.InstrumentType == typeof(Gauge) + || instrumentIdentity.InstrumentType == typeof(Gauge)) + { + aggType = AggregationType.DoubleGauge; + this.MetricType = MetricType.DoubleGauge; } else if (instrumentIdentity.InstrumentType == typeof(ObservableGauge) || instrumentIdentity.InstrumentType == typeof(ObservableGauge) @@ -125,6 +131,14 @@ internal Metric( aggType = AggregationType.LongGauge; this.MetricType = MetricType.LongGauge; } + else if (instrumentIdentity.InstrumentType == typeof(Gauge) + || instrumentIdentity.InstrumentType == typeof(Gauge) + || instrumentIdentity.InstrumentType == typeof(Gauge) + || instrumentIdentity.InstrumentType == typeof(Gauge)) + { + aggType = AggregationType.LongGauge; + this.MetricType = MetricType.LongGauge; + } else if (instrumentIdentity.IsHistogram) { var explicitBucketBounds = instrumentIdentity.HistogramBucketBounds; diff --git a/src/OpenTelemetry/Metrics/Reader/MetricReader.cs b/src/OpenTelemetry/Metrics/Reader/MetricReader.cs index decbf8ad70d..9ab08332b91 100644 --- a/src/OpenTelemetry/Metrics/Reader/MetricReader.cs +++ b/src/OpenTelemetry/Metrics/Reader/MetricReader.cs @@ -27,6 +27,7 @@ public abstract partial class MetricReader : IDisposable // Temporality is not defined for gauges, so this does not really affect anything. var type when type == typeof(ObservableGauge<>) => AggregationTemporality.Delta, + var type when type == typeof(Gauge<>) => AggregationTemporality.Delta, var type when type == typeof(UpDownCounter<>) => AggregationTemporality.Cumulative, var type when type == typeof(ObservableUpDownCounter<>) => AggregationTemporality.Cumulative, From 73b6fbd38177a5be8b0cc927f8502ff53087b393 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Thu, 26 Sep 2024 20:54:03 -0700 Subject: [PATCH 2/9] update tests --- .../Metrics/MetricApiTestsBase.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs b/test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs index 41726da14d7..78dfdd9707c 100644 --- a/test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs +++ b/test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs @@ -1611,6 +1611,34 @@ public void UnsupportedMetricInstrument() meterProvider.ForceFlush(MaxTimeToAllowForFlush); Assert.Empty(exportedItems); + } + + [Fact] + public void GaugeIsExportedCorrectly() + { + var exportedItems = new List(); + + using var meter = new Meter($"{Utils.GetCurrentMethodName()}"); + + using var container = this.BuildMeterProvider(out var meterProvider, builder => builder + .AddMeter(meter.Name) + .AddInMemoryExporter(exportedItems)); + + var gauge = meter.CreateGauge(name: "NoiseLevel", unit: "dB", description: "Background Noise Level"); + gauge.Record(10); + meterProvider.ForceFlush(MaxTimeToAllowForFlush); + Assert.Single(exportedItems); + var metric = exportedItems[0]; + Assert.Equal("Background Noise Level", metric.Description); + List metricPoints = new List(); + foreach (ref readonly var mp in metric.GetMetricPoints()) + { + metricPoints.Add(mp); + } + + var histogramPoint = metricPoints[0]; + var lastValue = metricPoints[0].GetGaugeLastValueLong(); + Assert.Equal(10, lastValue); } internal static IConfiguration BuildConfiguration(bool emitOverflowAttribute, bool shouldReclaimUnusedMetricPoints) From 8f891ebfc184ea411e311038417a965e3d33fed4 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Thu, 26 Sep 2024 21:01:44 -0700 Subject: [PATCH 3/9] Update changelog --- src/OpenTelemetry/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index 47b6536a5e5..1f32b8ab62a 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -31,6 +31,11 @@ Notes](../../RELEASENOTES.md). See [#5854](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5854) for details. +* Add support for collecting metrics from + [Gauge](https://learn.microsoft.com/dotnet/api/system.diagnostics.metrics.gauge-1) + API + ([#](https://github.com/open-telemetry/opentelemetry-dotnet/pull/)) + ## 1.9.0 Released 2024-Jun-14 From 8de84e7029f9225054c8466feda55aa03ece1ee9 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Thu, 26 Sep 2024 21:09:21 -0700 Subject: [PATCH 4/9] Lint fix + changelog patch --- src/OpenTelemetry/CHANGELOG.md | 2 +- .../Metrics/MetricApiTestsBase.cs | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index 1f32b8ab62a..c1b3d987acc 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -34,7 +34,7 @@ Notes](../../RELEASENOTES.md). * Add support for collecting metrics from [Gauge](https://learn.microsoft.com/dotnet/api/system.diagnostics.metrics.gauge-1) API - ([#](https://github.com/open-telemetry/opentelemetry-dotnet/pull/)) + ([#5867](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5867)) ## 1.9.0 diff --git a/test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs b/test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs index 78dfdd9707c..ee2af8d357b 100644 --- a/test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs +++ b/test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs @@ -1611,8 +1611,8 @@ public void UnsupportedMetricInstrument() meterProvider.ForceFlush(MaxTimeToAllowForFlush); Assert.Empty(exportedItems); - } - + } + [Fact] public void GaugeIsExportedCorrectly() { @@ -1629,16 +1629,16 @@ public void GaugeIsExportedCorrectly() meterProvider.ForceFlush(MaxTimeToAllowForFlush); Assert.Single(exportedItems); var metric = exportedItems[0]; - Assert.Equal("Background Noise Level", metric.Description); - List metricPoints = new List(); - foreach (ref readonly var mp in metric.GetMetricPoints()) - { - metricPoints.Add(mp); - } - - var histogramPoint = metricPoints[0]; - var lastValue = metricPoints[0].GetGaugeLastValueLong(); - Assert.Equal(10, lastValue); + Assert.Equal("Background Noise Level", metric.Description); + List metricPoints = new List(); + foreach (ref readonly var mp in metric.GetMetricPoints()) + { + metricPoints.Add(mp); + } + + var histogramPoint = metricPoints[0]; + var lastValue = metricPoints[0].GetGaugeLastValueLong(); + Assert.Equal(10, lastValue); } internal static IConfiguration BuildConfiguration(bool emitOverflowAttribute, bool shouldReclaimUnusedMetricPoints) From b1ef3953ef9dd566cf0b961e652f55132159eb2f Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Thu, 26 Sep 2024 21:13:48 -0700 Subject: [PATCH 5/9] lint --- src/OpenTelemetry/Metrics/Metric.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry/Metrics/Metric.cs b/src/OpenTelemetry/Metrics/Metric.cs index 3bad252331c..37dd3198b71 100644 --- a/src/OpenTelemetry/Metrics/Metric.cs +++ b/src/OpenTelemetry/Metrics/Metric.cs @@ -116,8 +116,8 @@ internal Metric( { aggType = AggregationType.DoubleGauge; this.MetricType = MetricType.DoubleGauge; - } - else if (instrumentIdentity.InstrumentType == typeof(Gauge) + } + else if (instrumentIdentity.InstrumentType == typeof(Gauge) || instrumentIdentity.InstrumentType == typeof(Gauge)) { aggType = AggregationType.DoubleGauge; From d8c2b043dfed1fa7217a45fc3650d58fcddc48e8 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Fri, 27 Sep 2024 12:52:20 -0700 Subject: [PATCH 6/9] New test + doc update --- docs/metrics/README.md | 2 +- .../Metrics/MetricApiTestsBase.cs | 65 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/docs/metrics/README.md b/docs/metrics/README.md index a57d92e9b61..db91e62f529 100644 --- a/docs/metrics/README.md +++ b/docs/metrics/README.md @@ -86,7 +86,7 @@ static readonly Meter MyMeter = new("MyCompany.MyProduct.MyLibrary", "1.0"); | [Asynchronous Gauge](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#asynchronous-gauge) | [`ObservableGauge`](https://learn.microsoft.com/dotnet/api/system.diagnostics.metrics.observablegauge-1) | | [Asynchronous UpDownCounter](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#asynchronous-updowncounter) | [`ObservableUpDownCounter`](https://learn.microsoft.com/dotnet/api/system.diagnostics.metrics.observableupdowncounter-1) | | [Counter](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#counter) | [`Counter`](https://learn.microsoft.com/dotnet/api/system.diagnostics.metrics.counter-1) | - | [Gauge](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#gauge) (experimental) | N/A | + | [Gauge](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#gauge) | [`Gauge`](https://learn.microsoft.com/dotnet/api/system.diagnostics.metrics.gauge-1) | | [Histogram](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#histogram) | [`Histogram`](https://learn.microsoft.com/dotnet/api/system.diagnostics.metrics.histogram-1) | | [UpDownCounter](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#updowncounter) | [`UpDownCounter`](https://learn.microsoft.com/dotnet/api/system.diagnostics.metrics.updowncounter-1) | diff --git a/test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs b/test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs index ee2af8d357b..2fce9757aac 100644 --- a/test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs +++ b/test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs @@ -1641,6 +1641,71 @@ public void GaugeIsExportedCorrectly() Assert.Equal(10, lastValue); } + [Theory] + [InlineData(MetricReaderTemporalityPreference.Cumulative)] + [InlineData(MetricReaderTemporalityPreference.Delta)] + public void GaugeHandlesNoNewMeasurementsCorrectlyWithTemporality(MetricReaderTemporalityPreference temporalityPreference) + { + var exportedMetrics = new List(); + + using var meter = new Meter($"{Utils.GetCurrentMethodName()}"); + using var container = this.BuildMeterProvider(out var meterProvider, builder => builder + .AddMeter(meter.Name) + .AddInMemoryExporter(exportedMetrics, metricReaderOptions => + { + metricReaderOptions.TemporalityPreference = temporalityPreference; + })); + + var noiseLevelGauge = meter.CreateGauge(name: "NoiseLevel", unit: "dB", description: "Background Noise Level"); + noiseLevelGauge.Record(10); + + // Force a flush to export the recorded data + meterProvider.ForceFlush(MaxTimeToAllowForFlush); + + // Validate first export / flush + var firstMetric = exportedMetrics[0]; + var firstMetricPoints = new List(); + foreach (ref readonly var metricPoint in firstMetric.GetMetricPoints()) + { + firstMetricPoints.Add(metricPoint); + } + + Assert.Single(firstMetricPoints); + var firstMetricPoint = firstMetricPoints[0]; + Assert.Equal(10, firstMetricPoint.GetGaugeLastValueLong()); + + // Flush the metrics again without recording any new measurements + meterProvider.ForceFlush(MaxTimeToAllowForFlush); + + // Validate second export / flush + if (temporalityPreference == MetricReaderTemporalityPreference.Cumulative) + { + // For cumulative temporality, data points should still be collected + // without any new measurements + var secondMetric = exportedMetrics[1]; + var secondMetricPoints = new List(); + foreach (ref readonly var metricPoint in secondMetric.GetMetricPoints()) + { + secondMetricPoints.Add(metricPoint); + } + + Assert.Single(secondMetricPoints); + var secondMetricPoint = secondMetricPoints[0]; + Assert.Equal(10, secondMetricPoint.GetGaugeLastValueLong()); + } + else if (temporalityPreference == MetricReaderTemporalityPreference.Delta) + { + // For delta temporality, no new metric should be collected + // Expect an exception if we try to access exportedMetrics[1] + var exception = Assert.Throws(() => + { + var secondMetric = exportedMetrics[1]; + }); + + Assert.Contains("Index was out of range. Must be non-negative and less than the size of the collection.", exception.Message); + } + } + internal static IConfiguration BuildConfiguration(bool emitOverflowAttribute, bool shouldReclaimUnusedMetricPoints) { var configurationData = new Dictionary(); From 96bbfc6baeec9de8bce043d3f1665b0b9ea0d379 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Fri, 27 Sep 2024 12:54:36 -0700 Subject: [PATCH 7/9] feedback --- test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs b/test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs index 2fce9757aac..8dd8ebc0df2 100644 --- a/test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs +++ b/test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs @@ -1636,7 +1636,6 @@ public void GaugeIsExportedCorrectly() metricPoints.Add(mp); } - var histogramPoint = metricPoints[0]; var lastValue = metricPoints[0].GetGaugeLastValueLong(); Assert.Equal(10, lastValue); } From 457173dd1a517e2effbd6df252cc7e0fa7d75d80 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Fri, 27 Sep 2024 15:49:45 -0700 Subject: [PATCH 8/9] exception in test to length --- test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs b/test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs index 8dd8ebc0df2..d4088bb0d1a 100644 --- a/test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs +++ b/test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs @@ -1681,6 +1681,7 @@ public void GaugeHandlesNoNewMeasurementsCorrectlyWithTemporality(MetricReaderTe { // For cumulative temporality, data points should still be collected // without any new measurements + Assert.Equal(2, exportedMetrics.Count); var secondMetric = exportedMetrics[1]; var secondMetricPoints = new List(); foreach (ref readonly var metricPoint in secondMetric.GetMetricPoints()) @@ -1695,13 +1696,7 @@ public void GaugeHandlesNoNewMeasurementsCorrectlyWithTemporality(MetricReaderTe else if (temporalityPreference == MetricReaderTemporalityPreference.Delta) { // For delta temporality, no new metric should be collected - // Expect an exception if we try to access exportedMetrics[1] - var exception = Assert.Throws(() => - { - var secondMetric = exportedMetrics[1]; - }); - - Assert.Contains("Index was out of range. Must be non-negative and less than the size of the collection.", exception.Message); + Assert.Single(exportedMetrics); } } From 8539901bf8b8fae2b8eb5a706689f3b7159bae1f Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 30 Sep 2024 11:28:27 -0700 Subject: [PATCH 9/9] CHANGELOG tweak. --- src/OpenTelemetry/CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index c1b3d987acc..893b9c6c4d7 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -31,9 +31,9 @@ Notes](../../RELEASENOTES.md). See [#5854](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5854) for details. -* Add support for collecting metrics from - [Gauge](https://learn.microsoft.com/dotnet/api/system.diagnostics.metrics.gauge-1) - API +* Added support for collecting metrics emitted via the .NET 9 + [Gauge<T>](https://learn.microsoft.com/dotnet/api/system.diagnostics.metrics.gauge-1) + API. ([#5867](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5867)) ## 1.9.0