diff --git a/CHANGELOG.md b/CHANGELOG.md index ae68fdbfdc..9404d0d56b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ ### Fixes +- OTel activities that are marked as not recorded are no longer sent to Sentry ([#3890](https://github.com/getsentry/sentry-dotnet/pull/3890)) - Fixed duplicate SentryMauiEventProcessors ([#3905](https://github.com/getsentry/sentry-dotnet/pull/3905)) ### Dependencies diff --git a/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs b/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs index 09172c0636..dca597950a 100644 --- a/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs +++ b/src/Sentry.OpenTelemetry/SentrySpanProcessor.cs @@ -181,6 +181,14 @@ public override void OnEnd(Activity data) return; } + // Skip any activities that are not recorded. + if (data is { Recorded: false }) + { + _options?.DiagnosticLogger?.LogDebug($"Ignoring unrecorded Activity {data.SpanId}."); + _map.TryRemove(data.SpanId, out _); + return; + } + // Make a dictionary of the attributes (aka "tags") for faster lookup when used throughout the processor. var attributes = data.TagObjects.ToDict(); diff --git a/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs b/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs index 92839b2f56..1b199c9fc4 100644 --- a/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs +++ b/test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs @@ -459,6 +459,88 @@ public void OnEnd_FinishesTransaction() } } + [Fact] + public void OnEnd_FilteredTransaction_DoesNotFinishTransaction() + { + // Arrange + _fixture.Options.Instrumenter = Instrumenter.OpenTelemetry; + var sut = _fixture.GetSut(); + + var parent = Tracer.StartActivity("transaction")!; + sut.OnStart(parent); + + var data = Tracer.StartActivity("test operation", kind: ActivityKind.Internal)!; + data.DisplayName = "test display name"; + sut.OnStart(data); + + FilterActivity(parent); + + sut._map.TryGetValue(parent.SpanId, out var span); + + // Act + sut.OnEnd(data); + sut.OnEnd(parent); + + // Assert + if (span is not TransactionTracer transaction) + { + Assert.Fail("Span is not a transaction tracer"); + return; + } + + using (new AssertionScope()) + { + transaction.EndTimestamp.Should().BeNull(); + transaction.Status.Should().BeNull(); + } + } + + [Fact] + public void OnEnd_FilteredSpan_RemovesSpan() + { + // Arrange + _fixture.Options.Instrumenter = Instrumenter.OpenTelemetry; + var sut = _fixture.GetSut(); + + var parent = Tracer.StartActivity("transaction")!; + sut.OnStart(parent); + + var data = Tracer.StartActivity("test operation", kind: ActivityKind.Internal)!; + data.DisplayName = "test display name"; + sut.OnStart(data); + + FilterActivity(data); + + sut._map.TryGetValue(parent.SpanId, out var parentSpan); + sut._map.TryGetValue(data.SpanId, out var childSpan); + + // Act + sut.OnEnd(data); + sut.OnEnd(parent); + + // Assert + if (parentSpan is not TransactionTracer transaction) + { + Assert.Fail("parentSpan is not a transaction tracer"); + return; + } + if (childSpan is not SpanTracer span) + { + Assert.Fail("span is not a span tracer"); + return; + } + + using (new AssertionScope()) + { + span.EndTimestamp.Should().BeNull(); + span.Status.Should().BeNull(); + + transaction.EndTimestamp.Should().NotBeNull(); + transaction.Status.Should().Be(SpanStatus.Ok); + transaction.Spans.Should().BeEmpty(); + } + } + [Theory] [InlineData(OtelSemanticConventions.AttributeUrlFull)] [InlineData(OtelSemanticConventions.AttributeHttpUrl)]