Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create span when SamplingDecision.Drop #3841

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/OpenTelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

## Unreleased

* Create spans even on a sampling decision of drop. This conforms the .NET SDK
to the specification regarding
[span creation](https:/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#sdk-span-creation).
This may have a performance impact as an `Activity` will always be allocated
irrespective of sampling decision.
([#3841](https:/open-telemetry/opentelemetry-dotnet/pull/3841))

## 1.4.0

Released 2023-Feb-24
Expand Down
19 changes: 2 additions & 17 deletions src/OpenTelemetry/Trace/TracerProviderSdk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#nullable enable

using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using OpenTelemetry.Internal;
Expand Down Expand Up @@ -240,7 +239,7 @@ internal TracerProviderSdk(
else if (this.sampler is AlwaysOffSampler)
{
listener.Sample = (ref ActivityCreationOptions<ActivityContext> options) =>
!Sdk.SuppressInstrumentation ? PropagateOrIgnoreData(options.Parent) : ActivitySamplingResult.None;
!Sdk.SuppressInstrumentation ? ActivitySamplingResult.PropagationData : ActivitySamplingResult.None;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is extremely hard to read. There is some deep nesting of if/else/Listener callback code going on here. I would suggest to extract some (named, top-level) methods to make the code more readable/reviewable.

this.getRequestedDataAction = this.RunGetRequestedDataAlwaysOffSampler;
}
else
Expand Down Expand Up @@ -452,23 +451,9 @@ private static ActivitySamplingResult ComputeActivitySamplingResult(
{
options = options with { TraceState = samplingResult.TraceStateString };
}

return activitySamplingResult;
}

return PropagateOrIgnoreData(options.Parent);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ActivitySamplingResult PropagateOrIgnoreData(in ActivityContext parentContext)
{
var isRootSpan = parentContext.TraceId == default;

// If it is the root span or the parent is remote select PropagationData so the trace ID is preserved
// even if no activity of the trace is recorded (sampled per OpenTelemetry parlance).
return (isRootSpan || parentContext.IsRemote)
? ActivitySamplingResult.PropagationData
: ActivitySamplingResult.None;
return activitySamplingResult;
}

private void RunGetRequestedDataAlwaysOnSampler(Activity activity)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -618,14 +618,6 @@ public async Task CustomPropagatorCalled(bool sample, bool createParentActivity)
{
Assert.Equal(contextFromPropagator, exportedItems[0].Context);
}

#if NETFRAMEWORK
if (!sample && createParentActivity)
{
Assert.Equal(parentContext.TraceId, contextFromPropagator.TraceId);
Assert.Equal(parentContext.SpanId, contextFromPropagator.SpanId);
}
#endif
}

public void Dispose()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,14 +296,6 @@ public async Task CustomPropagatorCalled(bool sample, bool createParentActivity)
{
Assert.Equal(contextFromPropagator, exportedItems[0].Context);
}

#if NETFRAMEWORK
if (!sample && createParentActivity)
{
Assert.Equal(parentContext.TraceId, contextFromPropagator.TraceId);
Assert.Equal(parentContext.SpanId, contextFromPropagator.SpanId);
}
#endif
}

[Theory]
Expand Down
58 changes: 20 additions & 38 deletions test/OpenTelemetry.Tests/Trace/TracerProviderSdkTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -250,39 +250,6 @@ public void TracerProviderSdkSamplerAttributesAreAppliedToActivity(SamplingDecis
}
}

[Fact]
public void TracerSdkSetsActivitySamplingResultAsPropagationWhenParentIsRemote()
{
var activitySourceName = Utils.GetCurrentMethodName();
var testSampler = new TestSampler();
using var activitySource = new ActivitySource(activitySourceName);
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddSource(activitySourceName)
.SetSampler(testSampler)
.Build();

testSampler.SamplingAction = (samplingParameters) =>
{
return new SamplingResult(SamplingDecision.Drop);
};

ActivityContext ctx = new ActivityContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None, isRemote: true);

using (var activity = activitySource.StartActivity("root", ActivityKind.Server, ctx))
{
// Even if sampling returns false, for activities with remote parent,
// activity is still created with PropagationOnly.
Assert.NotNull(activity);
Assert.False(activity.IsAllDataRequested);
Assert.False(activity.Recorded);

// This is not a root activity and parent is not remote.
// If sampling returns false, no activity is created at all.
using var innerActivity = activitySource.StartActivity("inner");
Assert.Null(innerActivity);
}
}

[Fact]
public void TracerSdkSetsActivitySamplingResultBasedOnSamplingDecision()
{
Expand Down Expand Up @@ -327,16 +294,31 @@ public void TracerSdkSetsActivitySamplingResultBasedOnSamplingDecision()

using (var activity = activitySource.StartActivity("root"))
{
// Even if sampling returns false, for root activities,
// activity is still created with PropagationOnly.
// Even if sampling returns false, for root activities and their children,
// are still created with PropagationOnly.
Assert.NotNull(activity);
Assert.False(activity.IsAllDataRequested);
Assert.False(activity.Recorded);

using var innerActivity = activitySource.StartActivity("inner");
Assert.NotNull(innerActivity);
Assert.False(activity.IsAllDataRequested);
Assert.False(activity.Recorded);
}

ActivityContext ctx = new ActivityContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None, isRemote: true);
using (var activity = activitySource.StartActivity("root", ActivityKind.Server, ctx))
{
// Even if sampling returns false, for activities with remote parent and their children,
// are still created with PropagationOnly.
Assert.NotNull(activity);
Assert.False(activity.IsAllDataRequested);
Assert.False(activity.Recorded);

// This is not a root activity.
// If sampling returns false, no activity is created at all.
using var innerActivity = activitySource.StartActivity("inner");
Assert.Null(innerActivity);
Assert.NotNull(innerActivity);
Assert.False(innerActivity.IsAllDataRequested);
Assert.False(innerActivity.Recorded);
}
}

Expand Down