Support UpDownCounter and ObservableUpDownCounter (#3606)

This commit is contained in:
Alan West 2022-09-01 18:17:37 -07:00 committed by GitHub
parent a37198c6d0
commit c2f5e80b0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 403 additions and 21 deletions

View File

@ -143,10 +143,11 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation
switch (metric.MetricType)
{
case MetricType.LongSum:
case MetricType.LongSumNonMonotonic:
{
var sum = new OtlpMetrics.Sum
{
IsMonotonic = true,
IsMonotonic = metric.MetricType == MetricType.LongSum,
AggregationTemporality = temporality,
};
@ -169,10 +170,11 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation
}
case MetricType.DoubleSum:
case MetricType.DoubleSumNonMonotonic:
{
var sum = new OtlpMetrics.Sum
{
IsMonotonic = true,
IsMonotonic = metric.MetricType == MetricType.DoubleSum,
AggregationTemporality = temporality,
};

View File

@ -25,7 +25,7 @@ namespace OpenTelemetry.Exporter.Prometheus
{
private static readonly string[] MetricTypes = new string[]
{
"untyped", "counter", "gauge", "summary", "histogram", "histogram", "histogram", "histogram", "untyped",
"untyped", "counter", "gauge", "summary", "histogram", "histogram", "histogram", "histogram", "gauge",
};
public static int WriteMetric(byte[] buffer, int cursor, Metric metric)

View File

@ -48,3 +48,5 @@ static Microsoft.Extensions.DependencyInjection.TracerProviderBuilderServiceColl
static Microsoft.Extensions.DependencyInjection.TracerProviderBuilderServiceCollectionExtensions.ConfigureOpenTelemetryTracing(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action<OpenTelemetry.Trace.TracerProviderBuilder!>! configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
~static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder, System.Action<OpenTelemetry.Resources.ResourceBuilder> configure) -> OpenTelemetry.Metrics.MeterProviderBuilder
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Trace.TracerProviderBuilder! tracerProviderBuilder, System.Action<OpenTelemetry.Resources.ResourceBuilder!>! configure) -> OpenTelemetry.Trace.TracerProviderBuilder!
OpenTelemetry.Metrics.MetricType.LongSumNonMonotonic = 138 -> OpenTelemetry.Metrics.MetricType
OpenTelemetry.Metrics.MetricType.DoubleSumNonMonotonic = 141 -> OpenTelemetry.Metrics.MetricType

View File

@ -48,3 +48,5 @@ static Microsoft.Extensions.DependencyInjection.TracerProviderBuilderServiceColl
static Microsoft.Extensions.DependencyInjection.TracerProviderBuilderServiceCollectionExtensions.ConfigureOpenTelemetryTracing(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action<OpenTelemetry.Trace.TracerProviderBuilder!>! configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
~static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder, System.Action<OpenTelemetry.Resources.ResourceBuilder> configure) -> OpenTelemetry.Metrics.MeterProviderBuilder
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Trace.TracerProviderBuilder! tracerProviderBuilder, System.Action<OpenTelemetry.Resources.ResourceBuilder!>! configure) -> OpenTelemetry.Trace.TracerProviderBuilder!
OpenTelemetry.Metrics.MetricType.LongSumNonMonotonic = 138 -> OpenTelemetry.Metrics.MetricType
OpenTelemetry.Metrics.MetricType.DoubleSumNonMonotonic = 141 -> OpenTelemetry.Metrics.MetricType

View File

@ -48,3 +48,5 @@ static Microsoft.Extensions.DependencyInjection.TracerProviderBuilderServiceColl
static Microsoft.Extensions.DependencyInjection.TracerProviderBuilderServiceCollectionExtensions.ConfigureOpenTelemetryTracing(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action<OpenTelemetry.Trace.TracerProviderBuilder!>! configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
~static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder, System.Action<OpenTelemetry.Resources.ResourceBuilder> configure) -> OpenTelemetry.Metrics.MeterProviderBuilder
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Trace.TracerProviderBuilder! tracerProviderBuilder, System.Action<OpenTelemetry.Resources.ResourceBuilder!>! configure) -> OpenTelemetry.Trace.TracerProviderBuilder!
OpenTelemetry.Metrics.MetricType.LongSumNonMonotonic = 138 -> OpenTelemetry.Metrics.MetricType
OpenTelemetry.Metrics.MetricType.DoubleSumNonMonotonic = 141 -> OpenTelemetry.Metrics.MetricType

View File

@ -48,3 +48,5 @@ static Microsoft.Extensions.DependencyInjection.TracerProviderBuilderServiceColl
static Microsoft.Extensions.DependencyInjection.TracerProviderBuilderServiceCollectionExtensions.ConfigureOpenTelemetryTracing(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action<OpenTelemetry.Trace.TracerProviderBuilder!>! configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
~static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder, System.Action<OpenTelemetry.Resources.ResourceBuilder> configure) -> OpenTelemetry.Metrics.MeterProviderBuilder
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Trace.TracerProviderBuilder! tracerProviderBuilder, System.Action<OpenTelemetry.Resources.ResourceBuilder!>! configure) -> OpenTelemetry.Trace.TracerProviderBuilder!
OpenTelemetry.Metrics.MetricType.LongSumNonMonotonic = 138 -> OpenTelemetry.Metrics.MetricType
OpenTelemetry.Metrics.MetricType.DoubleSumNonMonotonic = 141 -> OpenTelemetry.Metrics.MetricType

View File

@ -5,6 +5,9 @@
* Allows samplers the ability to modify tracestate if desired.
([#3610](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3610))
* Added support for `UpDownCounter` and `ObservableUpDownCounter` instruments.
([#3606](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3606))
## 1.4.0-alpha.2
Released 2022-Aug-18

View File

@ -67,6 +67,34 @@ namespace OpenTelemetry.Metrics
aggType = AggregationType.DoubleSumIncomingCumulative;
this.MetricType = MetricType.DoubleSum;
}
else if (instrumentIdentity.InstrumentType == typeof(ObservableUpDownCounter<long>)
|| instrumentIdentity.InstrumentType == typeof(ObservableUpDownCounter<int>)
|| instrumentIdentity.InstrumentType == typeof(ObservableUpDownCounter<short>)
|| instrumentIdentity.InstrumentType == typeof(ObservableUpDownCounter<byte>))
{
aggType = AggregationType.LongSumIncomingCumulative;
this.MetricType = MetricType.LongSumNonMonotonic;
}
else if (instrumentIdentity.InstrumentType == typeof(UpDownCounter<long>)
|| instrumentIdentity.InstrumentType == typeof(UpDownCounter<int>)
|| instrumentIdentity.InstrumentType == typeof(UpDownCounter<short>)
|| instrumentIdentity.InstrumentType == typeof(UpDownCounter<byte>))
{
aggType = AggregationType.LongSumIncomingDelta;
this.MetricType = MetricType.LongSumNonMonotonic;
}
else if (instrumentIdentity.InstrumentType == typeof(UpDownCounter<double>)
|| instrumentIdentity.InstrumentType == typeof(UpDownCounter<float>))
{
aggType = AggregationType.DoubleSumIncomingDelta;
this.MetricType = MetricType.DoubleSumNonMonotonic;
}
else if (instrumentIdentity.InstrumentType == typeof(ObservableUpDownCounter<double>)
|| instrumentIdentity.InstrumentType == typeof(ObservableUpDownCounter<float>))
{
aggType = AggregationType.DoubleSumIncomingCumulative;
this.MetricType = MetricType.DoubleSumNonMonotonic;
}
else if (instrumentIdentity.InstrumentType == typeof(ObservableGauge<double>)
|| instrumentIdentity.InstrumentType == typeof(ObservableGauge<float>))
{

View File

@ -44,13 +44,8 @@ namespace OpenTelemetry.Metrics
// Temporatlity is not defined for gauges, so this does not really affect anything.
var type when type == typeof(ObservableGauge<>) => AggregationTemporality.Delta,
// With .NET 7 the OpenTelemetry .NET SDK will support UpDownCounters.
// These will be aggregated using Cumulative temporatlity.
// See:
// https://docs.microsoft.com/dotnet/api/system.diagnostics.metrics.updowncounter-1
// https://docs.microsoft.com/dotnet/api/system.diagnostics.metrics.observableupdowncounter-1
// var type when type == typeof(UpDownCounter<>) => AggregationTemporality.Cumulative,
// var type when type == typeof(ObservableUpDownCounter<>) => AggregationTemporality.Cumulative,
var type when type == typeof(UpDownCounter<>) => AggregationTemporality.Cumulative,
var type when type == typeof(ObservableUpDownCounter<>) => AggregationTemporality.Cumulative,
// TODO: Consider logging here because we should not fall through to this case.
_ => AggregationTemporality.Delta,

View File

@ -30,7 +30,7 @@ namespace OpenTelemetry.Metrics
0x50: HistogramWithMinMax (reserved)
0x60: ExponentialHistogram (reserved)
0x70: ExponentialHistogramWithMinMax (reserved)
0x80: Reserved
0x80: SumNonMonotonic
Point kind:
0x04: I1 (signed 1-byte integer)
@ -69,5 +69,15 @@ namespace OpenTelemetry.Metrics
/// Histogram.
/// </summary>
Histogram = 0x40,
/// <summary>
/// Non-monotonic Sum of Long type.
/// </summary>
LongSumNonMonotonic = 0x8a,
/// <summary>
/// Non-monotonic Sum of Double type.
/// </summary>
DoubleSumNonMonotonic = 0x8d,
}
}

View File

@ -24,10 +24,11 @@ namespace OpenTelemetry.Metrics
internal const MetricType METRIC_TYPE_MASK = (MetricType)0xf0;
internal const MetricType METRIC_TYPE_SUM = (MetricType)0x10;
internal const MetricType METRIC_TYPE_MONOTONIC_SUM = (MetricType)0x10;
internal const MetricType METRIC_TYPE_GAUGE = (MetricType)0x20;
/* internal const byte METRIC_TYPE_SUMMARY = 0x30; // not used */
internal const MetricType METRIC_TYPE_HISTOGRAM = (MetricType)0x40;
internal const MetricType METRIC_TYPE_NON_MONOTONIC_SUM = (MetricType)0x80;
internal const MetricType POINT_KIND_MASK = (MetricType)0x0f;
@ -47,7 +48,8 @@ namespace OpenTelemetry.Metrics
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsSum(this MetricType self)
{
return (self & METRIC_TYPE_MASK) == METRIC_TYPE_SUM;
var type = self & METRIC_TYPE_MASK;
return type == METRIC_TYPE_MONOTONIC_SUM || type == METRIC_TYPE_NON_MONOTONIC_SUM;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

View File

@ -271,12 +271,12 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests
}
[Theory]
[InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative, true)]
[InlineData("test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative, true)]
[InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, true)]
[InlineData("test_counter", "description", "unit", 123L, null, MetricReaderTemporalityPreference.Cumulative, true)]
[InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, true, "key1", "value1", "key2", 123)]
public void TestCounterToOtlpMetric(string name, string description, string unit, long? longValue, double? doubleValue, MetricReaderTemporalityPreference aggregationTemporality, bool isMonotonic, params object[] keysValues)
[InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative)]
[InlineData("test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative)]
[InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta)]
[InlineData("test_counter", "description", "unit", 123L, null, MetricReaderTemporalityPreference.Cumulative)]
[InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, "key1", "value1", "key2", 123)]
public void TestCounterToOtlpMetric(string name, string description, string unit, long? longValue, double? doubleValue, MetricReaderTemporalityPreference aggregationTemporality, params object[] keysValues)
{
var metrics = new List<Metric>();
@ -324,7 +324,7 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests
Assert.Null(actual.ExponentialHistogram);
Assert.Null(actual.Summary);
Assert.Equal(isMonotonic, actual.Sum.IsMonotonic);
Assert.True(actual.Sum.IsMonotonic);
var otlpAggregationTemporality = aggregationTemporality == MetricReaderTemporalityPreference.Cumulative
? OtlpMetrics.AggregationTemporality.Cumulative
@ -359,6 +359,95 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests
Assert.Empty(dataPoint.Exemplars);
}
[Theory]
[InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative)]
[InlineData("test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative)]
[InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta)]
[InlineData("test_counter", "description", "unit", 123L, null, MetricReaderTemporalityPreference.Cumulative)]
[InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, "key1", "value1", "key2", 123)]
public void TestUpDownCounterToOtlpMetric(string name, string description, string unit, long? longValue, double? doubleValue, MetricReaderTemporalityPreference aggregationTemporality, params object[] keysValues)
{
var metrics = new List<Metric>();
using var meter = new Meter(Utils.GetCurrentMethodName());
using var provider = Sdk.CreateMeterProviderBuilder()
.AddMeter(meter.Name)
.AddInMemoryExporter(metrics, metricReaderOptions =>
{
metricReaderOptions.TemporalityPreference = aggregationTemporality;
})
.Build();
var attributes = ToAttributes(keysValues).ToArray();
if (longValue.HasValue)
{
var counter = meter.CreateUpDownCounter<long>(name, unit, description);
counter.Add(longValue.Value, attributes);
}
else
{
var counter = meter.CreateUpDownCounter<double>(name, unit, description);
counter.Add(doubleValue.Value, attributes);
}
provider.ForceFlush();
var batch = new Batch<Metric>(metrics.ToArray(), metrics.Count);
var request = new OtlpCollector.ExportMetricsServiceRequest();
request.AddMetrics(ResourceBuilder.CreateEmpty().Build().ToOtlpResource(), batch);
var resourceMetric = request.ResourceMetrics.Single();
var scopeMetrics = resourceMetric.ScopeMetrics.Single();
var actual = scopeMetrics.Metrics.Single();
Assert.Equal(name, actual.Name);
Assert.Equal(description ?? string.Empty, actual.Description);
Assert.Equal(unit ?? string.Empty, actual.Unit);
Assert.Equal(OtlpMetrics.Metric.DataOneofCase.Sum, actual.DataCase);
Assert.Null(actual.Gauge);
Assert.NotNull(actual.Sum);
Assert.Null(actual.Histogram);
Assert.Null(actual.ExponentialHistogram);
Assert.Null(actual.Summary);
Assert.False(actual.Sum.IsMonotonic);
var otlpAggregationTemporality = aggregationTemporality == MetricReaderTemporalityPreference.Cumulative
? OtlpMetrics.AggregationTemporality.Cumulative
: OtlpMetrics.AggregationTemporality.Cumulative;
Assert.Equal(otlpAggregationTemporality, actual.Sum.AggregationTemporality);
Assert.Single(actual.Sum.DataPoints);
var dataPoint = actual.Sum.DataPoints.First();
Assert.True(dataPoint.StartTimeUnixNano > 0);
Assert.True(dataPoint.TimeUnixNano > 0);
if (longValue.HasValue)
{
Assert.Equal(OtlpMetrics.NumberDataPoint.ValueOneofCase.AsInt, dataPoint.ValueCase);
Assert.Equal(longValue, dataPoint.AsInt);
}
else
{
Assert.Equal(OtlpMetrics.NumberDataPoint.ValueOneofCase.AsDouble, dataPoint.ValueCase);
Assert.Equal(doubleValue, dataPoint.AsDouble);
}
if (attributes.Length > 0)
{
OtlpTestHelpers.AssertOtlpAttributes(attributes, dataPoint.Attributes);
}
else
{
Assert.Empty(dataPoint.Attributes);
}
Assert.Empty(dataPoint.Exemplars);
}
[Theory]
[InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative)]
[InlineData("test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative)]

View File

@ -187,6 +187,33 @@ namespace OpenTelemetry.Exporter.Prometheus.Tests
Encoding.UTF8.GetString(buffer, 0, cursor));
}
[Fact]
public void SumNonMonotonicDouble()
{
var buffer = new byte[85000];
var metrics = new List<Metric>();
using var meter = new Meter(Utils.GetCurrentMethodName());
using var provider = Sdk.CreateMeterProviderBuilder()
.AddMeter(meter.Name)
.AddInMemoryExporter(metrics)
.Build();
var counter = meter.CreateUpDownCounter<double>("test_updown_counter");
counter.Add(10);
counter.Add(-11);
provider.ForceFlush();
var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]);
Assert.Matches(
("^"
+ "# TYPE test_updown_counter gauge\n"
+ "test_updown_counter -1 \\d+\n"
+ "$").Replace('\'', '"'),
Encoding.UTF8.GetString(buffer, 0, cursor));
}
[Fact]
public void HistogramZeroDimension()
{

View File

@ -586,7 +586,7 @@ namespace OpenTelemetry.Metrics.Tests
exportedItems.Clear();
#if NETFRAMEWORK
Thread.Sleep(5000); // Compensates for low resolution timing in netfx.
Thread.Sleep(10); // Compensates for low resolution timing in netfx.
#endif
counterLong.Add(10);
@ -864,6 +864,224 @@ namespace OpenTelemetry.Metrics.Tests
Assert.Equal(30, metricPoint1.GetSumLong());
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void UpDownCounterAggregationTest(bool exportDelta)
{
DateTime testStartTime = DateTime.UtcNow;
var exportedItems = new List<Metric>();
using var meter = new Meter($"{Utils.GetCurrentMethodName()}.{exportDelta}");
var counterLong = meter.CreateUpDownCounter<long>("mycounter");
using var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddMeter(meter.Name)
.AddInMemoryExporter(exportedItems, metricReaderOptions =>
{
metricReaderOptions.TemporalityPreference = exportDelta ? MetricReaderTemporalityPreference.Delta : MetricReaderTemporalityPreference.Cumulative;
})
.Build();
counterLong.Add(10);
counterLong.Add(-5);
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
long sumReceived = GetLongSum(exportedItems);
Assert.Equal(5, sumReceived);
var metricPoint = GetFirstMetricPoint(exportedItems);
Assert.NotNull(metricPoint);
Assert.True(metricPoint.Value.StartTime >= testStartTime);
Assert.True(metricPoint.Value.EndTime != default);
DateTimeOffset firstRunStartTime = metricPoint.Value.StartTime;
DateTimeOffset firstRunEndTime = metricPoint.Value.EndTime;
exportedItems.Clear();
#if NETFRAMEWORK
Thread.Sleep(10); // Compensates for low resolution timing in netfx.
#endif
counterLong.Add(10);
counterLong.Add(-5);
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
sumReceived = GetLongSum(exportedItems);
// Same for both cumulative and delta. MetricReaderTemporalityPreference.Delta implies cumulative for UpDownCounters.
Assert.Equal(10, sumReceived);
metricPoint = GetFirstMetricPoint(exportedItems);
Assert.NotNull(metricPoint);
Assert.True(metricPoint.Value.StartTime >= testStartTime);
Assert.True(metricPoint.Value.EndTime != default);
// Same for both cumulative and delta. MetricReaderTemporalityPreference.Delta implies cumulative for UpDownCounters.
Assert.Equal(firstRunStartTime, metricPoint.Value.StartTime);
Assert.True(metricPoint.Value.EndTime > firstRunEndTime);
exportedItems.Clear();
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
sumReceived = GetLongSum(exportedItems);
// Same for both cumulative and delta. MetricReaderTemporalityPreference.Delta implies cumulative for UpDownCounters.
Assert.Equal(10, sumReceived);
exportedItems.Clear();
counterLong.Add(40);
counterLong.Add(-20);
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
sumReceived = GetLongSum(exportedItems);
// Same for both cumulative and delta. MetricReaderTemporalityPreference.Delta implies cumulative for UpDownCounters.
Assert.Equal(30, sumReceived);
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void ObservableUpDownCounterAggregationTest(bool exportDelta)
{
var exportedItems = new List<Metric>();
using var meter = new Meter($"{Utils.GetCurrentMethodName()}.{exportDelta}");
int i = 1;
var counterLong = meter.CreateObservableUpDownCounter(
"observable-counter",
() =>
{
return new List<Measurement<long>>()
{
new Measurement<long>(i++ * 10),
};
});
using var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddMeter(meter.Name)
.AddInMemoryExporter(exportedItems, metricReaderOptions =>
{
metricReaderOptions.TemporalityPreference = exportDelta ? MetricReaderTemporalityPreference.Delta : MetricReaderTemporalityPreference.Cumulative;
})
.Build();
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
long sumReceived = GetLongSum(exportedItems);
Assert.Equal(10, sumReceived);
exportedItems.Clear();
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
sumReceived = GetLongSum(exportedItems);
// Same for both cumulative and delta. MetricReaderTemporalityPreference.Delta implies cumulative for UpDownCounters.
Assert.Equal(20, sumReceived);
exportedItems.Clear();
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
sumReceived = GetLongSum(exportedItems);
// Same for both cumulative and delta. MetricReaderTemporalityPreference.Delta implies cumulative for UpDownCounters.
Assert.Equal(30, sumReceived);
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void ObservableUpDownCounterWithTagsAggregationTest(bool exportDelta)
{
var exportedItems = new List<Metric>();
var tags1 = new List<KeyValuePair<string, object>>
{
new("statusCode", 200),
new("verb", "get"),
};
var tags2 = new List<KeyValuePair<string, object>>
{
new("statusCode", 200),
new("verb", "post"),
};
var tags3 = new List<KeyValuePair<string, object>>
{
new("statusCode", 500),
new("verb", "get"),
};
using var meter = new Meter($"{Utils.GetCurrentMethodName()}.{exportDelta}");
var counterLong = meter.CreateObservableUpDownCounter(
"observable-counter",
() =>
{
return new List<Measurement<long>>()
{
new Measurement<long>(10, tags1),
new Measurement<long>(10, tags2),
new Measurement<long>(10, tags3),
};
});
using var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddMeter(meter.Name)
.AddInMemoryExporter(exportedItems, metricReaderOptions =>
{
metricReaderOptions.TemporalityPreference = exportDelta ? MetricReaderTemporalityPreference.Delta : MetricReaderTemporalityPreference.Cumulative;
})
.Build();
// Export 1
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
Assert.Single(exportedItems);
var metric = exportedItems[0];
Assert.Equal("observable-counter", metric.Name);
List<MetricPoint> metricPoints = new List<MetricPoint>();
foreach (ref readonly var mp in metric.GetMetricPoints())
{
metricPoints.Add(mp);
}
Assert.Equal(3, metricPoints.Count);
var metricPoint1 = metricPoints[0];
Assert.Equal(10, metricPoint1.GetSumLong());
ValidateMetricPointTags(tags1, metricPoint1.Tags);
var metricPoint2 = metricPoints[1];
Assert.Equal(10, metricPoint2.GetSumLong());
ValidateMetricPointTags(tags2, metricPoint2.Tags);
var metricPoint3 = metricPoints[2];
Assert.Equal(10, metricPoint3.GetSumLong());
ValidateMetricPointTags(tags3, metricPoint3.Tags);
// Export 2
exportedItems.Clear();
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
Assert.Single(exportedItems);
metric = exportedItems[0];
Assert.Equal("observable-counter", metric.Name);
metricPoints.Clear();
foreach (ref readonly var mp in metric.GetMetricPoints())
{
metricPoints.Add(mp);
}
Assert.Equal(3, metricPoints.Count);
// Same for both cumulative and delta. MetricReaderTemporalityPreference.Delta implies cumulative for UpDownCounters.
metricPoint1 = metricPoints[0];
Assert.Equal(10, metricPoint1.GetSumLong());
ValidateMetricPointTags(tags1, metricPoint1.Tags);
metricPoint2 = metricPoints[1];
Assert.Equal(10, metricPoint2.GetSumLong());
ValidateMetricPointTags(tags2, metricPoint2.Tags);
metricPoint3 = metricPoints[2];
Assert.Equal(10, metricPoint3.GetSumLong());
ValidateMetricPointTags(tags3, metricPoint3.Tags);
}
[Theory]
[InlineData(true)]
[InlineData(false)]