Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[sdk-metrics] Add support for .NET 9 Advice API #5854

48 changes: 47 additions & 1 deletion src/OpenTelemetry/Metrics/MetricStreamIdentity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<long> longHistogram => GetExplicitBucketHistogramBoundsFromAdvice(longHistogram),
Histogram<int> intHistogram => GetExplicitBucketHistogramBoundsFromAdvice(intHistogram),
Histogram<short> shortHistogram => GetExplicitBucketHistogramBoundsFromAdvice(shortHistogram),
Histogram<byte> byteHistogram => GetExplicitBucketHistogramBoundsFromAdvice(byteHistogram),
Histogram<float> floatHistogram => GetExplicitBucketHistogramBoundsFromAdvice(floatHistogram),
Histogram<double> doubleHistogram => GetExplicitBucketHistogramBoundsFromAdvice(doubleHistogram),
_ => null,
};
}

private static double[]? GetExplicitBucketHistogramBoundsFromAdvice<T>(Histogram<T> histogram)
where T : struct
{
var adviceExplicitBucketBoundaries = histogram.Advice?.HistogramBucketBoundaries;
if (adviceExplicitBucketBoundaries == null)
{
return null;
}

if (typeof(T) == typeof(double))
{
return ((IReadOnlyList<double>)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))
Expand Down
124 changes: 124 additions & 0 deletions test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Metric>();
IReadOnlyList<long> boundaries = new List<long>() { 10, 20 };

using var container = this.BuildMeterProvider(out var meterProvider, builder => builder
.AddMeter(meter.Name)
.AddInMemoryExporter(exportedItems));

var histogram = meter.CreateHistogram<long>(
"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<MetricPoint> metricPointsCustom = new List<MetricPoint>();
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()
rajkumar-rangaraj marked this conversation as resolved.
Show resolved Hide resolved
{
using var meter = new Meter(Utils.GetCurrentMethodName());
var exportedItems = new List<Metric>();
IReadOnlyList<long> adviceBoundaries = new List<long>() { 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<long>(
"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<MetricPoint> metricPointsCustom = new List<MetricPoint>();
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()
{
Expand Down
Loading