Always generate a new metric stream for each view an instrument matches (#3148)
This commit is contained in:
parent
4cabb23f09
commit
879d3c91a0
|
|
@ -8,6 +8,12 @@
|
|||
may be possible in the future.
|
||||
([#3126](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3126))
|
||||
|
||||
* Conformed to the specification to ensure that each view that an instrument
|
||||
matches results in a new metric stream. With this change it is possible for
|
||||
views to introduce conflicting metric streams. Any conflicts encountered will
|
||||
result in a diagnostic log.
|
||||
([#3148](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3148))
|
||||
|
||||
## 1.2.0-rc4
|
||||
|
||||
Released 2022-Mar-30
|
||||
|
|
|
|||
|
|
@ -155,14 +155,23 @@ namespace OpenTelemetry.Metrics
|
|||
// There may be excess space wasted, but it'll eligible for
|
||||
// GC right after this method.
|
||||
var metricStreamConfigs = new List<MetricStreamConfiguration>(viewConfigCount);
|
||||
foreach (var viewConfig in this.viewConfigs)
|
||||
for (var i = 0; i < viewConfigCount; ++i)
|
||||
{
|
||||
var viewConfig = this.viewConfigs[i];
|
||||
MetricStreamConfiguration metricStreamConfig = null;
|
||||
|
||||
try
|
||||
{
|
||||
metricStreamConfig = viewConfig(instrument);
|
||||
|
||||
// The SDK provides some static MetricStreamConfigurations.
|
||||
// For example, the Drop configuration. The static ViewId
|
||||
// should not be changed for these configurations.
|
||||
if (!metricStreamConfig.ViewId.HasValue)
|
||||
{
|
||||
metricStreamConfig.ViewId = i;
|
||||
}
|
||||
|
||||
if (metricStreamConfig is ExplicitBucketHistogramConfiguration
|
||||
&& instrument.GetType().GetGenericTypeDefinition() != typeof(Histogram<>))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -38,11 +38,7 @@ namespace OpenTelemetry.Metrics
|
|||
|
||||
internal Metric AddMetricWithNoViews(Instrument instrument)
|
||||
{
|
||||
var meterName = instrument.Meter.Name;
|
||||
var meterVersion = instrument.Meter.Version;
|
||||
var metricName = instrument.Name;
|
||||
var metricStreamName = $"{meterName}.{meterVersion}.{metricName}";
|
||||
var metricStreamIdentity = new MetricStreamIdentity(instrument.Meter, metricName, instrument.Unit, instrument.Description, instrument.GetType(), null, null);
|
||||
var metricStreamIdentity = new MetricStreamIdentity(instrument, metricStreamConfiguration: null);
|
||||
lock (this.instrumentCreationLock)
|
||||
{
|
||||
if (this.instrumentIdentityToMetric.TryGetValue(metricStreamIdentity, out var existingMetric))
|
||||
|
|
@ -50,11 +46,11 @@ namespace OpenTelemetry.Metrics
|
|||
return existingMetric;
|
||||
}
|
||||
|
||||
if (this.metricStreamNames.Contains(metricStreamName))
|
||||
if (this.metricStreamNames.Contains(metricStreamIdentity.MetricStreamName))
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.DuplicateMetricInstrument(
|
||||
metricName,
|
||||
meterName,
|
||||
metricStreamIdentity.InstrumentName,
|
||||
metricStreamIdentity.MeterName,
|
||||
"Metric instrument has the same name as an existing one but differs by description, unit, or instrument type. Measurements from this instrument will still be exported but may result in conflicts.",
|
||||
"Either change the name of the instrument or use MeterProviderBuilder.AddView to resolve the conflict.");
|
||||
}
|
||||
|
|
@ -62,7 +58,7 @@ namespace OpenTelemetry.Metrics
|
|||
var index = ++this.metricIndex;
|
||||
if (index >= this.maxMetricStreams)
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.MetricInstrumentIgnored(metricName, instrument.Meter.Name, "Maximum allowed Metric streams for the provider exceeded.", "Use MeterProviderBuilder.AddView to drop unused instruments. Or use MeterProviderBuilder.SetMaxMetricStreams to configure MeterProvider to allow higher limit.");
|
||||
OpenTelemetrySdkEventSource.Log.MetricInstrumentIgnored(metricStreamIdentity.InstrumentName, metricStreamIdentity.MeterName, "Maximum allowed Metric streams for the provider exceeded.", "Use MeterProviderBuilder.AddView to drop unused instruments. Or use MeterProviderBuilder.SetMaxMetricStreams to configure MeterProvider to allow higher limit.");
|
||||
return null;
|
||||
}
|
||||
else
|
||||
|
|
@ -78,13 +74,13 @@ namespace OpenTelemetry.Metrics
|
|||
// Could be improved with separate Event.
|
||||
// Also the message could call out what Instruments
|
||||
// and types (eg: int, long etc) are supported.
|
||||
OpenTelemetrySdkEventSource.Log.MetricInstrumentIgnored(metricName, instrument.Meter.Name, "Unsupported instrument. Details: " + nse.Message, "Switch to a supported instrument type.");
|
||||
OpenTelemetrySdkEventSource.Log.MetricInstrumentIgnored(metricStreamIdentity.InstrumentName, metricStreamIdentity.MeterName, "Unsupported instrument. Details: " + nse.Message, "Switch to a supported instrument type.");
|
||||
return null;
|
||||
}
|
||||
|
||||
this.instrumentIdentityToMetric[metricStreamIdentity] = metric;
|
||||
this.metrics[index] = metric;
|
||||
this.metricStreamNames.Add(metricStreamName);
|
||||
this.metricStreamNames.Add(metricStreamIdentity.MetricStreamName);
|
||||
return metric;
|
||||
}
|
||||
}
|
||||
|
|
@ -114,21 +110,13 @@ namespace OpenTelemetry.Metrics
|
|||
for (int i = 0; i < maxCountMetricsToBeCreated; i++)
|
||||
{
|
||||
var metricStreamConfig = metricStreamConfigs[i];
|
||||
var meterName = instrument.Meter.Name;
|
||||
var meterVersion = instrument.Meter.Version;
|
||||
var metricName = metricStreamConfig?.Name ?? instrument.Name;
|
||||
var metricStreamName = $"{meterName}.{meterVersion}.{metricName}";
|
||||
var metricDescription = metricStreamConfig?.Description ?? instrument.Description;
|
||||
var tagKeysInteresting = metricStreamConfig?.CopiedTagKeys;
|
||||
var histogramBucketBounds = (metricStreamConfig is ExplicitBucketHistogramConfiguration histogramConfig
|
||||
&& histogramConfig.CopiedBoundaries != null) ? histogramConfig.CopiedBoundaries : null;
|
||||
var metricStreamIdentity = new MetricStreamIdentity(instrument.Meter, metricName, instrument.Unit, metricDescription, instrument.GetType(), tagKeysInteresting, histogramBucketBounds);
|
||||
var metricStreamIdentity = new MetricStreamIdentity(instrument, metricStreamConfig);
|
||||
|
||||
if (!MeterProviderBuilderSdk.IsValidInstrumentName(metricName))
|
||||
if (!MeterProviderBuilderSdk.IsValidInstrumentName(metricStreamIdentity.InstrumentName))
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.MetricInstrumentIgnored(
|
||||
metricName,
|
||||
instrument.Meter.Name,
|
||||
metricStreamIdentity.InstrumentName,
|
||||
metricStreamIdentity.MeterName,
|
||||
"Metric name is invalid.",
|
||||
"The name must comply with the OpenTelemetry specification.");
|
||||
|
||||
|
|
@ -137,45 +125,39 @@ namespace OpenTelemetry.Metrics
|
|||
|
||||
if (this.instrumentIdentityToMetric.TryGetValue(metricStreamIdentity, out var existingMetric))
|
||||
{
|
||||
// The list of metrics may already contain a matching metric with the same
|
||||
// identity when a single instrument is selected by multiple views.
|
||||
if (!metrics.Contains(existingMetric))
|
||||
{
|
||||
metrics.Add(existingMetric);
|
||||
}
|
||||
|
||||
metrics.Add(existingMetric);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.metricStreamNames.Contains(metricStreamName))
|
||||
if (this.metricStreamNames.Contains(metricStreamIdentity.MetricStreamName))
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.DuplicateMetricInstrument(
|
||||
metricName,
|
||||
meterName,
|
||||
metricStreamIdentity.InstrumentName,
|
||||
metricStreamIdentity.MeterName,
|
||||
"Metric instrument has the same name as an existing one but differs by description, unit, or instrument type. Measurements from this instrument will still be exported but may result in conflicts.",
|
||||
"Either change the name of the instrument or use MeterProviderBuilder.AddView to resolve the conflict.");
|
||||
}
|
||||
|
||||
if (metricStreamConfig == MetricStreamConfiguration.Drop)
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.MetricInstrumentIgnored(metricName, instrument.Meter.Name, "View configuration asks to drop this instrument.", "Modify view configuration to allow this instrument, if desired.");
|
||||
OpenTelemetrySdkEventSource.Log.MetricInstrumentIgnored(metricStreamIdentity.InstrumentName, metricStreamIdentity.MeterName, "View configuration asks to drop this instrument.", "Modify view configuration to allow this instrument, if desired.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var index = ++this.metricIndex;
|
||||
if (index >= this.maxMetricStreams)
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.MetricInstrumentIgnored(metricName, instrument.Meter.Name, "Maximum allowed Metric streams for the provider exceeded.", "Use MeterProviderBuilder.AddView to drop unused instruments. Or use MeterProviderBuilder.SetMaxMetricStreams to configure MeterProvider to allow higher limit.");
|
||||
OpenTelemetrySdkEventSource.Log.MetricInstrumentIgnored(metricStreamIdentity.InstrumentName, metricStreamIdentity.MeterName, "Maximum allowed Metric streams for the provider exceeded.", "Use MeterProviderBuilder.AddView to drop unused instruments. Or use MeterProviderBuilder.SetMaxMetricStreams to configure MeterProvider to allow higher limit.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Metric metric;
|
||||
metric = new Metric(metricStreamIdentity, this.Temporality, this.maxMetricPointsPerMetricStream, histogramBucketBounds, tagKeysInteresting);
|
||||
metric = new Metric(metricStreamIdentity, this.Temporality, this.maxMetricPointsPerMetricStream, metricStreamIdentity.HistogramBucketBounds, metricStreamIdentity.TagKeys);
|
||||
|
||||
this.instrumentIdentityToMetric[metricStreamIdentity] = metric;
|
||||
this.metrics[index] = metric;
|
||||
metrics.Add(metric);
|
||||
this.metricStreamNames.Add(metricStreamName);
|
||||
this.metricStreamNames.Add(metricStreamIdentity.MetricStreamName);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ namespace OpenTelemetry.Metrics
|
|||
/// Note: All metrics for the given instrument will be dropped (not
|
||||
/// collected).
|
||||
/// </remarks>
|
||||
public static MetricStreamConfiguration Drop { get; } = new MetricStreamConfiguration();
|
||||
public static MetricStreamConfiguration Drop { get; } = new MetricStreamConfiguration { ViewId = -1 };
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the optional name of the metric stream.
|
||||
|
|
@ -107,6 +107,8 @@ namespace OpenTelemetry.Metrics
|
|||
|
||||
internal string[] CopiedTagKeys { get; private set; }
|
||||
|
||||
internal int? ViewId { get; set; }
|
||||
|
||||
// TODO: MetricPoints caps can be configured here on
|
||||
// a per stream basis, when we add such a capability
|
||||
// in the future.
|
||||
|
|
|
|||
|
|
@ -24,34 +24,18 @@ namespace OpenTelemetry.Metrics
|
|||
private static readonly StringArrayEqualityComparer StringArrayComparer = new StringArrayEqualityComparer();
|
||||
private readonly int hashCode;
|
||||
|
||||
public MetricStreamIdentity(Meter meter, string instrumentName, string unit, string description, Type instrumentType, string[] tagKeys, double[] histogramBucketBounds)
|
||||
public MetricStreamIdentity(Instrument instrument, MetricStreamConfiguration metricStreamConfiguration)
|
||||
{
|
||||
this.MeterName = meter.Name;
|
||||
this.MeterVersion = meter.Version ?? string.Empty;
|
||||
this.InstrumentName = instrumentName;
|
||||
this.Unit = unit ?? string.Empty;
|
||||
this.Description = description ?? string.Empty;
|
||||
this.InstrumentType = instrumentType;
|
||||
|
||||
if (tagKeys != null && tagKeys.Length > 0)
|
||||
{
|
||||
this.TagKeys = new string[tagKeys.Length];
|
||||
tagKeys.CopyTo(this.TagKeys, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.TagKeys = null;
|
||||
}
|
||||
|
||||
if (histogramBucketBounds != null && histogramBucketBounds.Length > 0)
|
||||
{
|
||||
this.HistogramBucketBounds = new double[histogramBucketBounds.Length];
|
||||
histogramBucketBounds.CopyTo(this.HistogramBucketBounds, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.HistogramBucketBounds = null;
|
||||
}
|
||||
this.MeterName = instrument.Meter.Name;
|
||||
this.MeterVersion = instrument.Meter.Version ?? string.Empty;
|
||||
this.InstrumentName = metricStreamConfiguration?.Name ?? instrument.Name;
|
||||
this.Unit = instrument.Unit ?? string.Empty;
|
||||
this.Description = metricStreamConfiguration?.Description ?? instrument.Description ?? string.Empty;
|
||||
this.InstrumentType = instrument.GetType();
|
||||
this.ViewId = metricStreamConfiguration?.ViewId;
|
||||
this.MetricStreamName = $"{this.MeterName}.{this.MeterVersion}.{this.InstrumentName}";
|
||||
this.TagKeys = metricStreamConfiguration?.CopiedTagKeys;
|
||||
this.HistogramBucketBounds = (metricStreamConfiguration as ExplicitBucketHistogramConfiguration)?.CopiedBoundaries;
|
||||
|
||||
unchecked
|
||||
{
|
||||
|
|
@ -62,6 +46,7 @@ namespace OpenTelemetry.Metrics
|
|||
hash = (hash * 31) + this.InstrumentName.GetHashCode();
|
||||
hash = this.Unit == null ? hash : (hash * 31) + this.Unit.GetHashCode();
|
||||
hash = this.Description == null ? hash : (hash * 31) + this.Description.GetHashCode();
|
||||
hash = !this.ViewId.HasValue ? hash : (hash * 31) + this.ViewId.Value;
|
||||
hash = this.TagKeys == null ? hash : (hash * 31) + StringArrayComparer.GetHashCode(this.TagKeys);
|
||||
if (this.HistogramBucketBounds != null)
|
||||
{
|
||||
|
|
@ -88,6 +73,10 @@ namespace OpenTelemetry.Metrics
|
|||
|
||||
public Type InstrumentType { get; }
|
||||
|
||||
public int? ViewId { get; }
|
||||
|
||||
public string MetricStreamName { get; }
|
||||
|
||||
public string[] TagKeys { get; }
|
||||
|
||||
public double[] HistogramBucketBounds { get; }
|
||||
|
|
@ -109,6 +98,7 @@ namespace OpenTelemetry.Metrics
|
|||
&& this.InstrumentName == other.InstrumentName
|
||||
&& this.Unit == other.Unit
|
||||
&& this.Description == other.Description
|
||||
&& this.ViewId == other.ViewId
|
||||
&& StringArrayComparer.Equals(this.TagKeys, other.TagKeys)
|
||||
&& HistogramBoundsEqual(this.HistogramBucketBounds, other.HistogramBucketBounds);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ using Xunit.Abstractions;
|
|||
|
||||
namespace OpenTelemetry.Metrics.Tests
|
||||
{
|
||||
public class MetricApiTest
|
||||
public class MetricApiTest : MetricTestsBase
|
||||
{
|
||||
private const int MaxTimeToAllowForFlush = 10000;
|
||||
private static readonly int NumberOfThreads = Environment.ProcessorCount;
|
||||
|
|
@ -366,279 +366,6 @@ namespace OpenTelemetry.Metrics.Tests
|
|||
Assert.Equal(20D, metricPoint2.GetHistogramSum());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuplicateInstrumentRegistration_WithViews_DuplicateInstruments_DifferentDescription()
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}");
|
||||
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.AddView("instrumentName", new MetricStreamConfiguration() { Description = "newDescription1" })
|
||||
.AddView("instrumentName", new MetricStreamConfiguration() { Description = "newDescription2" })
|
||||
.AddInMemoryExporter(exportedItems);
|
||||
|
||||
using var meterProvider = meterProviderBuilder.Build();
|
||||
|
||||
var instrument = meter.CreateCounter<long>("instrumentName", "instrumentUnit", "instrumentDescription");
|
||||
|
||||
instrument.Add(10);
|
||||
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
Assert.Equal(2, exportedItems.Count);
|
||||
|
||||
var metric1 = exportedItems[0];
|
||||
var metric2 = exportedItems[1];
|
||||
Assert.Equal("newDescription1", metric1.Description);
|
||||
Assert.Equal("newDescription2", metric2.Description);
|
||||
|
||||
List<MetricPoint> metric1MetricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric1.GetMetricPoints())
|
||||
{
|
||||
metric1MetricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metric1MetricPoints);
|
||||
var metricPoint1 = metric1MetricPoints[0];
|
||||
Assert.Equal(10, metricPoint1.GetSumLong());
|
||||
|
||||
List<MetricPoint> metric2MetricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric2.GetMetricPoints())
|
||||
{
|
||||
metric2MetricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metric2MetricPoints);
|
||||
var metricPoint2 = metric2MetricPoints[0];
|
||||
Assert.Equal(10, metricPoint2.GetSumLong());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuplicateInstrumentRegistration_WithViews_TwoInstruments_ThreeStreams()
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}");
|
||||
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.AddView((instrument) =>
|
||||
{
|
||||
return new MetricStreamConfiguration() { Name = "MetricStreamA", Description = "description" };
|
||||
})
|
||||
.AddView((instrument) =>
|
||||
{
|
||||
return instrument.Description == "description1"
|
||||
? new MetricStreamConfiguration() { Name = "MetricStreamB" }
|
||||
: new MetricStreamConfiguration() { Name = "MetricStreamC" };
|
||||
})
|
||||
.AddInMemoryExporter(exportedItems);
|
||||
|
||||
using var meterProvider = meterProviderBuilder.Build();
|
||||
|
||||
var instrument1 = meter.CreateCounter<long>("name", "unit", "description1");
|
||||
var instrument2 = meter.CreateCounter<long>("name", "unit", "description2");
|
||||
|
||||
instrument1.Add(10);
|
||||
instrument2.Add(10);
|
||||
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
Assert.Equal(3, exportedItems.Count);
|
||||
|
||||
var metricA = exportedItems[0];
|
||||
var metricB = exportedItems[1];
|
||||
var metricC = exportedItems[2];
|
||||
|
||||
Assert.Equal("MetricStreamA", metricA.Name);
|
||||
Assert.Equal(20, GetAggregatedValue(metricA));
|
||||
|
||||
Assert.Equal("MetricStreamB", metricB.Name);
|
||||
Assert.Equal(10, GetAggregatedValue(metricB));
|
||||
|
||||
Assert.Equal("MetricStreamC", metricC.Name);
|
||||
Assert.Equal(10, GetAggregatedValue(metricC));
|
||||
|
||||
long GetAggregatedValue(Metric metric)
|
||||
{
|
||||
var metricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric.GetMetricPoints())
|
||||
{
|
||||
metricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metricPoints);
|
||||
return metricPoints[0].GetSumLong();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuplicateInstrumentRegistration_WithViews_TwoIdenticalInstruments_TwoViews_DifferentTags()
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}");
|
||||
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.AddView((instrument) =>
|
||||
{
|
||||
return new MetricStreamConfiguration { TagKeys = new[] { "key1" } };
|
||||
})
|
||||
.AddView((instrument) =>
|
||||
{
|
||||
return new MetricStreamConfiguration { TagKeys = new[] { "key2" } };
|
||||
})
|
||||
.AddInMemoryExporter(exportedItems);
|
||||
|
||||
using var meterProvider = meterProviderBuilder.Build();
|
||||
|
||||
var instrument1 = meter.CreateCounter<long>("name");
|
||||
var instrument2 = meter.CreateCounter<long>("name");
|
||||
|
||||
var tags = new KeyValuePair<string, object>[]
|
||||
{
|
||||
new("key1", "value"),
|
||||
new("key2", "value"),
|
||||
};
|
||||
|
||||
instrument1.Add(10, tags);
|
||||
instrument2.Add(10, tags);
|
||||
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
|
||||
Assert.Equal(2, exportedItems.Count);
|
||||
var metric1 = new List<Metric>() { exportedItems[0] };
|
||||
var metric2 = new List<Metric>() { exportedItems[1] };
|
||||
var tag1 = new List<KeyValuePair<string, object>> { tags[0] };
|
||||
var tag2 = new List<KeyValuePair<string, object>> { tags[1] };
|
||||
|
||||
Assert.Equal("name", exportedItems[0].Name);
|
||||
Assert.Equal("name", exportedItems[1].Name);
|
||||
Assert.Equal(20, GetLongSum(metric1));
|
||||
Assert.Equal(20, GetLongSum(metric2));
|
||||
CheckTagsForNthMetricPoint(metric1, tag1, 1);
|
||||
CheckTagsForNthMetricPoint(metric2, tag2, 1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuplicateInstrumentRegistration_WithViews_TwoIdenticalInstruments_TwoViews_SameTags()
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}");
|
||||
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.AddView((instrument) =>
|
||||
{
|
||||
return new MetricStreamConfiguration { TagKeys = new[] { "key1" } };
|
||||
})
|
||||
.AddView((instrument) =>
|
||||
{
|
||||
return new MetricStreamConfiguration { TagKeys = new[] { "key1" } };
|
||||
})
|
||||
.AddInMemoryExporter(exportedItems);
|
||||
|
||||
using var meterProvider = meterProviderBuilder.Build();
|
||||
|
||||
var instrument1 = meter.CreateCounter<long>("name");
|
||||
var instrument2 = meter.CreateCounter<long>("name");
|
||||
|
||||
var tags = new KeyValuePair<string, object>[]
|
||||
{
|
||||
new("key1", "value"),
|
||||
new("key2", "value"),
|
||||
};
|
||||
|
||||
instrument1.Add(10, tags);
|
||||
instrument2.Add(10, tags);
|
||||
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
|
||||
Assert.Single(exportedItems);
|
||||
var metric1 = new List<Metric>() { exportedItems[0] };
|
||||
var tag1 = new List<KeyValuePair<string, object>> { tags[0] };
|
||||
|
||||
Assert.Equal("name", exportedItems[0].Name);
|
||||
Assert.Equal(20, GetLongSum(metric1));
|
||||
CheckTagsForNthMetricPoint(metric1, tag1, 1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuplicateInstrumentRegistration_WithViews_TwoIdenticalInstruments_TwoViews_DifferentHistogramBounds()
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}");
|
||||
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.AddView((instrument) =>
|
||||
{
|
||||
return new ExplicitBucketHistogramConfiguration { Boundaries = new[] { 5.0, 10.0 } };
|
||||
})
|
||||
.AddView((instrument) =>
|
||||
{
|
||||
return new ExplicitBucketHistogramConfiguration { Boundaries = new[] { 10.0, 20.0 } };
|
||||
})
|
||||
.AddInMemoryExporter(exportedItems);
|
||||
|
||||
using var meterProvider = meterProviderBuilder.Build();
|
||||
|
||||
var instrument1 = meter.CreateHistogram<long>("name");
|
||||
var instrument2 = meter.CreateHistogram<long>("name");
|
||||
|
||||
instrument1.Record(15);
|
||||
instrument2.Record(15);
|
||||
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
|
||||
Assert.Equal(2, exportedItems.Count);
|
||||
var metric1 = exportedItems[0];
|
||||
var metric2 = exportedItems[1];
|
||||
|
||||
Assert.Equal("name", exportedItems[0].Name);
|
||||
Assert.Equal("name", exportedItems[1].Name);
|
||||
|
||||
var metricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric1.GetMetricPoints())
|
||||
{
|
||||
metricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metricPoints);
|
||||
var metricPoint = metricPoints[0];
|
||||
Assert.Equal(2, metricPoint.GetHistogramCount());
|
||||
Assert.Equal(30, metricPoint.GetHistogramSum());
|
||||
|
||||
var index = 0;
|
||||
var actualCount = 0;
|
||||
var expectedBucketCounts = new long[] { 0, 0, 2 };
|
||||
foreach (var histogramMeasurement in metricPoint.GetHistogramBuckets())
|
||||
{
|
||||
Assert.Equal(expectedBucketCounts[index], histogramMeasurement.BucketCount);
|
||||
index++;
|
||||
actualCount++;
|
||||
}
|
||||
|
||||
metricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric2.GetMetricPoints())
|
||||
{
|
||||
metricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metricPoints);
|
||||
metricPoint = metricPoints[0];
|
||||
Assert.Equal(2, metricPoint.GetHistogramCount());
|
||||
Assert.Equal(30, metricPoint.GetHistogramSum());
|
||||
|
||||
index = 0;
|
||||
actualCount = 0;
|
||||
expectedBucketCounts = new long[] { 0, 2, 0 };
|
||||
foreach (var histogramMeasurement in metricPoint.GetHistogramBuckets())
|
||||
{
|
||||
Assert.Equal(expectedBucketCounts[index], histogramMeasurement.BucketCount);
|
||||
index++;
|
||||
actualCount++;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuplicateInstrumentNamesFromDifferentMetersWithSameNameDifferentVersion()
|
||||
{
|
||||
|
|
@ -1544,109 +1271,6 @@ namespace OpenTelemetry.Metrics.Tests
|
|||
Assert.Empty(exportedItems);
|
||||
}
|
||||
|
||||
private static void ValidateMetricPointTags(List<KeyValuePair<string, object>> expectedTags, ReadOnlyTagCollection actualTags)
|
||||
{
|
||||
int tagIndex = 0;
|
||||
foreach (var tag in actualTags)
|
||||
{
|
||||
Assert.Equal(expectedTags[tagIndex].Key, tag.Key);
|
||||
Assert.Equal(expectedTags[tagIndex].Value, tag.Value);
|
||||
tagIndex++;
|
||||
}
|
||||
|
||||
Assert.Equal(expectedTags.Count, tagIndex);
|
||||
}
|
||||
|
||||
private static long GetLongSum(List<Metric> metrics)
|
||||
{
|
||||
long sum = 0;
|
||||
foreach (var metric in metrics)
|
||||
{
|
||||
foreach (ref readonly var metricPoint in metric.GetMetricPoints())
|
||||
{
|
||||
if (metric.MetricType.IsSum())
|
||||
{
|
||||
sum += metricPoint.GetSumLong();
|
||||
}
|
||||
else
|
||||
{
|
||||
sum += metricPoint.GetGaugeLastValueLong();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
private static double GetDoubleSum(List<Metric> metrics)
|
||||
{
|
||||
double sum = 0;
|
||||
foreach (var metric in metrics)
|
||||
{
|
||||
foreach (ref readonly var metricPoint in metric.GetMetricPoints())
|
||||
{
|
||||
if (metric.MetricType.IsSum())
|
||||
{
|
||||
sum += metricPoint.GetSumDouble();
|
||||
}
|
||||
else
|
||||
{
|
||||
sum += metricPoint.GetGaugeLastValueDouble();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
private static int GetNumberOfMetricPoints(List<Metric> metrics)
|
||||
{
|
||||
int count = 0;
|
||||
foreach (var metric in metrics)
|
||||
{
|
||||
foreach (ref readonly var metricPoint in metric.GetMetricPoints())
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private static MetricPoint? GetFirstMetricPoint(List<Metric> metrics)
|
||||
{
|
||||
foreach (var metric in metrics)
|
||||
{
|
||||
foreach (ref readonly var metricPoint in metric.GetMetricPoints())
|
||||
{
|
||||
return metricPoint;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Provide tags input sorted by Key
|
||||
private static void CheckTagsForNthMetricPoint(List<Metric> metrics, List<KeyValuePair<string, object>> tags, int n)
|
||||
{
|
||||
var metric = metrics[0];
|
||||
var metricPointEnumerator = metric.GetMetricPoints().GetEnumerator();
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
Assert.True(metricPointEnumerator.MoveNext());
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
var metricPoint = metricPointEnumerator.Current;
|
||||
foreach (var tag in metricPoint.Tags)
|
||||
{
|
||||
Assert.Equal(tags[index].Key, tag.Key);
|
||||
Assert.Equal(tags[index].Value, tag.Value);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
private static void CounterUpdateThread<T>(object obj)
|
||||
where T : struct, IComparable
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,126 @@
|
|||
// <copyright file="MetricTestsBase.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Metrics.Tests;
|
||||
|
||||
public class MetricTestsBase
|
||||
{
|
||||
public static void ValidateMetricPointTags(List<KeyValuePair<string, object>> expectedTags, ReadOnlyTagCollection actualTags)
|
||||
{
|
||||
int tagIndex = 0;
|
||||
foreach (var tag in actualTags)
|
||||
{
|
||||
Assert.Equal(expectedTags[tagIndex].Key, tag.Key);
|
||||
Assert.Equal(expectedTags[tagIndex].Value, tag.Value);
|
||||
tagIndex++;
|
||||
}
|
||||
|
||||
Assert.Equal(expectedTags.Count, tagIndex);
|
||||
}
|
||||
|
||||
public static long GetLongSum(List<Metric> metrics)
|
||||
{
|
||||
long sum = 0;
|
||||
foreach (var metric in metrics)
|
||||
{
|
||||
foreach (ref readonly var metricPoint in metric.GetMetricPoints())
|
||||
{
|
||||
if (metric.MetricType.IsSum())
|
||||
{
|
||||
sum += metricPoint.GetSumLong();
|
||||
}
|
||||
else
|
||||
{
|
||||
sum += metricPoint.GetGaugeLastValueLong();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static double GetDoubleSum(List<Metric> metrics)
|
||||
{
|
||||
double sum = 0;
|
||||
foreach (var metric in metrics)
|
||||
{
|
||||
foreach (ref readonly var metricPoint in metric.GetMetricPoints())
|
||||
{
|
||||
if (metric.MetricType.IsSum())
|
||||
{
|
||||
sum += metricPoint.GetSumDouble();
|
||||
}
|
||||
else
|
||||
{
|
||||
sum += metricPoint.GetGaugeLastValueDouble();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static int GetNumberOfMetricPoints(List<Metric> metrics)
|
||||
{
|
||||
int count = 0;
|
||||
foreach (var metric in metrics)
|
||||
{
|
||||
foreach (ref readonly var metricPoint in metric.GetMetricPoints())
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public static MetricPoint? GetFirstMetricPoint(List<Metric> metrics)
|
||||
{
|
||||
foreach (var metric in metrics)
|
||||
{
|
||||
foreach (ref readonly var metricPoint in metric.GetMetricPoints())
|
||||
{
|
||||
return metricPoint;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Provide tags input sorted by Key
|
||||
public static void CheckTagsForNthMetricPoint(List<Metric> metrics, List<KeyValuePair<string, object>> tags, int n)
|
||||
{
|
||||
var metric = metrics[0];
|
||||
var metricPointEnumerator = metric.GetMetricPoints().GetEnumerator();
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
Assert.True(metricPointEnumerator.MoveNext());
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
var metricPoint = metricPointEnumerator.Current;
|
||||
foreach (var tag in metricPoint.Tags)
|
||||
{
|
||||
Assert.Equal(tags[index].Key, tag.Key);
|
||||
Assert.Equal(tags[index].Value, tag.Value);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ using Xunit;
|
|||
|
||||
namespace OpenTelemetry.Metrics.Tests
|
||||
{
|
||||
public class MetricViewTests
|
||||
public class MetricViewTests : MetricTestsBase
|
||||
{
|
||||
private const int MaxTimeToAllowForFlush = 10000;
|
||||
|
||||
|
|
@ -445,16 +445,16 @@ namespace OpenTelemetry.Metrics.Tests
|
|||
.AddInMemoryExporter(exportedItems)
|
||||
.Build();
|
||||
|
||||
// Expecting two metric stream.
|
||||
// the .AddView("name1", "renamedStream2")
|
||||
// won't produce new Metric as the name
|
||||
// conflicts.
|
||||
// Expecting three metric stream.
|
||||
// the second .AddView("name1", "renamedStream2")
|
||||
// produces a conflicting metric stream.
|
||||
var counterLong = meter.CreateCounter<long>("name1");
|
||||
counterLong.Add(10);
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
Assert.Equal(2, exportedItems.Count);
|
||||
Assert.Equal(3, exportedItems.Count);
|
||||
Assert.Equal("renamedStream1", exportedItems[0].Name);
|
||||
Assert.Equal("renamedStream2", exportedItems[1].Name);
|
||||
Assert.Equal("renamedStream2", exportedItems[2].Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -758,5 +758,376 @@ namespace OpenTelemetry.Metrics.Tests
|
|||
Assert.Single(exportedItems);
|
||||
Assert.Equal("server.request_renamed", exportedItems[0].Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ViewConflict_OneInstrument_DifferentDescription()
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}");
|
||||
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.AddView("instrumentName", new MetricStreamConfiguration() { Description = "newDescription1" })
|
||||
.AddView("instrumentName", new MetricStreamConfiguration() { Description = "newDescription2" })
|
||||
.AddInMemoryExporter(exportedItems);
|
||||
|
||||
using var meterProvider = meterProviderBuilder.Build();
|
||||
|
||||
var instrument = meter.CreateCounter<long>("instrumentName", "instrumentUnit", "instrumentDescription");
|
||||
|
||||
instrument.Add(10);
|
||||
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
Assert.Equal(2, exportedItems.Count);
|
||||
|
||||
var metric1 = exportedItems[0];
|
||||
var metric2 = exportedItems[1];
|
||||
Assert.Equal("newDescription1", metric1.Description);
|
||||
Assert.Equal("newDescription2", metric2.Description);
|
||||
|
||||
List<MetricPoint> metric1MetricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric1.GetMetricPoints())
|
||||
{
|
||||
metric1MetricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metric1MetricPoints);
|
||||
var metricPoint1 = metric1MetricPoints[0];
|
||||
Assert.Equal(10, metricPoint1.GetSumLong());
|
||||
|
||||
List<MetricPoint> metric2MetricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric2.GetMetricPoints())
|
||||
{
|
||||
metric2MetricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metric2MetricPoints);
|
||||
var metricPoint2 = metric2MetricPoints[0];
|
||||
Assert.Equal(10, metricPoint2.GetSumLong());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ViewConflict_TwoDistinctInstruments_ThreeStreams()
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}");
|
||||
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.AddView((instrument) =>
|
||||
{
|
||||
return new MetricStreamConfiguration() { Name = "MetricStreamA", Description = "description" };
|
||||
})
|
||||
.AddView((instrument) =>
|
||||
{
|
||||
return instrument.Description == "description1"
|
||||
? new MetricStreamConfiguration() { Name = "MetricStreamB" }
|
||||
: new MetricStreamConfiguration() { Name = "MetricStreamC" };
|
||||
})
|
||||
.AddInMemoryExporter(exportedItems);
|
||||
|
||||
using var meterProvider = meterProviderBuilder.Build();
|
||||
|
||||
var instrument1 = meter.CreateCounter<long>("name", "unit", "description1");
|
||||
var instrument2 = meter.CreateCounter<long>("name", "unit", "description2");
|
||||
|
||||
instrument1.Add(10);
|
||||
instrument2.Add(10);
|
||||
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
Assert.Equal(3, exportedItems.Count);
|
||||
|
||||
var metricA = exportedItems[0];
|
||||
var metricB = exportedItems[1];
|
||||
var metricC = exportedItems[2];
|
||||
|
||||
Assert.Equal("MetricStreamA", metricA.Name);
|
||||
Assert.Equal(20, GetAggregatedValue(metricA));
|
||||
|
||||
Assert.Equal("MetricStreamB", metricB.Name);
|
||||
Assert.Equal(10, GetAggregatedValue(metricB));
|
||||
|
||||
Assert.Equal("MetricStreamC", metricC.Name);
|
||||
Assert.Equal(10, GetAggregatedValue(metricC));
|
||||
|
||||
long GetAggregatedValue(Metric metric)
|
||||
{
|
||||
var metricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric.GetMetricPoints())
|
||||
{
|
||||
metricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metricPoints);
|
||||
return metricPoints[0].GetSumLong();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ViewConflict_TwoIdenticalInstruments_TwoViews_DifferentTags()
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}");
|
||||
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.AddView((instrument) =>
|
||||
{
|
||||
return new MetricStreamConfiguration { TagKeys = new[] { "key1" } };
|
||||
})
|
||||
.AddView((instrument) =>
|
||||
{
|
||||
return new MetricStreamConfiguration { TagKeys = new[] { "key2" } };
|
||||
})
|
||||
.AddInMemoryExporter(exportedItems);
|
||||
|
||||
using var meterProvider = meterProviderBuilder.Build();
|
||||
|
||||
var instrument1 = meter.CreateCounter<long>("name");
|
||||
var instrument2 = meter.CreateCounter<long>("name");
|
||||
|
||||
var tags = new KeyValuePair<string, object>[]
|
||||
{
|
||||
new("key1", "value"),
|
||||
new("key2", "value"),
|
||||
};
|
||||
|
||||
instrument1.Add(10, tags);
|
||||
instrument2.Add(10, tags);
|
||||
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
|
||||
Assert.Equal(2, exportedItems.Count);
|
||||
var metric1 = new List<Metric>() { exportedItems[0] };
|
||||
var metric2 = new List<Metric>() { exportedItems[1] };
|
||||
var tag1 = new List<KeyValuePair<string, object>> { tags[0] };
|
||||
var tag2 = new List<KeyValuePair<string, object>> { tags[1] };
|
||||
|
||||
Assert.Equal("name", exportedItems[0].Name);
|
||||
Assert.Equal("name", exportedItems[1].Name);
|
||||
Assert.Equal(20, GetLongSum(metric1));
|
||||
Assert.Equal(20, GetLongSum(metric2));
|
||||
CheckTagsForNthMetricPoint(metric1, tag1, 1);
|
||||
CheckTagsForNthMetricPoint(metric2, tag2, 1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ViewConflict_TwoIdenticalInstruments_TwoViews_SameTags()
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}");
|
||||
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.AddView((instrument) =>
|
||||
{
|
||||
return new MetricStreamConfiguration { TagKeys = new[] { "key1" } };
|
||||
})
|
||||
.AddView((instrument) =>
|
||||
{
|
||||
return new MetricStreamConfiguration { TagKeys = new[] { "key1" } };
|
||||
})
|
||||
.AddInMemoryExporter(exportedItems);
|
||||
|
||||
using var meterProvider = meterProviderBuilder.Build();
|
||||
|
||||
var instrument1 = meter.CreateCounter<long>("name");
|
||||
var instrument2 = meter.CreateCounter<long>("name");
|
||||
|
||||
var tags = new KeyValuePair<string, object>[]
|
||||
{
|
||||
new("key1", "value"),
|
||||
new("key2", "value"),
|
||||
};
|
||||
|
||||
instrument1.Add(10, tags);
|
||||
instrument2.Add(10, tags);
|
||||
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
|
||||
Assert.Equal(2, exportedItems.Count);
|
||||
|
||||
var metric1 = new List<Metric>() { exportedItems[0] };
|
||||
var tag1 = new List<KeyValuePair<string, object>> { tags[0] };
|
||||
Assert.Equal("name", exportedItems[0].Name);
|
||||
Assert.Equal(20, GetLongSum(metric1));
|
||||
CheckTagsForNthMetricPoint(metric1, tag1, 1);
|
||||
|
||||
var metric2 = new List<Metric>() { exportedItems[1] };
|
||||
var tag2 = new List<KeyValuePair<string, object>> { tags[0] };
|
||||
Assert.Equal("name", exportedItems[1].Name);
|
||||
Assert.Equal(20, GetLongSum(metric2));
|
||||
CheckTagsForNthMetricPoint(metric2, tag2, 1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ViewConflict_TwoIdenticalInstruments_TwoViews_DifferentHistogramBounds()
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}");
|
||||
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.AddView((instrument) =>
|
||||
{
|
||||
return new ExplicitBucketHistogramConfiguration { Boundaries = new[] { 5.0, 10.0 } };
|
||||
})
|
||||
.AddView((instrument) =>
|
||||
{
|
||||
return new ExplicitBucketHistogramConfiguration { Boundaries = new[] { 10.0, 20.0 } };
|
||||
})
|
||||
.AddInMemoryExporter(exportedItems);
|
||||
|
||||
using var meterProvider = meterProviderBuilder.Build();
|
||||
|
||||
var instrument1 = meter.CreateHistogram<long>("name");
|
||||
var instrument2 = meter.CreateHistogram<long>("name");
|
||||
|
||||
instrument1.Record(15);
|
||||
instrument2.Record(15);
|
||||
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
|
||||
Assert.Equal(2, exportedItems.Count);
|
||||
var metric1 = exportedItems[0];
|
||||
var metric2 = exportedItems[1];
|
||||
|
||||
Assert.Equal("name", exportedItems[0].Name);
|
||||
Assert.Equal("name", exportedItems[1].Name);
|
||||
|
||||
var metricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric1.GetMetricPoints())
|
||||
{
|
||||
metricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metricPoints);
|
||||
var metricPoint = metricPoints[0];
|
||||
Assert.Equal(2, metricPoint.GetHistogramCount());
|
||||
Assert.Equal(30, metricPoint.GetHistogramSum());
|
||||
|
||||
var index = 0;
|
||||
var actualCount = 0;
|
||||
var expectedBucketCounts = new long[] { 0, 0, 2 };
|
||||
foreach (var histogramMeasurement in metricPoint.GetHistogramBuckets())
|
||||
{
|
||||
Assert.Equal(expectedBucketCounts[index], histogramMeasurement.BucketCount);
|
||||
index++;
|
||||
actualCount++;
|
||||
}
|
||||
|
||||
metricPoints = new List<MetricPoint>();
|
||||
foreach (ref readonly var mp in metric2.GetMetricPoints())
|
||||
{
|
||||
metricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metricPoints);
|
||||
metricPoint = metricPoints[0];
|
||||
Assert.Equal(2, metricPoint.GetHistogramCount());
|
||||
Assert.Equal(30, metricPoint.GetHistogramSum());
|
||||
|
||||
index = 0;
|
||||
actualCount = 0;
|
||||
expectedBucketCounts = new long[] { 0, 2, 0 };
|
||||
foreach (var histogramMeasurement in metricPoint.GetHistogramBuckets())
|
||||
{
|
||||
Assert.Equal(expectedBucketCounts[index], histogramMeasurement.BucketCount);
|
||||
index++;
|
||||
actualCount++;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ViewConflict_TwoInstruments_OneMatchesView()
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}");
|
||||
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.AddView((instrument) =>
|
||||
{
|
||||
if (instrument.Name == "name")
|
||||
{
|
||||
return new MetricStreamConfiguration { Name = "othername", TagKeys = new[] { "key1" } };
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.AddInMemoryExporter(exportedItems);
|
||||
|
||||
using var meterProvider = meterProviderBuilder.Build();
|
||||
|
||||
var instrument1 = meter.CreateCounter<long>("name");
|
||||
var instrument2 = meter.CreateCounter<long>("othername");
|
||||
|
||||
var tags = new KeyValuePair<string, object>[]
|
||||
{
|
||||
new("key1", "value"),
|
||||
new("key2", "value"),
|
||||
};
|
||||
|
||||
instrument1.Add(10, tags);
|
||||
instrument2.Add(10, tags);
|
||||
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
|
||||
Assert.Equal(2, exportedItems.Count);
|
||||
var metric1 = new List<Metric>() { exportedItems[0] };
|
||||
var metric2 = new List<Metric>() { exportedItems[1] };
|
||||
|
||||
var tags1 = new List<KeyValuePair<string, object>> { tags[0] };
|
||||
var tags2 = new List<KeyValuePair<string, object>> { tags[0], tags[1] };
|
||||
|
||||
Assert.Equal("othername", exportedItems[0].Name);
|
||||
Assert.Equal("othername", exportedItems[1].Name);
|
||||
|
||||
Assert.Equal(10, GetLongSum(metric1));
|
||||
Assert.Equal(10, GetLongSum(metric2));
|
||||
|
||||
CheckTagsForNthMetricPoint(metric1, tags1, 1);
|
||||
CheckTagsForNthMetricPoint(metric2, tags2, 1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ViewConflict_TwoInstruments_ConflictAvoidedBecauseSecondInstrumentIsDropped()
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}");
|
||||
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.AddView((instrument) =>
|
||||
{
|
||||
if (instrument.Name == "name")
|
||||
{
|
||||
return new MetricStreamConfiguration { Name = "othername" };
|
||||
}
|
||||
else
|
||||
{
|
||||
return MetricStreamConfiguration.Drop;
|
||||
}
|
||||
})
|
||||
.AddInMemoryExporter(exportedItems);
|
||||
|
||||
using var meterProvider = meterProviderBuilder.Build();
|
||||
|
||||
var instrument1 = meter.CreateCounter<long>("name");
|
||||
var instrument2 = meter.CreateCounter<long>("othername");
|
||||
|
||||
instrument1.Add(10);
|
||||
instrument2.Add(20);
|
||||
|
||||
meterProvider.ForceFlush(MaxTimeToAllowForFlush);
|
||||
|
||||
Assert.Single(exportedItems);
|
||||
var metric1 = new List<Metric>() { exportedItems[0] };
|
||||
|
||||
Assert.Equal("othername", exportedItems[0].Name);
|
||||
Assert.Equal(10, GetLongSum(metric1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue