From a7f34009778fae54a8b6623186a9876216634a12 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 5 Mar 2024 16:59:52 -0800 Subject: [PATCH] [sdk-metrics] Pass all tags supplied at measurement to ExemplarReservoir.Offer methods (#5414) --- src/OpenTelemetry/Metrics/AggregatorStore.cs | 4 +- .../Metrics/Exemplar/Exemplar.cs | 5 +- .../Metrics/MetricExemplarTests.cs | 100 +++++++++++++----- 3 files changed, 82 insertions(+), 27 deletions(-) diff --git a/src/OpenTelemetry/Metrics/AggregatorStore.cs b/src/OpenTelemetry/Metrics/AggregatorStore.cs index e11eb9ee78..ca71b6f10d 100644 --- a/src/OpenTelemetry/Metrics/AggregatorStore.cs +++ b/src/OpenTelemetry/Metrics/AggregatorStore.cs @@ -959,7 +959,7 @@ private void UpdateLong(long value, ReadOnlySpan> { var index = this.FindMetricAggregatorsDefault(tags); - this.UpdateLongMetricPoint(index, value, tags: default); + this.UpdateLongMetricPoint(index, value, tags); } private void UpdateLongCustomTags(long value, ReadOnlySpan> tags) @@ -1014,7 +1014,7 @@ private void UpdateDouble(double value, ReadOnlySpan> tags) diff --git a/src/OpenTelemetry/Metrics/Exemplar/Exemplar.cs b/src/OpenTelemetry/Metrics/Exemplar/Exemplar.cs index 1c2d27b99e..d9eda128f1 100644 --- a/src/OpenTelemetry/Metrics/Exemplar/Exemplar.cs +++ b/src/OpenTelemetry/Metrics/Exemplar/Exemplar.cs @@ -131,7 +131,10 @@ internal void Update(in ExemplarMeasurement measurement) this.SpanId = default; } - this.StoreRawTags(measurement.Tags); + if (this.ViewDefinedTagKeys != null) + { + this.StoreRawTags(measurement.Tags); + } } internal void Reset() diff --git a/test/OpenTelemetry.Tests/Metrics/MetricExemplarTests.cs b/test/OpenTelemetry.Tests/Metrics/MetricExemplarTests.cs index 4c8a4c4153..2499c825b6 100644 --- a/test/OpenTelemetry.Tests/Metrics/MetricExemplarTests.cs +++ b/test/OpenTelemetry.Tests/Metrics/MetricExemplarTests.cs @@ -689,49 +689,79 @@ public void TestTraceBasedExemplarFilter(bool enableTracing) } } - [Fact] - public void TestExemplarsFilterTags() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void TestExemplarsFilterTags(bool enableTagFiltering) { - DateTime testStartTime = DateTime.UtcNow; var exportedItems = new List(); using var meter = new Meter($"{Utils.GetCurrentMethodName()}"); + var histogram = meter.CreateHistogram("testHistogram"); + TestExemplarReservoir? testExemplarReservoir = null; + using var container = this.BuildMeterProvider(out var meterProvider, builder => builder .AddMeter(meter.Name) .SetExemplarFilter(ExemplarFilterType.AlwaysOn) - .AddView(histogram.Name, new MetricStreamConfiguration() { TagKeys = new string[] { "key1" } }) - .AddInMemoryExporter(exportedItems, metricReaderOptions => - { - metricReaderOptions.TemporalityPreference = MetricReaderTemporalityPreference.Delta; - })); + .AddView( + histogram.Name, + new MetricStreamConfiguration() + { + TagKeys = enableTagFiltering ? new string[] { "key1" } : null, + ExemplarReservoirFactory = () => + { + if (testExemplarReservoir != null) + { + throw new InvalidOperationException(); + } + + return testExemplarReservoir = new TestExemplarReservoir(); + }, + }) + .AddInMemoryExporter(exportedItems)); - var measurementValues = GenerateRandomValues(10, false, null); - foreach (var value in measurementValues) - { - histogram.Record( - value.Value, - new("key1", "value1"), - new("key2", "value1"), - new("key3", "value1")); - } + histogram.Record( + 0, + new("key1", "value1"), + new("key2", "value2"), + new("key3", "value3")); + + meterProvider.ForceFlush(); + + Assert.NotNull(testExemplarReservoir); + Assert.NotNull(testExemplarReservoir.MeasurementTags); + Assert.Equal(3, testExemplarReservoir.MeasurementTags.Length); + Assert.Contains(testExemplarReservoir.MeasurementTags, t => t.Key == "key1" && (string?)t.Value == "value1"); + Assert.Contains(testExemplarReservoir.MeasurementTags, t => t.Key == "key2" && (string?)t.Value == "value2"); + Assert.Contains(testExemplarReservoir.MeasurementTags, t => t.Key == "key3" && (string?)t.Value == "value3"); - meterProvider.ForceFlush(MaxTimeToAllowForFlush); var metricPoint = GetFirstMetricPoint(exportedItems); + Assert.NotNull(metricPoint); - Assert.True(metricPoint.Value.StartTime >= testStartTime); - Assert.True(metricPoint.Value.EndTime != default); + var exemplars = GetExemplars(metricPoint.Value); + Assert.NotNull(exemplars); + foreach (var exemplar in exemplars) { - Assert.NotEqual(0, exemplar.FilteredTags.MaximumCount); + if (!enableTagFiltering) + { + Assert.Equal(0, exemplar.FilteredTags.MaximumCount); + } + else + { + Assert.Equal(3, exemplar.FilteredTags.MaximumCount); - var filteredTags = exemplar.FilteredTags.ToReadOnlyList(); + var filteredTags = exemplar.FilteredTags.ToReadOnlyList(); - Assert.Contains(new("key2", "value1"), filteredTags); - Assert.Contains(new("key3", "value1"), filteredTags); + Assert.Equal(2, filteredTags.Count); + + Assert.Contains(new("key2", "value2"), filteredTags); + Assert.Contains(new("key3", "value3"), filteredTags); + } } } @@ -791,4 +821,26 @@ private static void ValidateExemplars( Assert.Equal(measurementValues.Count(), count); } + + private sealed class TestExemplarReservoir : FixedSizeExemplarReservoir + { + public TestExemplarReservoir() + : base(1) + { + } + + public KeyValuePair[]? MeasurementTags { get; private set; } + + public override void Offer(in ExemplarMeasurement measurement) + { + this.MeasurementTags = measurement.Tags.ToArray(); + + this.UpdateExemplar(0, in measurement); + } + + public override void Offer(in ExemplarMeasurement measurement) + { + throw new NotSupportedException(); + } + } }