Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
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
2 changes: 1 addition & 1 deletion docs/metrics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>`](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<T>`](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<T>`](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<T>`](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<T>`](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<T>`](https://learn.microsoft.com/dotnet/api/system.diagnostics.metrics.updowncounter-1) |

Expand Down
5 changes: 5 additions & 0 deletions src/OpenTelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
([#5867](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5867))

## 1.9.0

Released 2024-Jun-14
Expand Down
14 changes: 14 additions & 0 deletions src/OpenTelemetry/Metrics/Metric.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ internal Metric(
aggType = AggregationType.DoubleGauge;
this.MetricType = MetricType.DoubleGauge;
}
else if (instrumentIdentity.InstrumentType == typeof(Gauge<double>)
|| instrumentIdentity.InstrumentType == typeof(Gauge<float>))
{
aggType = AggregationType.DoubleGauge;
this.MetricType = MetricType.DoubleGauge;
}
else if (instrumentIdentity.InstrumentType == typeof(ObservableGauge<long>)
|| instrumentIdentity.InstrumentType == typeof(ObservableGauge<int>)
|| instrumentIdentity.InstrumentType == typeof(ObservableGauge<short>)
Expand All @@ -125,6 +131,14 @@ internal Metric(
aggType = AggregationType.LongGauge;
this.MetricType = MetricType.LongGauge;
}
else if (instrumentIdentity.InstrumentType == typeof(Gauge<long>)
|| instrumentIdentity.InstrumentType == typeof(Gauge<int>)
|| instrumentIdentity.InstrumentType == typeof(Gauge<short>)
|| instrumentIdentity.InstrumentType == typeof(Gauge<byte>))
{
aggType = AggregationType.LongGauge;
this.MetricType = MetricType.LongGauge;
}
else if (instrumentIdentity.IsHistogram)
{
var explicitBucketBounds = instrumentIdentity.HistogramBucketBounds;
Expand Down
1 change: 1 addition & 0 deletions src/OpenTelemetry/Metrics/Reader/MetricReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
92 changes: 92 additions & 0 deletions test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1613,6 +1613,98 @@ public void UnsupportedMetricInstrument()
Assert.Empty(exportedItems);
}

[Fact]
public void GaugeIsExportedCorrectly()
{
var exportedItems = new List<Metric>();

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<long>(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<MetricPoint> metricPoints = new List<MetricPoint>();
foreach (ref readonly var mp in metric.GetMetricPoints())
{
metricPoints.Add(mp);
}

var lastValue = metricPoints[0].GetGaugeLastValueLong();
Assert.Equal(10, lastValue);
}

[Theory]
[InlineData(MetricReaderTemporalityPreference.Cumulative)]
[InlineData(MetricReaderTemporalityPreference.Delta)]
public void GaugeHandlesNoNewMeasurementsCorrectlyWithTemporality(MetricReaderTemporalityPreference temporalityPreference)
{
var exportedMetrics = new List<Metric>();

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<long>(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<MetricPoint>();
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<MetricPoint>();
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<ArgumentOutOfRangeException>(() =>
{
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<string, string>();
Expand Down