Skip to content

Commit

Permalink
Apply Scope to exceptions captured via Activity.RecordException (#2712)
Browse files Browse the repository at this point in the history
  • Loading branch information
jamescrosswell committed Oct 10, 2023
1 parent 46f6d25 commit 41714ae
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 9 deletions.
4 changes: 3 additions & 1 deletion src/Sentry.AspNetCore/ScopeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,9 @@ private static void SetEnv(Scope scope, HttpContext context, SentryAspNetCoreOpt

private static void SetBody(Scope scope, HttpContext context, SentryAspNetCoreOptions options)
{
var extractors = context.RequestServices.GetService<IEnumerable<IRequestPayloadExtractor>>();
// Resharper says this can't be null, but actually it can if SetBody is called multiple times in a single request
// ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
var extractors = context.RequestServices?.GetService<IEnumerable<IRequestPayloadExtractor>>();
if (extractors == null)
{
return;
Expand Down
25 changes: 19 additions & 6 deletions src/Sentry.OpenTelemetry/SentrySpanProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,6 @@ public override void OnEnd(Activity data)

// Transactions set otel attributes (and resource attributes) as context.
transaction.Contexts["otel"] = GetOtelContext(attributes);
// Events are received/processed in a different AsyncLocal context. Restoring the scope that started it.
var activityScope = data.GetFused<Scope>();
if (activityScope is { } savedScope && _hub is Hub hub)
{
hub.RestoreScope(savedScope);
}
}
else
{
Expand All @@ -175,6 +169,12 @@ public override void OnEnd(Activity data)
span.SetExtra("otel.kind", data.Kind);
}

// Events are received/processed in a different AsyncLocal context. Restoring the scope that started it.
var activityScope = GetSavedScope(data);
if (activityScope is { } savedScope && _hub is Hub hub)
{
hub.RestoreScope(savedScope);
}
GenerateSentryErrorsFromOtelSpan(data, attributes);

var status = GetSpanStatus(data.Status, attributes);
Expand All @@ -187,6 +187,19 @@ public override void OnEnd(Activity data)
_map.TryRemove(data.SpanId, out _);
}

private static Scope? GetSavedScope(Activity? activity)
{
while (activity is not null)
{
if (activity.GetFused<Scope>() is {} savedScope)
{
return savedScope;
}
activity = activity.Parent;
}
return null;
}

internal static SpanStatus GetSpanStatus(ActivityStatusCode status, IDictionary<string, object?> attributes)
{
// See https://github.com/open-telemetry/opentelemetry-dotnet/discussions/4703
Expand Down
26 changes: 24 additions & 2 deletions test/Sentry.OpenTelemetry.Tests/SentrySpanProcessorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ public void OnEnd_FinishesSpan()
}

[Fact]
public void OnEnd_RestoresSavedScope()
public void OnEnd_Transaction_RestoresSavedScope()
{
// Arrange
_fixture.Options.Instrumenter = Instrumenter.OpenTelemetry;
Expand All @@ -276,7 +276,29 @@ public void OnEnd_RestoresSavedScope()
data.SetFused(scope);
sut.OnStart(data);

sut._map.TryGetValue(data.SpanId, out var span);
// Act
sut.OnEnd(data);

// Assert
_fixture.ScopeManager.Received(1).RestoreScope(scope);
}

[Fact]
public void OnEnd_Span_RestoresSavedScope()
{
// Arrange
_fixture.Options.Instrumenter = Instrumenter.OpenTelemetry;
_fixture.ScopeManager = Substitute.For<IInternalScopeManager>();
var sut = _fixture.GetSut();

var scope = new Scope();
var parent = Tracer.StartActivity("transaction")!;
parent.SetFused(scope);
sut.OnStart(parent);

var data = Tracer.StartActivity("test operation")!;
data.DisplayName = "test display name";
sut.OnStart(data);

// Act
sut.OnEnd(data);
Expand Down

0 comments on commit 41714ae

Please sign in to comment.