Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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.

* Added support for collecting metrics emitted via the .NET 9
[Gauge&lt;T&gt;](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
87 changes: 87 additions & 0 deletions test/OpenTelemetry.Tests/Metrics/MetricApiTestsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1613,6 +1613,93 @@ 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
Assert.Equal(2, exportedMetrics.Count);
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
Assert.Single(exportedMetrics);
}
}

internal static IConfiguration BuildConfiguration(bool emitOverflowAttribute, bool shouldReclaimUnusedMetricPoints)
{
var configurationData = new Dictionary<string, string>();
Expand Down