From 8879238af476b147ac83cde93b0421fbf650d9d5 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Thu, 10 Oct 2024 14:21:01 -0700 Subject: [PATCH 01/11] Add support for exporting instrumentation scope attributes from ActivitySource.Tags in OTLP exporter. --- .../CHANGELOG.md | 4 + .../Implementation/ActivityExtensions.cs | 68 +++++++--- .../OtlpTraceExporterTests.cs | 128 +++++++++++++++--- 3 files changed, 159 insertions(+), 41 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md index 1c62bad01a9..7a9fd6336ab 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md @@ -7,6 +7,10 @@ Notes](../../RELEASENOTES.md). ## Unreleased +*Added support for exporting instrumentation scope attributes from +`ActivitySource.Tags`. +([#](https://github.com/open-telemetry/opentelemetry-dotnet/pull/)) + ## 1.10.0-beta.1 Released 2024-Sep-30 diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs index 15115f0479d..89b9c80a394 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs @@ -44,15 +44,15 @@ internal static void AddBatch( } var activitySourceName = activity.Source.Name; - if (!spansByLibrary.TryGetValue(activitySourceName, out var spans)) + if (!spansByLibrary.TryGetValue(activitySourceName, out var scopeSpans)) { - spans = GetSpanListFromPool(activitySourceName, activity.Source.Version); + scopeSpans = GetSpanListFromPool(sdkLimitOptions, activity.Source); - spansByLibrary.Add(activitySourceName, spans); - resourceSpans.ScopeSpans.Add(spans); + spansByLibrary.Add(activitySourceName, scopeSpans); + resourceSpans.ScopeSpans.Add(scopeSpans); } - spans.Spans.Add(span); + scopeSpans.Spans.Add(span); } } @@ -65,34 +65,40 @@ internal static void Return(this ExportTraceServiceRequest request) return; } - foreach (var scope in resourceSpans.ScopeSpans) + foreach (var scopeSpan in resourceSpans.ScopeSpans) { - scope.Spans.Clear(); - SpanListPool.Add(scope); + scopeSpan.Spans.Clear(); + SpanListPool.Add(scopeSpan); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ScopeSpans GetSpanListFromPool(string name, string? version) + internal static ScopeSpans GetSpanListFromPool(SdkLimitOptions sdkLimitOptions, ActivitySource activitySource) { - if (!SpanListPool.TryTake(out var spans)) + if (!SpanListPool.TryTake(out var scopeSpans)) { - spans = new ScopeSpans + scopeSpans = new ScopeSpans { Scope = new InstrumentationScope { - Name = name, // Name is enforced to not be null, but it can be empty. - Version = version ?? string.Empty, // NRE throw by proto + Name = activitySource.Name, // Name is enforced to not be null, but it can be empty. + Version = activitySource.Version ?? string.Empty, // NRE throw by proto }, }; - } - else - { - spans.Scope.Name = name; - spans.Scope.Version = version ?? string.Empty; + + if (activitySource.Tags != null) + { + ActivitySourceTagsEnumerationState scopeAttributes = new() + { + SdkLimitOptions = sdkLimitOptions, + InstrumentationScope = scopeSpans.Scope, + }; + + scopeAttributes.EnumeratTags(activitySource, sdkLimitOptions.AttributeCountLimit ?? int.MaxValue); + } } - return spans; + return scopeSpans; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -405,4 +411,28 @@ public void EnumerateLinks(Activity activity, int maxLinks) } } } + + private struct ActivitySourceTagsEnumerationState + { + public SdkLimitOptions SdkLimitOptions; + + public InstrumentationScope InstrumentationScope; + + public void EnumeratTags(ActivitySource activitySource, int maxTags) + { + var scopeAttributes = this.InstrumentationScope.Attributes; + + foreach (var tag in activitySource.Tags ?? []) + { + if (this.InstrumentationScope.Attributes.Count() < maxTags) + { + OtlpTagWriter.Instance.TryWriteTag(ref scopeAttributes, tag, this.SdkLimitOptions.AttributeValueLengthLimit); + } + else + { + this.InstrumentationScope.DroppedAttributesCount++; + } + } + } + } } diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs index 9f5da4cd8d7..8339acf133e 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs @@ -221,6 +221,90 @@ void RunTest(SdkLimitOptions sdkOptions, Batch batch) } } + [Fact] + public void ScopeAttributesRemainConsistentAcrossMultipleBatches() + { + var activitySourceTags = new TagList + { + new("k0", "v0"), + }; + + using var activitySource = new ActivitySource(nameof(this.ScopeAttributesRemainConsistentAcrossMultipleBatches), "1.1.1.3", activitySourceTags); + using var rootActivity = activitySource.StartActivity("root", ActivityKind.Server, default(ActivityContext)); + using var childActivity = activitySource.StartActivity("child", ActivityKind.Client); + + Batch batch = new Batch(new[] { rootActivity!, childActivity! }, 2); + + var request = new OtlpCollector.ExportTraceServiceRequest(); + request.AddBatch(DefaultSdkLimitOptions, ResourceBuilder.CreateDefault().Build().ToOtlpResource(), batch); + + var resourceSpans = request.ResourceSpans.First(); + Assert.NotNull(request.ResourceSpans.First()); + + var scopeSpans = resourceSpans.ScopeSpans.First(); + Assert.NotNull(scopeSpans); + + var scope = scopeSpans.Scope; + Assert.Single(scope.Attributes); + Assert.Equal(nameof(this.ScopeAttributesRemainConsistentAcrossMultipleBatches), scope.Name); + Assert.Equal("1.1.1.3", scope.Version); + Assert.Contains(scope.Attributes, (kvp) => kvp.Key == "k0" && kvp.Value.StringValue == "v0"); + + // Return and re-add batch to simulate reuse + request.Return(); + request.AddBatch(DefaultSdkLimitOptions, ResourceBuilder.CreateDefault().Build().ToOtlpResource(), batch); + + resourceSpans = request.ResourceSpans.First(); + scopeSpans = resourceSpans.ScopeSpans.First(); + scope = scopeSpans.Scope; + + Assert.Single(scope.Attributes); + Assert.Equal(nameof(this.ScopeAttributesRemainConsistentAcrossMultipleBatches), scope.Name); + Assert.Equal("1.1.1.3", scope.Version); + Assert.Contains(scope.Attributes, (kvp) => kvp.Key == "k0" && kvp.Value.StringValue == "v0"); + } + + [Fact] + public void ScopeAttributesLimitsTest() + { + var sdkOptions = new SdkLimitOptions() + { + AttributeValueLengthLimit = 4, + AttributeCountLimit = 3, + }; + + // ActivitySource Tags are sorted in .NET. + var activitySourceTags = new TagList + { + new("1_TruncatedSourceTag", "12345"), + new("2_TruncatedSourceStringArray", new string?[] { "12345", "1234", string.Empty, null }), + new("3_TruncatedSourceObjectTag", new object()), + new("4_OneSourceTagTooMany", 1), + }; + + using var activitySource = new ActivitySource(name: nameof(this.ScopeAttributesLimitsTest), tags: activitySourceTags); + using var activity = activitySource.StartActivity("root", ActivityKind.Server, default(ActivityContext)); + Batch batch = new Batch(new[] { activity! }, 1); + + var request = new OtlpCollector.ExportTraceServiceRequest(); + request.AddBatch(sdkOptions, ResourceBuilder.CreateDefault().Build().ToOtlpResource(), batch); + + var resourceSpans = request.ResourceSpans.First(); + Assert.NotNull(request.ResourceSpans.First()); + + var scopeSpans = resourceSpans.ScopeSpans.First(); + Assert.NotNull(scopeSpans); + + var scope = scopeSpans.Scope; + Assert.NotNull(scope); + + Assert.Equal(3, scope.Attributes.Count); + Assert.Equal(1u, scope.DroppedAttributesCount); + Assert.Equal("1234", scope.Attributes[0].Value.StringValue); + this.ArrayValueAsserts(scope.Attributes[1].Value.ArrayValue.Values); + Assert.Equal(new object().ToString()!.Substring(0, 4), scope.Attributes[2].Value.StringValue); + } + [Fact] public void SpanLimitsTest() { @@ -263,7 +347,7 @@ public void SpanLimitsTest() Assert.Equal(3, otlpSpan.Attributes.Count); Assert.Equal(1u, otlpSpan.DroppedAttributesCount); Assert.Equal("1234", otlpSpan.Attributes[0].Value.StringValue); - ArrayValueAsserts(otlpSpan.Attributes[1].Value.ArrayValue.Values); + this.ArrayValueAsserts(otlpSpan.Attributes[1].Value.ArrayValue.Values); Assert.Equal(new object().ToString()!.Substring(0, 4), otlpSpan.Attributes[2].Value.StringValue); Assert.Single(otlpSpan.Events); @@ -271,7 +355,7 @@ public void SpanLimitsTest() Assert.Equal(3, otlpSpan.Events[0].Attributes.Count); Assert.Equal(1u, otlpSpan.Events[0].DroppedAttributesCount); Assert.Equal("1234", otlpSpan.Events[0].Attributes[0].Value.StringValue); - ArrayValueAsserts(otlpSpan.Events[0].Attributes[1].Value.ArrayValue.Values); + this.ArrayValueAsserts(otlpSpan.Events[0].Attributes[1].Value.ArrayValue.Values); Assert.Equal(new object().ToString()!.Substring(0, 4), otlpSpan.Events[0].Attributes[2].Value.StringValue); Assert.Single(otlpSpan.Links); @@ -279,27 +363,8 @@ public void SpanLimitsTest() Assert.Equal(3, otlpSpan.Links[0].Attributes.Count); Assert.Equal(1u, otlpSpan.Links[0].DroppedAttributesCount); Assert.Equal("1234", otlpSpan.Links[0].Attributes[0].Value.StringValue); - ArrayValueAsserts(otlpSpan.Links[0].Attributes[1].Value.ArrayValue.Values); + this.ArrayValueAsserts(otlpSpan.Links[0].Attributes[1].Value.ArrayValue.Values); Assert.Equal(new object().ToString()!.Substring(0, 4), otlpSpan.Links[0].Attributes[2].Value.StringValue); - - void ArrayValueAsserts(RepeatedField values) - { - var expectedStringArray = new string?[] { "1234", "1234", string.Empty, null }; - for (var i = 0; i < expectedStringArray.Length; ++i) - { - var expectedValue = expectedStringArray[i]; - var expectedValueCase = expectedValue != null - ? OtlpCommon.AnyValue.ValueOneofCase.StringValue - : OtlpCommon.AnyValue.ValueOneofCase.None; - - var actual = values[i]; - Assert.Equal(expectedValueCase, actual.ValueCase); - if (expectedValueCase != OtlpCommon.AnyValue.ValueOneofCase.None) - { - Assert.Equal(expectedValue, actual.StringValue); - } - } - } } [Fact] @@ -859,4 +924,23 @@ public void SpanLinkFlagsTest(bool isRecorded, bool isRemote) Assert.False(flags.HasFlag(OtlpTrace.SpanFlags.ContextIsRemoteMask)); } } + + private void ArrayValueAsserts(RepeatedField values) + { + var expectedStringArray = new string?[] { "1234", "1234", string.Empty, null }; + for (var i = 0; i < expectedStringArray.Length; ++i) + { + var expectedValue = expectedStringArray[i]; + var expectedValueCase = expectedValue != null + ? OtlpCommon.AnyValue.ValueOneofCase.StringValue + : OtlpCommon.AnyValue.ValueOneofCase.None; + + var actual = values[i]; + Assert.Equal(expectedValueCase, actual.ValueCase); + if (expectedValueCase != OtlpCommon.AnyValue.ValueOneofCase.None) + { + Assert.Equal(expectedValue, actual.StringValue); + } + } + } } From 2774800433623b4fbe928459e611d25e30a3d4a4 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Thu, 10 Oct 2024 14:26:22 -0700 Subject: [PATCH 02/11] change log patch --- src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md index 7a9fd6336ab..be735832f70 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md @@ -9,7 +9,7 @@ Notes](../../RELEASENOTES.md). *Added support for exporting instrumentation scope attributes from `ActivitySource.Tags`. -([#](https://github.com/open-telemetry/opentelemetry-dotnet/pull/)) +([#5897](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5897)) ## 1.10.0-beta.1 From bb2ef8ec20df03e0132bfccc0447ba2cf711b348 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Thu, 10 Oct 2024 16:17:38 -0700 Subject: [PATCH 03/11] refactor - remove struct. --- .../Implementation/ActivityExtensions.cs | 52 +++++++------------ 1 file changed, 18 insertions(+), 34 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs index 89b9c80a394..3954725ebe8 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs @@ -32,6 +32,8 @@ internal static void AddBatch( }; request.ResourceSpans.Add(resourceSpans); + var maxTags = sdkLimitOptions.AttributeCountLimit ?? int.MaxValue; + foreach (var activity in activityBatch) { Span? span = activity.ToOtlpSpan(sdkLimitOptions); @@ -46,7 +48,7 @@ internal static void AddBatch( var activitySourceName = activity.Source.Name; if (!spansByLibrary.TryGetValue(activitySourceName, out var scopeSpans)) { - scopeSpans = GetSpanListFromPool(sdkLimitOptions, activity.Source); + scopeSpans = GetSpanListFromPool(activity.Source, maxTags, sdkLimitOptions.AttributeValueLengthLimit); spansByLibrary.Add(activitySourceName, scopeSpans); resourceSpans.ScopeSpans.Add(scopeSpans); @@ -73,7 +75,7 @@ internal static void Return(this ExportTraceServiceRequest request) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ScopeSpans GetSpanListFromPool(SdkLimitOptions sdkLimitOptions, ActivitySource activitySource) + internal static ScopeSpans GetSpanListFromPool(ActivitySource activitySource, int maxTags, int? attributeValueLengthLimit) { if (!SpanListPool.TryTake(out var scopeSpans)) { @@ -87,14 +89,20 @@ internal static ScopeSpans GetSpanListFromPool(SdkLimitOptions sdkLimitOptions, }; if (activitySource.Tags != null) - { - ActivitySourceTagsEnumerationState scopeAttributes = new() - { - SdkLimitOptions = sdkLimitOptions, - InstrumentationScope = scopeSpans.Scope, - }; - - scopeAttributes.EnumeratTags(activitySource, sdkLimitOptions.AttributeCountLimit ?? int.MaxValue); + { + var scopeAttributes = scopeSpans.Scope.Attributes; + + foreach (var tag in activitySource.Tags ?? []) + { + if (scopeAttributes.Count < maxTags) + { + OtlpTagWriter.Instance.TryWriteTag(ref scopeAttributes, tag, attributeValueLengthLimit); + } + else + { + scopeSpans.Scope.DroppedAttributesCount++; + } + } } } @@ -411,28 +419,4 @@ public void EnumerateLinks(Activity activity, int maxLinks) } } } - - private struct ActivitySourceTagsEnumerationState - { - public SdkLimitOptions SdkLimitOptions; - - public InstrumentationScope InstrumentationScope; - - public void EnumeratTags(ActivitySource activitySource, int maxTags) - { - var scopeAttributes = this.InstrumentationScope.Attributes; - - foreach (var tag in activitySource.Tags ?? []) - { - if (this.InstrumentationScope.Attributes.Count() < maxTags) - { - OtlpTagWriter.Instance.TryWriteTag(ref scopeAttributes, tag, this.SdkLimitOptions.AttributeValueLengthLimit); - } - else - { - this.InstrumentationScope.DroppedAttributesCount++; - } - } - } - } } From 52f38b751f495044e9f1946529b2b071a345344d Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Thu, 10 Oct 2024 16:28:36 -0700 Subject: [PATCH 04/11] PR feedback - extend foreach for IReadOnlyList --- .../Implementation/ActivityExtensions.cs | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs index 3954725ebe8..b16113ecdce 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs @@ -92,15 +92,32 @@ internal static ScopeSpans GetSpanListFromPool(ActivitySource activitySource, in { var scopeAttributes = scopeSpans.Scope.Attributes; - foreach (var tag in activitySource.Tags ?? []) + if (activitySource.Tags is IReadOnlyList> activitySourceTagsList) { - if (scopeAttributes.Count < maxTags) + for (int i = 0; i < activitySourceTagsList.Count; i++) { - OtlpTagWriter.Instance.TryWriteTag(ref scopeAttributes, tag, attributeValueLengthLimit); + if (scopeAttributes.Count < maxTags) + { + OtlpTagWriter.Instance.TryWriteTag(ref scopeAttributes, activitySourceTagsList[i], attributeValueLengthLimit); + } + else + { + scopeSpans.Scope.DroppedAttributesCount++; + } } - else + } + else + { + foreach (var tag in activitySource.Tags ?? []) { - scopeSpans.Scope.DroppedAttributesCount++; + if (scopeAttributes.Count < maxTags) + { + OtlpTagWriter.Instance.TryWriteTag(ref scopeAttributes, tag, attributeValueLengthLimit); + } + else + { + scopeSpans.Scope.DroppedAttributesCount++; + } } } } From 67dd085908e16165a04d0d9804d684421c382183 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Thu, 10 Oct 2024 16:46:13 -0700 Subject: [PATCH 05/11] lint fixes + test debug --- .../Implementation/ActivityExtensions.cs | 60 +++++++++---------- .../OtlpTraceExporterTests.cs | 2 +- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs index b16113ecdce..c52d601778e 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs @@ -89,36 +89,36 @@ internal static ScopeSpans GetSpanListFromPool(ActivitySource activitySource, in }; if (activitySource.Tags != null) - { - var scopeAttributes = scopeSpans.Scope.Attributes; - - if (activitySource.Tags is IReadOnlyList> activitySourceTagsList) - { - for (int i = 0; i < activitySourceTagsList.Count; i++) - { - if (scopeAttributes.Count < maxTags) - { - OtlpTagWriter.Instance.TryWriteTag(ref scopeAttributes, activitySourceTagsList[i], attributeValueLengthLimit); - } - else - { - scopeSpans.Scope.DroppedAttributesCount++; - } - } - } - else - { - foreach (var tag in activitySource.Tags ?? []) - { - if (scopeAttributes.Count < maxTags) - { - OtlpTagWriter.Instance.TryWriteTag(ref scopeAttributes, tag, attributeValueLengthLimit); - } - else - { - scopeSpans.Scope.DroppedAttributesCount++; - } - } + { + var scopeAttributes = scopeSpans.Scope.Attributes; + + if (activitySource.Tags is IReadOnlyList> activitySourceTagsList) + { + for (int i = 0; i < activitySourceTagsList.Count; i++) + { + if (scopeAttributes.Count < maxTags) + { + OtlpTagWriter.Instance.TryWriteTag(ref scopeAttributes, activitySourceTagsList[i], attributeValueLengthLimit); + } + else + { + scopeSpans.Scope.DroppedAttributesCount++; + } + } + } + else + { + foreach (var tag in activitySource.Tags ?? []) + { + if (scopeAttributes.Count < maxTags) + { + OtlpTagWriter.Instance.TryWriteTag(ref scopeAttributes, tag, attributeValueLengthLimit); + } + else + { + scopeSpans.Scope.DroppedAttributesCount++; + } + } } } } diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs index 8339acf133e..a84a33808f4 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs @@ -264,7 +264,7 @@ public void ScopeAttributesRemainConsistentAcrossMultipleBatches() Assert.Contains(scope.Attributes, (kvp) => kvp.Key == "k0" && kvp.Value.StringValue == "v0"); } - [Fact] + [Fact(Skip = "Troubleshoot failing test.")] public void ScopeAttributesLimitsTest() { var sdkOptions = new SdkLimitOptions() From 2ed38b08bb2d51beb1e5e53a04c378c9474525f9 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Thu, 10 Oct 2024 16:59:31 -0700 Subject: [PATCH 06/11] test troubleshoot --- .../OtlpTraceExporterTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs index a84a33808f4..c24885fd396 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs @@ -221,7 +221,7 @@ void RunTest(SdkLimitOptions sdkOptions, Batch batch) } } - [Fact] + [Fact(Skip = "Troubleshoot failing test.")] public void ScopeAttributesRemainConsistentAcrossMultipleBatches() { var activitySourceTags = new TagList @@ -264,7 +264,7 @@ public void ScopeAttributesRemainConsistentAcrossMultipleBatches() Assert.Contains(scope.Attributes, (kvp) => kvp.Key == "k0" && kvp.Value.StringValue == "v0"); } - [Fact(Skip = "Troubleshoot failing test.")] + [Fact] public void ScopeAttributesLimitsTest() { var sdkOptions = new SdkLimitOptions() From d956f203c919fdee6bc21b0ff60817f4b1edfcf3 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Tue, 15 Oct 2024 11:16:10 -0700 Subject: [PATCH 07/11] Debug / fix tests. --- .../Implementation/ActivityExtensions.cs | 2 +- .../OtlpTraceExporterTests.cs | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs index c52d601778e..149e0bca6da 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs @@ -108,7 +108,7 @@ internal static ScopeSpans GetSpanListFromPool(ActivitySource activitySource, in } else { - foreach (var tag in activitySource.Tags ?? []) + foreach (var tag in activitySource.Tags) { if (scopeAttributes.Count < maxTags) { diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs index c24885fd396..ae561437d73 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs @@ -221,7 +221,7 @@ void RunTest(SdkLimitOptions sdkOptions, Batch batch) } } - [Fact(Skip = "Troubleshoot failing test.")] + [Fact] public void ScopeAttributesRemainConsistentAcrossMultipleBatches() { var activitySourceTags = new TagList @@ -233,6 +233,9 @@ public void ScopeAttributesRemainConsistentAcrossMultipleBatches() using var rootActivity = activitySource.StartActivity("root", ActivityKind.Server, default(ActivityContext)); using var childActivity = activitySource.StartActivity("child", ActivityKind.Client); + Assert.Equal(1, rootActivity?.Source.Tags?.Count()); + Assert.Equal(1, childActivity?.Source.Tags?.Count()); + Batch batch = new Batch(new[] { rootActivity!, childActivity! }, 2); var request = new OtlpCollector.ExportTraceServiceRequest(); @@ -262,6 +265,7 @@ public void ScopeAttributesRemainConsistentAcrossMultipleBatches() Assert.Equal(nameof(this.ScopeAttributesRemainConsistentAcrossMultipleBatches), scope.Name); Assert.Equal("1.1.1.3", scope.Version); Assert.Contains(scope.Attributes, (kvp) => kvp.Key == "k0" && kvp.Value.StringValue == "v0"); + request = null; } [Fact] @@ -284,8 +288,9 @@ public void ScopeAttributesLimitsTest() using var activitySource = new ActivitySource(name: nameof(this.ScopeAttributesLimitsTest), tags: activitySourceTags); using var activity = activitySource.StartActivity("root", ActivityKind.Server, default(ActivityContext)); - Batch batch = new Batch(new[] { activity! }, 1); + Assert.Equal(4, activity?.Source.Tags?.Count()); + Batch batch = new Batch(new[] { activity! }, 1); var request = new OtlpCollector.ExportTraceServiceRequest(); request.AddBatch(sdkOptions, ResourceBuilder.CreateDefault().Build().ToOtlpResource(), batch); @@ -303,6 +308,7 @@ public void ScopeAttributesLimitsTest() Assert.Equal("1234", scope.Attributes[0].Value.StringValue); this.ArrayValueAsserts(scope.Attributes[1].Value.ArrayValue.Values); Assert.Equal(new object().ToString()!.Substring(0, 4), scope.Attributes[2].Value.StringValue); + request = null; } [Fact] From 6dd8289d3eb423bbd9ca9214e37ff80c8f15952a Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Tue, 15 Oct 2024 12:24:20 -0700 Subject: [PATCH 08/11] change test to new format --- .../OtlpTraceExporterTests.cs | 121 +++++++++++------- 1 file changed, 77 insertions(+), 44 deletions(-) diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs index ae561437d73..a36fb7bffef 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs @@ -230,42 +230,58 @@ public void ScopeAttributesRemainConsistentAcrossMultipleBatches() }; using var activitySource = new ActivitySource(nameof(this.ScopeAttributesRemainConsistentAcrossMultipleBatches), "1.1.1.3", activitySourceTags); - using var rootActivity = activitySource.StartActivity("root", ActivityKind.Server, default(ActivityContext)); - using var childActivity = activitySource.StartActivity("child", ActivityKind.Client); + var resourceBuilder = ResourceBuilder.CreateDefault(); - Assert.Equal(1, rootActivity?.Source.Tags?.Count()); - Assert.Equal(1, childActivity?.Source.Tags?.Count()); + var exportedItems = new List(); + var builder = Sdk.CreateTracerProviderBuilder() + .SetResourceBuilder(resourceBuilder) + .AddSource(activitySource.Name) + .AddProcessor(new SimpleActivityExportProcessor(new InMemoryExporter(exportedItems))); - Batch batch = new Batch(new[] { rootActivity!, childActivity! }, 2); + using var openTelemetrySdk = builder.Build(); - var request = new OtlpCollector.ExportTraceServiceRequest(); - request.AddBatch(DefaultSdkLimitOptions, ResourceBuilder.CreateDefault().Build().ToOtlpResource(), batch); + var parentActivity = activitySource.StartActivity("parent", ActivityKind.Server, default(ActivityContext)); + var nestedChildActivity = activitySource.StartActivity("nested-child", ActivityKind.Client); + parentActivity?.Dispose(); + nestedChildActivity?.Dispose(); - var resourceSpans = request.ResourceSpans.First(); - Assert.NotNull(request.ResourceSpans.First()); + Assert.Equal(2, exportedItems.Count); + var batch = new Batch(exportedItems.ToArray(), exportedItems.Count); + RunTest(DefaultSdkLimitOptions, batch); - var scopeSpans = resourceSpans.ScopeSpans.First(); - Assert.NotNull(scopeSpans); + void RunTest(SdkLimitOptions sdkOptions, Batch batch) + { + var request = new OtlpCollector.ExportTraceServiceRequest(); + + request.AddBatch(sdkOptions, resourceBuilder.Build().ToOtlpResource(), batch); + + var resourceSpans = request.ResourceSpans.First(); + Assert.NotNull(request.ResourceSpans.First()); - var scope = scopeSpans.Scope; - Assert.Single(scope.Attributes); - Assert.Equal(nameof(this.ScopeAttributesRemainConsistentAcrossMultipleBatches), scope.Name); - Assert.Equal("1.1.1.3", scope.Version); - Assert.Contains(scope.Attributes, (kvp) => kvp.Key == "k0" && kvp.Value.StringValue == "v0"); + var scopeSpans = resourceSpans.ScopeSpans.First(); + Assert.NotNull(scopeSpans); - // Return and re-add batch to simulate reuse - request.Return(); - request.AddBatch(DefaultSdkLimitOptions, ResourceBuilder.CreateDefault().Build().ToOtlpResource(), batch); + var scope = scopeSpans.Scope; + Assert.NotNull(scope); - resourceSpans = request.ResourceSpans.First(); - scopeSpans = resourceSpans.ScopeSpans.First(); - scope = scopeSpans.Scope; + Assert.Single(scope.Attributes); + Assert.Equal(nameof(this.ScopeAttributesRemainConsistentAcrossMultipleBatches), scope.Name); + Assert.Equal("1.1.1.3", scope.Version); + Assert.Contains(scope.Attributes, (kvp) => kvp.Key == "k0" && kvp.Value.StringValue == "v0"); - Assert.Single(scope.Attributes); - Assert.Equal(nameof(this.ScopeAttributesRemainConsistentAcrossMultipleBatches), scope.Name); - Assert.Equal("1.1.1.3", scope.Version); - Assert.Contains(scope.Attributes, (kvp) => kvp.Key == "k0" && kvp.Value.StringValue == "v0"); - request = null; + // Return and re-add batch to simulate reuse + request.Return(); + request.AddBatch(DefaultSdkLimitOptions, ResourceBuilder.CreateDefault().Build().ToOtlpResource(), batch); + + resourceSpans = request.ResourceSpans.First(); + scopeSpans = resourceSpans.ScopeSpans.First(); + scope = scopeSpans.Scope; + + Assert.Single(scope.Attributes); + Assert.Equal(nameof(this.ScopeAttributesRemainConsistentAcrossMultipleBatches), scope.Name); + Assert.Equal("1.1.1.3", scope.Version); + Assert.Contains(scope.Attributes, (kvp) => kvp.Key == "k0" && kvp.Value.StringValue == "v0"); + } } [Fact] @@ -286,29 +302,46 @@ public void ScopeAttributesLimitsTest() new("4_OneSourceTagTooMany", 1), }; + var resourceBuilder = ResourceBuilder.CreateDefault(); + using var activitySource = new ActivitySource(name: nameof(this.ScopeAttributesLimitsTest), tags: activitySourceTags); - using var activity = activitySource.StartActivity("root", ActivityKind.Server, default(ActivityContext)); - Assert.Equal(4, activity?.Source.Tags?.Count()); - Batch batch = new Batch(new[] { activity! }, 1); - var request = new OtlpCollector.ExportTraceServiceRequest(); - request.AddBatch(sdkOptions, ResourceBuilder.CreateDefault().Build().ToOtlpResource(), batch); + var exportedItems = new List(); + var builder = Sdk.CreateTracerProviderBuilder() + .SetResourceBuilder(resourceBuilder) + .AddSource(activitySource.Name) + .AddProcessor(new SimpleActivityExportProcessor(new InMemoryExporter(exportedItems))); + + using var openTelemetrySdk = builder.Build(); + + var activity = activitySource.StartActivity("parent", ActivityKind.Server, default(ActivityContext)); + activity?.Dispose(); + + Assert.Single(exportedItems); + var batch = new Batch(exportedItems.ToArray(), exportedItems.Count); + RunTest(sdkOptions, batch); + + void RunTest(SdkLimitOptions sdkOptions, Batch batch) + { + var request = new OtlpCollector.ExportTraceServiceRequest(); + + request.AddBatch(sdkOptions, resourceBuilder.Build().ToOtlpResource(), batch); - var resourceSpans = request.ResourceSpans.First(); - Assert.NotNull(request.ResourceSpans.First()); + var resourceSpans = request.ResourceSpans.First(); + Assert.NotNull(request.ResourceSpans.First()); - var scopeSpans = resourceSpans.ScopeSpans.First(); - Assert.NotNull(scopeSpans); + var scopeSpans = resourceSpans.ScopeSpans.First(); + Assert.NotNull(scopeSpans); - var scope = scopeSpans.Scope; - Assert.NotNull(scope); + var scope = scopeSpans.Scope; + Assert.NotNull(scope); - Assert.Equal(3, scope.Attributes.Count); - Assert.Equal(1u, scope.DroppedAttributesCount); - Assert.Equal("1234", scope.Attributes[0].Value.StringValue); - this.ArrayValueAsserts(scope.Attributes[1].Value.ArrayValue.Values); - Assert.Equal(new object().ToString()!.Substring(0, 4), scope.Attributes[2].Value.StringValue); - request = null; + Assert.Equal(3, scope.Attributes.Count); + Assert.Equal(1u, scope.DroppedAttributesCount); + Assert.Equal("1234", scope.Attributes[0].Value.StringValue); + this.ArrayValueAsserts(scope.Attributes[1].Value.ArrayValue.Values); + Assert.Equal(new object().ToString()!.Substring(0, 4), scope.Attributes[2].Value.StringValue); + } } [Fact] From fe7fa8c8bc9c2ad8408074a0000e2d5c541b4ec1 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 15 Oct 2024 13:09:34 -0700 Subject: [PATCH 09/11] Update src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md --- .../CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md index be735832f70..1d890a3da60 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md @@ -7,9 +7,9 @@ Notes](../../RELEASENOTES.md). ## Unreleased -*Added support for exporting instrumentation scope attributes from -`ActivitySource.Tags`. -([#5897](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5897)) +* Added support for exporting instrumentation scope attributes from + `ActivitySource.Tags`. + ([#5897](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5897)) ## 1.10.0-beta.1 From 02a4e066288bbb03e77e7d058fb65079a796ce93 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Tue, 15 Oct 2024 13:13:04 -0700 Subject: [PATCH 10/11] Dispose otlp exporter in MockCollectorIntegrationTests --- .../MockCollectorIntegrationTests.cs | 10 +++++----- .../OtlpTraceExporterTests.cs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/MockCollectorIntegrationTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/MockCollectorIntegrationTests.cs index e72a6773b81..27b64e08f63 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/MockCollectorIntegrationTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/MockCollectorIntegrationTests.cs @@ -75,7 +75,7 @@ public async Task TestRecoveryAfterFailedExport() await httpClient.GetAsync($"/MockCollector/SetResponseCodes/{string.Join(",", codes.Select(x => (int)x))}"); var exportResults = new List(); - var otlpExporter = new OtlpTraceExporter(new OtlpExporterOptions() { Endpoint = new Uri($"http://localhost:{testGrpcPort}") }); + using var otlpExporter = new OtlpTraceExporter(new OtlpExporterOptions() { Endpoint = new Uri($"http://localhost:{testGrpcPort}") }); var delegatingExporter = new DelegatingExporter { OnExportFunc = (batch) => @@ -183,7 +183,7 @@ public async Task GrpcRetryTests(bool useRetryTransmissionHandler, ExportResult .AddInMemoryCollection(new Dictionary { [ExperimentalOptions.OtlpRetryEnvVar] = useRetryTransmissionHandler ? "in_memory" : null }) .Build(); - var otlpExporter = new OtlpTraceExporter(exporterOptions, new SdkLimitOptions(), new ExperimentalOptions(configuration)); + using var otlpExporter = new OtlpTraceExporter(exporterOptions, new SdkLimitOptions(), new ExperimentalOptions(configuration)); var activitySourceName = "otel.grpc.retry.test"; using var source = new ActivitySource(activitySourceName); @@ -268,7 +268,7 @@ public async Task HttpRetryTests(bool useRetryTransmissionHandler, ExportResult .AddInMemoryCollection(new Dictionary { [ExperimentalOptions.OtlpRetryEnvVar] = useRetryTransmissionHandler ? "in_memory" : null }) .Build(); - var otlpExporter = new OtlpTraceExporter(exporterOptions, new SdkLimitOptions(), new ExperimentalOptions(configuration)); + using var otlpExporter = new OtlpTraceExporter(exporterOptions, new SdkLimitOptions(), new ExperimentalOptions(configuration)); var activitySourceName = "otel.http.retry.test"; using var source = new ActivitySource(activitySourceName); @@ -371,7 +371,7 @@ public async Task HttpPersistentStorageRetryTests(bool usePersistentStorageTrans transmissionHandler = new OtlpExporterTransmissionHandler(exportClient, exporterOptions.TimeoutMilliseconds); } - var otlpExporter = new OtlpTraceExporter(exporterOptions, new(), new(), transmissionHandler); + using var otlpExporter = new OtlpTraceExporter(exporterOptions, new(), new(), transmissionHandler); var activitySourceName = "otel.http.persistent.storage.retry.test"; using var source = new ActivitySource(activitySourceName); @@ -510,7 +510,7 @@ public async Task GrpcPersistentStorageRetryTests(bool usePersistentStorageTrans transmissionHandler = new OtlpExporterTransmissionHandler(exportClient, exporterOptions.TimeoutMilliseconds); } - var otlpExporter = new OtlpTraceExporter(exporterOptions, new(), new(), transmissionHandler); + using var otlpExporter = new OtlpTraceExporter(exporterOptions, new(), new(), transmissionHandler); var activitySourceName = "otel.grpc.persistent.storage.retry.test"; using var source = new ActivitySource(activitySourceName); diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs index a36fb7bffef..953b582926f 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs @@ -754,7 +754,7 @@ public void Shutdown_ClientShutdownIsCalled() var exporterOptions = new OtlpExporterOptions(); var transmissionHandler = new OtlpExporterTransmissionHandler(exportClientMock, exporterOptions.TimeoutMilliseconds); - var exporter = new OtlpTraceExporter(new OtlpExporterOptions(), DefaultSdkLimitOptions, DefaultExperimentalOptions, transmissionHandler); + using var exporter = new OtlpTraceExporter(new OtlpExporterOptions(), DefaultSdkLimitOptions, DefaultExperimentalOptions, transmissionHandler); exporter.Shutdown(); Assert.True(exportClientMock.ShutdownCalled); From 1ca63a6892734664d5508e5189c76bb21b5c328f Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 15 Oct 2024 15:29:23 -0700 Subject: [PATCH 11/11] Bug fixes. --- .../Implementation/ActivityExtensions.cs | 52 ++++++++++-------- .../OtlpTraceExporterTests.cs | 54 ++++++++++++++----- 2 files changed, 69 insertions(+), 37 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs index 149e0bca6da..313a8e9f59b 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs @@ -70,6 +70,7 @@ internal static void Return(this ExportTraceServiceRequest request) foreach (var scopeSpan in resourceSpans.ScopeSpans) { scopeSpan.Spans.Clear(); + scopeSpan.Scope.Attributes.Clear(); SpanListPool.Add(scopeSpan); } } @@ -87,37 +88,42 @@ internal static ScopeSpans GetSpanListFromPool(ActivitySource activitySource, in Version = activitySource.Version ?? string.Empty, // NRE throw by proto }, }; + } + else + { + scopeSpans.Scope.Name = activitySource.Name; // Name is enforced to not be null, but it can be empty. + scopeSpans.Scope.Version = activitySource.Version ?? string.Empty; // NRE throw by proto + } - if (activitySource.Tags != null) - { - var scopeAttributes = scopeSpans.Scope.Attributes; + if (activitySource.Tags != null) + { + var scopeAttributes = scopeSpans.Scope.Attributes; - if (activitySource.Tags is IReadOnlyList> activitySourceTagsList) + if (activitySource.Tags is IReadOnlyList> activitySourceTagsList) + { + for (int i = 0; i < activitySourceTagsList.Count; i++) { - for (int i = 0; i < activitySourceTagsList.Count; i++) + if (scopeAttributes.Count < maxTags) { - if (scopeAttributes.Count < maxTags) - { - OtlpTagWriter.Instance.TryWriteTag(ref scopeAttributes, activitySourceTagsList[i], attributeValueLengthLimit); - } - else - { - scopeSpans.Scope.DroppedAttributesCount++; - } + OtlpTagWriter.Instance.TryWriteTag(ref scopeAttributes, activitySourceTagsList[i], attributeValueLengthLimit); + } + else + { + scopeSpans.Scope.DroppedAttributesCount++; } } - else + } + else + { + foreach (var tag in activitySource.Tags) { - foreach (var tag in activitySource.Tags) + if (scopeAttributes.Count < maxTags) + { + OtlpTagWriter.Instance.TryWriteTag(ref scopeAttributes, tag, attributeValueLengthLimit); + } + else { - if (scopeAttributes.Count < maxTags) - { - OtlpTagWriter.Instance.TryWriteTag(ref scopeAttributes, tag, attributeValueLengthLimit); - } - else - { - scopeSpans.Scope.DroppedAttributesCount++; - } + scopeSpans.Scope.DroppedAttributesCount++; } } } diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs index 953b582926f..bb76069214a 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs @@ -229,27 +229,39 @@ public void ScopeAttributesRemainConsistentAcrossMultipleBatches() new("k0", "v0"), }; - using var activitySource = new ActivitySource(nameof(this.ScopeAttributesRemainConsistentAcrossMultipleBatches), "1.1.1.3", activitySourceTags); + using var activitySourceWithTags = new ActivitySource($"{nameof(this.ScopeAttributesRemainConsistentAcrossMultipleBatches)}_WithTags", "1.1.1.3", activitySourceTags); + using var activitySourceWithoutTags = new ActivitySource($"{nameof(this.ScopeAttributesRemainConsistentAcrossMultipleBatches)}_WithoutTags", "1.1.1.4"); + var resourceBuilder = ResourceBuilder.CreateDefault(); var exportedItems = new List(); var builder = Sdk.CreateTracerProviderBuilder() .SetResourceBuilder(resourceBuilder) - .AddSource(activitySource.Name) + .AddSource(activitySourceWithTags.Name) + .AddSource(activitySourceWithoutTags.Name) .AddProcessor(new SimpleActivityExportProcessor(new InMemoryExporter(exportedItems))); using var openTelemetrySdk = builder.Build(); - var parentActivity = activitySource.StartActivity("parent", ActivityKind.Server, default(ActivityContext)); - var nestedChildActivity = activitySource.StartActivity("nested-child", ActivityKind.Client); + var parentActivity = activitySourceWithTags.StartActivity("parent", ActivityKind.Server, default(ActivityContext)); + var nestedChildActivity = activitySourceWithTags.StartActivity("nested-child", ActivityKind.Client); parentActivity?.Dispose(); nestedChildActivity?.Dispose(); Assert.Equal(2, exportedItems.Count); var batch = new Batch(exportedItems.ToArray(), exportedItems.Count); - RunTest(DefaultSdkLimitOptions, batch); + RunTest(DefaultSdkLimitOptions, batch, activitySourceWithTags); - void RunTest(SdkLimitOptions sdkOptions, Batch batch) + exportedItems.Clear(); + + var parentActivityNoTags = activitySourceWithoutTags.StartActivity("parent", ActivityKind.Server, default(ActivityContext)); + parentActivityNoTags?.Dispose(); + + Assert.Single(exportedItems); + batch = new Batch(exportedItems.ToArray(), exportedItems.Count); + RunTest(DefaultSdkLimitOptions, batch, activitySourceWithoutTags); + + void RunTest(SdkLimitOptions sdkOptions, Batch batch, ActivitySource activitySource) { var request = new OtlpCollector.ExportTraceServiceRequest(); @@ -264,10 +276,14 @@ void RunTest(SdkLimitOptions sdkOptions, Batch batch) var scope = scopeSpans.Scope; Assert.NotNull(scope); - Assert.Single(scope.Attributes); - Assert.Equal(nameof(this.ScopeAttributesRemainConsistentAcrossMultipleBatches), scope.Name); - Assert.Equal("1.1.1.3", scope.Version); - Assert.Contains(scope.Attributes, (kvp) => kvp.Key == "k0" && kvp.Value.StringValue == "v0"); + Assert.Equal(activitySource.Name, scope.Name); + Assert.Equal(activitySource.Version, scope.Version); + Assert.Equal(activitySource.Tags?.Count() ?? 0, scope.Attributes.Count); + + foreach (var tag in activitySource.Tags ?? []) + { + Assert.Contains(scope.Attributes, (kvp) => kvp.Key == tag.Key && kvp.Value.StringValue == (string?)tag.Value); + } // Return and re-add batch to simulate reuse request.Return(); @@ -277,10 +293,17 @@ void RunTest(SdkLimitOptions sdkOptions, Batch batch) scopeSpans = resourceSpans.ScopeSpans.First(); scope = scopeSpans.Scope; - Assert.Single(scope.Attributes); - Assert.Equal(nameof(this.ScopeAttributesRemainConsistentAcrossMultipleBatches), scope.Name); - Assert.Equal("1.1.1.3", scope.Version); - Assert.Contains(scope.Attributes, (kvp) => kvp.Key == "k0" && kvp.Value.StringValue == "v0"); + Assert.Equal(activitySource.Name, scope.Name); + Assert.Equal(activitySource.Version, scope.Version); + Assert.Equal(activitySource.Tags?.Count() ?? 0, scope.Attributes.Count); + + foreach (var tag in activitySource.Tags ?? []) + { + Assert.Contains(scope.Attributes, (kvp) => kvp.Key == tag.Key && kvp.Value.StringValue == (string?)tag.Value); + } + + // Return and re-add batch to simulate reuse + request.Return(); } } @@ -341,6 +364,9 @@ void RunTest(SdkLimitOptions sdkOptions, Batch batch) Assert.Equal("1234", scope.Attributes[0].Value.StringValue); this.ArrayValueAsserts(scope.Attributes[1].Value.ArrayValue.Values); Assert.Equal(new object().ToString()!.Substring(0, 4), scope.Attributes[2].Value.StringValue); + + // Return and re-add batch to simulate reuse + request.Return(); } }