ASP.NET & ASP.NET Core: Clear baggage when root activity is complete (#2284)

* Clear baggage when root activity is complete.

* Test fixes.

* Code review.

* A couple improvements found reviewing baggage code.
This commit is contained in:
Mikel Blanchard 2021-08-30 12:46:01 -07:00 committed by GitHub
parent 5eed27ab5b
commit 69c6bf2de9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 200 additions and 62 deletions

View File

@ -29,7 +29,7 @@ namespace OpenTelemetry
/// </remarks>
public readonly struct Baggage : IEquatable<Baggage>
{
private static readonly RuntimeContextSlot<Baggage> RuntimeContextSlot = RuntimeContext.RegisterSlot<Baggage>("otel.baggage");
private static readonly RuntimeContextSlot<BaggageHolder> RuntimeContextSlot = RuntimeContext.RegisterSlot<BaggageHolder>("otel.baggage");
private static readonly Dictionary<string, string> EmptyBaggage = new Dictionary<string, string>();
private readonly Dictionary<string, string> baggage;
@ -48,8 +48,19 @@ namespace OpenTelemetry
/// </summary>
public static Baggage Current
{
get => RuntimeContextSlot.Get();
set => RuntimeContextSlot.Set(value);
get => RuntimeContextSlot.Get()?.Baggage ?? default;
set
{
var baggageHolder = RuntimeContextSlot.Get();
if (baggageHolder == null)
{
RuntimeContextSlot.Set(new BaggageHolder { Baggage = value });
}
else
{
baggageHolder.Baggage = value;
}
}
}
/// <summary>
@ -83,7 +94,7 @@ namespace OpenTelemetry
return default;
}
Dictionary<string, string> baggageCopy = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
Dictionary<string, string> baggageCopy = new Dictionary<string, string>(baggageItems.Count, StringComparer.OrdinalIgnoreCase);
foreach (KeyValuePair<string, string> baggageItem in baggageItems)
{
if (string.IsNullOrEmpty(baggageItem.Value))
@ -222,7 +233,7 @@ namespace OpenTelemetry
/// <returns>New <see cref="Baggage"/> containing the key/value pair.</returns>
public Baggage SetBaggage(IEnumerable<KeyValuePair<string, string>> baggageItems)
{
if ((baggageItems?.Count() ?? 0) <= 0)
if (baggageItems?.Any() != true)
{
return this;
}
@ -305,5 +316,10 @@ namespace OpenTelemetry
return res;
}
}
private class BaggageHolder
{
public Baggage Baggage;
}
}
}

View File

@ -83,7 +83,7 @@ namespace OpenTelemetry.Instrumentation.AspNet
{
context.Items[ActivityKey] = activity;
if (propagationContext.Baggage != default)
if (!(textMapPropagator is TraceContextPropagator))
{
// todo: RestoreActivityIfNeeded below compensates for
// AsyncLocal Activity.Current being lost. Baggage
@ -116,11 +116,12 @@ namespace OpenTelemetry.Instrumentation.AspNet
/// <summary>
/// Stops the activity and notifies listeners about it.
/// </summary>
/// <param name="textMapPropagator"><see cref="TextMapPropagator"/>.</param>
/// <param name="aspNetActivity"><see cref="Activity"/>.</param>
/// <param name="context"><see cref="HttpContext"/>.</param>
/// <param name="onRequestStoppedCallback">Callback action.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StopAspNetActivity(Activity aspNetActivity, HttpContext context, Action<Activity, HttpContext> onRequestStoppedCallback)
public static void StopAspNetActivity(TextMapPropagator textMapPropagator, Activity aspNetActivity, HttpContext context, Action<Activity, HttpContext> onRequestStoppedCallback)
{
Debug.Assert(context != null, "Context is null.");
@ -152,6 +153,11 @@ namespace OpenTelemetry.Instrumentation.AspNet
AspNetTelemetryEventSource.Log.ActivityStopped(currentActivity);
if (!(textMapPropagator is TraceContextPropagator))
{
Baggage.Current = default;
}
if (currentActivity != aspNetActivity)
{
Activity.Current = currentActivity;

View File

@ -145,7 +145,7 @@ namespace OpenTelemetry.Instrumentation.AspNet
if (trackActivity)
{
ActivityHelper.StopAspNetActivity(aspNetActivity, context, Options.OnRequestStoppedCallback);
ActivityHelper.StopAspNetActivity(Options.TextMapPropagator, aspNetActivity, context, Options.OnRequestStoppedCallback);
}
}

View File

@ -25,13 +25,9 @@ namespace OpenTelemetry.Instrumentation.AspNetCore
{
private readonly DiagnosticSourceSubscriber diagnosticSourceSubscriber;
/// <summary>
/// Initializes a new instance of the <see cref="AspNetCoreInstrumentation"/> class.
/// </summary>
/// <param name="options">Configuration options for ASP.NET Core instrumentation.</param>
public AspNetCoreInstrumentation(AspNetCoreInstrumentationOptions options)
public AspNetCoreInstrumentation(HttpInListener httpInListener)
{
this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new HttpInListener("Microsoft.AspNetCore", options), null);
this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(httpInListener, null);
this.diagnosticSourceSubscriber.Subscribe();
}

View File

@ -19,11 +19,15 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
#if !NETSTANDARD2_0
using System.Runtime.CompilerServices;
#endif
using System.Text;
using Microsoft.AspNetCore.Http;
using OpenTelemetry.Context.Propagation;
#if !NETSTANDARD2_0
using OpenTelemetry.Instrumentation.GrpcNetClient;
#endif
using OpenTelemetry.Trace;
namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
@ -35,6 +39,7 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
internal static readonly string ActivitySourceName = AssemblyName.Name;
internal static readonly Version Version = AssemblyName.Version;
internal static readonly ActivitySource ActivitySource = new ActivitySource(ActivitySourceName, Version.ToString());
private const string DiagnosticSourceName = "Microsoft.AspNetCore";
private const string UnknownHostName = "UNKNOWN-HOST";
private static readonly Func<HttpRequest, string, IEnumerable<string>> HttpRequestHeaderValuesGetter = (request, name) => request.Headers[name];
private readonly PropertyFetcher<HttpContext> startContextFetcher = new PropertyFetcher<HttpContext>("HttpContext");
@ -45,8 +50,8 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
private readonly PropertyFetcher<string> beforeActionTemplateFetcher = new PropertyFetcher<string>("Template");
private readonly AspNetCoreInstrumentationOptions options;
public HttpInListener(string name, AspNetCoreInstrumentationOptions options)
: base(name)
public HttpInListener(AspNetCoreInstrumentationOptions options)
: base(DiagnosticSourceName)
{
this.options = options ?? throw new ArgumentNullException(nameof(options));
}
@ -103,10 +108,7 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
activity = newOne;
}
if (ctx.Baggage != default)
{
Baggage.Current = ctx.Baggage;
}
Baggage.Current = ctx.Baggage;
}
// enrich Activity from payload only if sampling decision
@ -184,17 +186,14 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
activity.SetTag(SemanticConventions.AttributeHttpStatusCode, response.StatusCode);
#if NETSTANDARD2_1 || NETCOREAPP3_1 || NET5_0
#if !NETSTANDARD2_0
if (this.options.EnableGrpcAspNetCoreSupport && TryGetGrpcMethod(activity, out var grpcMethod))
{
AddGrpcAttributes(activity, grpcMethod, context);
}
else
else if (activity.GetStatus().StatusCode == StatusCode.Unset)
{
if (activity.GetStatus().StatusCode == StatusCode.Unset)
{
activity.SetStatus(SpanHelper.ResolveSpanStatusForHttpStatusCode(response.StatusCode));
}
activity.SetStatus(SpanHelper.ResolveSpanStatusForHttpStatusCode(response.StatusCode));
}
#else
if (activity.GetStatus().StatusCode == StatusCode.Unset)
@ -231,6 +230,12 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
// the one created by the instrumentation.
// And retrieve it here, and set it to Current.
}
var textMapPropagator = Propagators.DefaultTextMapPropagator;
if (!(textMapPropagator is TraceContextPropagator))
{
Baggage.Current = default;
}
}
public override void OnCustom(string name, Activity activity, object payload)
@ -325,7 +330,7 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
return builder.ToString();
}
#if NETSTANDARD2_1 || NETCOREAPP3_1 || NET5_0
#if !NETSTANDARD2_0
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryGetGrpcMethod(Activity activity, out string grpcMethod)
{

View File

@ -51,13 +51,24 @@ namespace OpenTelemetry.Trace
return AddAspNetCoreInstrumentation(builder, new AspNetCoreInstrumentationOptions(), configureAspNetCoreInstrumentationOptions);
}
private static TracerProviderBuilder AddAspNetCoreInstrumentation(TracerProviderBuilder builder, AspNetCoreInstrumentationOptions options, Action<AspNetCoreInstrumentationOptions> configure = null)
internal static TracerProviderBuilder AddAspNetCoreInstrumentation(
this TracerProviderBuilder builder,
AspNetCoreInstrumentation instrumentation)
{
configure?.Invoke(options);
var instrumentation = new AspNetCoreInstrumentation(options);
builder.AddSource(HttpInListener.ActivitySourceName);
builder.AddLegacySource(HttpInListener.ActivityOperationName); // for the activities created by AspNetCore
return builder.AddInstrumentation(() => instrumentation);
}
private static TracerProviderBuilder AddAspNetCoreInstrumentation(
TracerProviderBuilder builder,
AspNetCoreInstrumentationOptions options,
Action<AspNetCoreInstrumentationOptions> configure = null)
{
configure?.Invoke(options);
return AddAspNetCoreInstrumentation(
builder,
new AspNetCoreInstrumentation(new HttpInListener(options)));
}
}
}

View File

@ -32,15 +32,11 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
private const string TraceParentHeaderName = "traceparent";
private const string TraceStateHeaderName = "tracestate";
private const string BaggageHeaderName = "baggage";
private const string BaggageInHeader = "TestKey1=123,TestKey2=456,TestKey1=789";
private const string TestActivityName = "Activity.Test";
private readonly string baggageInHeader;
private readonly TextMapPropagator noopTextMapPropagator = new NoopTextMapPropagator();
private ActivityListener activitySourceListener;
public ActivityHelperTest()
{
this.baggageInHeader = "TestKey1=123,TestKey2=456,TestKey1=789";
}
public void Dispose()
{
this.activitySourceListener?.Dispose();
@ -78,7 +74,7 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
{
this.EnableListener();
var context = HttpContextHelper.GetFakeHttpContext();
var rootActivity = ActivityHelper.StartAspNetActivity(new NoopTextMapPropagator(), context, null);
var rootActivity = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
rootActivity.AddTag("k1", "v1");
rootActivity.AddTag("k2", "v2");
@ -99,13 +95,13 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
Assert.Equal(TelemetryHttpModule.AspNetActivityName, Activity.Current.OperationName);
});
var context = HttpContextHelper.GetFakeHttpContext();
var rootActivity = ActivityHelper.StartAspNetActivity(new NoopTextMapPropagator(), context, null);
var rootActivity = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
rootActivity.AddTag("k1", "v1");
rootActivity.AddTag("k2", "v2");
Activity.Current = null;
ActivityHelper.StopAspNetActivity(rootActivity, context, null);
ActivityHelper.StopAspNetActivity(this.noopTextMapPropagator, rootActivity, context, null);
Assert.True(rootActivity.Duration != TimeSpan.Zero);
Assert.Null(Activity.Current);
Assert.Null(context.Items[ActivityHelper.ActivityKey]);
@ -142,7 +138,7 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
rootActivity.Start();
context.Items[ActivityHelper.ActivityKey] = rootActivity;
Thread.Sleep(100);
ActivityHelper.StopAspNetActivity(rootActivity, context, null);
ActivityHelper.StopAspNetActivity(this.noopTextMapPropagator, rootActivity, context, null);
Assert.True(rootActivity.Duration != TimeSpan.Zero);
Assert.Null(rootActivity.Parent);
@ -158,7 +154,7 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
context.Items[ActivityHelper.ActivityKey] = rootActivity;
Thread.Sleep(100);
this.EnableListener();
ActivityHelper.StopAspNetActivity(rootActivity, context, null);
ActivityHelper.StopAspNetActivity(this.noopTextMapPropagator, rootActivity, context, null);
Assert.True(rootActivity.Duration != TimeSpan.Zero);
Assert.Null(rootActivity.Parent);
@ -170,12 +166,12 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
{
this.EnableListener();
var context = HttpContextHelper.GetFakeHttpContext();
var rootActivity = ActivityHelper.StartAspNetActivity(new NoopTextMapPropagator(), context, null);
var rootActivity = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
var child = new Activity("child").Start();
new Activity("grandchild").Start();
ActivityHelper.StopAspNetActivity(rootActivity, context, null);
ActivityHelper.StopAspNetActivity(this.noopTextMapPropagator, rootActivity, context, null);
Assert.True(rootActivity.Duration != TimeSpan.Zero);
Assert.True(child.Duration == TimeSpan.Zero);
@ -188,10 +184,10 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
{
this.EnableListener();
var context = HttpContextHelper.GetFakeHttpContext();
var rootActivity = ActivityHelper.StartAspNetActivity(new NoopTextMapPropagator(), context, null);
var rootActivity = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
var child = new Activity("child").Start();
ActivityHelper.StopAspNetActivity(rootActivity, context, null);
ActivityHelper.StopAspNetActivity(this.noopTextMapPropagator, rootActivity, context, null);
Assert.True(child.Duration == TimeSpan.Zero);
Assert.NotNull(Activity.Current);
@ -204,7 +200,7 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
{
this.EnableListener();
var context = HttpContextHelper.GetFakeHttpContext();
var root = ActivityHelper.StartAspNetActivity(new NoopTextMapPropagator(), context, null);
var root = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
new Activity("child").Start();
for (int i = 0; i < 2; i++)
@ -223,7 +219,7 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
// do not affect 'parent' context in which Task.Run is called.
// But 'child' Activity is stopped, thus consequent calls to Stop will
// not update Current
ActivityHelper.StopAspNetActivity(root, context, null);
ActivityHelper.StopAspNetActivity(this.noopTextMapPropagator, root, context, null);
Assert.True(root.Duration != TimeSpan.Zero);
Assert.Null(context.Items[ActivityHelper.ActivityKey]);
Assert.Null(Activity.Current);
@ -234,7 +230,7 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
{
this.EnableListener();
var context = HttpContextHelper.GetFakeHttpContext();
var root = ActivityHelper.StartAspNetActivity(new NoopTextMapPropagator(), context, null);
var root = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
for (int i = 0; i < 129; i++)
{
@ -242,7 +238,7 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
}
// can stop any activity regardless of the stack depth
ActivityHelper.StopAspNetActivity(root, context, null);
ActivityHelper.StopAspNetActivity(this.noopTextMapPropagator, root, context, null);
Assert.True(root.Duration != TimeSpan.Zero);
Assert.Null(context.Items[ActivityHelper.ActivityKey]);
@ -253,12 +249,12 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
public void Should_Not_Create_RootActivity_If_AspNetListener_Not_Enabled()
{
var context = HttpContextHelper.GetFakeHttpContext();
var rootActivity = ActivityHelper.StartAspNetActivity(new NoopTextMapPropagator(), context, null);
var rootActivity = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
Assert.Null(rootActivity);
Assert.Equal(ActivityHelper.StartedButNotSampledObj, context.Items[ActivityHelper.ActivityKey]);
ActivityHelper.StopAspNetActivity(rootActivity, context, null);
ActivityHelper.StopAspNetActivity(this.noopTextMapPropagator, rootActivity, context, null);
Assert.Null(context.Items[ActivityHelper.ActivityKey]);
}
@ -267,12 +263,12 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
{
var context = HttpContextHelper.GetFakeHttpContext();
this.EnableListener(onSample: (context) => ActivitySamplingResult.None);
var rootActivity = ActivityHelper.StartAspNetActivity(new NoopTextMapPropagator(), context, null);
var rootActivity = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
Assert.Null(rootActivity);
Assert.Equal(ActivityHelper.StartedButNotSampledObj, context.Items[ActivityHelper.ActivityKey]);
ActivityHelper.StopAspNetActivity(rootActivity, context, null);
ActivityHelper.StopAspNetActivity(this.noopTextMapPropagator, rootActivity, context, null);
Assert.Null(context.Items[ActivityHelper.ActivityKey]);
}
@ -334,7 +330,7 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
var requestHeaders = new Dictionary<string, string>
{
{ TraceParentHeaderName, "00-0123456789abcdef0123456789abcdef-0123456789abcdef-00" },
{ BaggageHeaderName, this.baggageInHeader },
{ BaggageHeaderName, BaggageInHeader },
};
var context = HttpContextHelper.GetFakeHttpContext(headers: requestHeaders);
@ -353,6 +349,10 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
Assert.Equal(2, Baggage.Current.Count);
Assert.Equal("789", Baggage.Current.GetBaggage("TestKey1"));
Assert.Equal("456", Baggage.Current.GetBaggage("TestKey2"));
ActivityHelper.StopAspNetActivity(this.noopTextMapPropagator, rootActivity, context, null);
Assert.Equal(0, Baggage.Current.Count);
}
[Fact]
@ -365,7 +365,7 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
};
var context = HttpContextHelper.GetFakeHttpContext(headers: requestHeaders);
var rootActivity = ActivityHelper.StartAspNetActivity(new NoopTextMapPropagator(), context, null);
var rootActivity = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
Assert.NotNull(rootActivity);
Assert.Null(rootActivity.ParentId);
@ -377,7 +377,7 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
{
this.EnableListener();
var context = HttpContextHelper.GetFakeHttpContext();
var rootActivity = ActivityHelper.StartAspNetActivity(new NoopTextMapPropagator(), context, null);
var rootActivity = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
Assert.NotNull(rootActivity);
Assert.True(!string.IsNullOrEmpty(rootActivity.Id));
@ -388,7 +388,7 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
{
this.EnableListener();
var context = HttpContextHelper.GetFakeHttpContext();
var rootActivity = ActivityHelper.StartAspNetActivity(new NoopTextMapPropagator(), context, null);
var rootActivity = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
Assert.NotNull(rootActivity);
Assert.Same(rootActivity, context.Items[ActivityHelper.ActivityKey]);

View File

@ -167,7 +167,7 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
ActivityHelper.WriteActivityException(activity, HttpContext.Current, new InvalidOperationException(), TelemetryHttpModule.Options.OnExceptionCallback);
}
ActivityHelper.StopAspNetActivity(activity, HttpContext.Current, TelemetryHttpModule.Options.OnRequestStoppedCallback);
ActivityHelper.StopAspNetActivity(Propagators.DefaultTextMapPropagator, activity, HttpContext.Current, TelemetryHttpModule.Options.OnRequestStoppedCallback);
}
if (HttpContext.Current.Request.Path == filter || filter == "{ThrowException}")
@ -281,7 +281,7 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
.AddProcessor(activityProcessor.Object).Build())
{
var activity = ActivityHelper.StartAspNetActivity(Propagators.DefaultTextMapPropagator, HttpContext.Current, TelemetryHttpModule.Options.OnRequestStartedCallback);
ActivityHelper.StopAspNetActivity(activity, HttpContext.Current, TelemetryHttpModule.Options.OnRequestStoppedCallback);
ActivityHelper.StopAspNetActivity(Propagators.DefaultTextMapPropagator, activity, HttpContext.Current, TelemetryHttpModule.Options.OnRequestStoppedCallback);
}
Assert.True(isPropagatorCalled);
@ -324,7 +324,7 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
.AddProcessor(activityProcessor.Object).Build())
{
var activity = ActivityHelper.StartAspNetActivity(Propagators.DefaultTextMapPropagator, HttpContext.Current, TelemetryHttpModule.Options.OnRequestStartedCallback);
ActivityHelper.StopAspNetActivity(activity, HttpContext.Current, TelemetryHttpModule.Options.OnRequestStoppedCallback);
ActivityHelper.StopAspNetActivity(Propagators.DefaultTextMapPropagator, activity, HttpContext.Current, TelemetryHttpModule.Options.OnRequestStoppedCallback);
}
Assert.True(isFilterCalled);

View File

@ -41,7 +41,7 @@ using Xunit;
namespace OpenTelemetry.Instrumentation.AspNetCore.Tests
{
// See https://github.com/aspnet/Docs/tree/master/aspnetcore/test/integration-tests/samples/2.x/IntegrationTestsSample
public class BasicTests
public sealed class BasicTests
: IClassFixture<WebApplicationFactory<Startup>>, IDisposable
{
private readonly WebApplicationFactory<Startup> factory;
@ -508,6 +508,55 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Tests
}
}
[Fact]
public async Task BaggageClearedWhenActivityStopped()
{
int? baggageCountAfterStart = null;
int? baggageCountAfterStop = null;
using EventWaitHandle stopSignal = new EventWaitHandle(false, EventResetMode.ManualReset);
void ConfigureTestServices(IServiceCollection services)
{
this.openTelemetrySdk = Sdk.CreateTracerProviderBuilder()
.AddAspNetCoreInstrumentation(new AspNetCoreInstrumentation(
new TestHttpInListener(new AspNetCoreInstrumentationOptions())
{
OnStartActivityCallback = (activity, payload) =>
{
baggageCountAfterStart = Baggage.Current.Count;
},
OnStopActivityCallback = (activity, payload) =>
{
baggageCountAfterStop = Baggage.Current.Count;
stopSignal.Set();
},
}))
.Build();
}
// Arrange
using (var client = this.factory
.WithWebHostBuilder(builder =>
builder.ConfigureTestServices(ConfigureTestServices))
.CreateClient())
{
using var request = new HttpRequestMessage(HttpMethod.Get, "/api/values");
request.Headers.TryAddWithoutValidation("baggage", "TestKey1=123,TestKey2=456");
// Act
using var response = await client.SendAsync(request);
}
stopSignal.WaitOne(5000);
// Assert
Assert.NotNull(baggageCountAfterStart);
Assert.Equal(2, baggageCountAfterStart);
Assert.NotNull(baggageCountAfterStop);
Assert.Equal(0, baggageCountAfterStop);
}
[Theory]
[InlineData(SamplingDecision.Drop, false, false)]
[InlineData(SamplingDecision.RecordOnly, true, true)]
@ -622,7 +671,7 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Tests
private class TestSampler : Sampler
{
private SamplingDecision samplingDecision;
private readonly SamplingDecision samplingDecision;
public TestSampler(SamplingDecision samplingDecision)
{
@ -634,5 +683,31 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Tests
return new SamplingResult(this.samplingDecision);
}
}
private class TestHttpInListener : HttpInListener
{
public Action<Activity, object> OnStartActivityCallback;
public Action<Activity, object> OnStopActivityCallback;
public TestHttpInListener(AspNetCoreInstrumentationOptions options)
: base(options)
{
}
public override void OnStartActivity(Activity activity, object payload)
{
base.OnStartActivity(activity, payload);
this.OnStartActivityCallback?.Invoke(activity, payload);
}
public override void OnStopActivity(Activity activity, object payload)
{
base.OnStopActivity(activity, payload);
this.OnStopActivityCallback?.Invoke(activity, payload);
}
}
}
}

View File

@ -16,6 +16,8 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace OpenTelemetry.Tests
@ -269,5 +271,32 @@ namespace OpenTelemetry.Tests
Assert.Equal(expectedBaggage.GetHashCode(), baggage.GetHashCode());
}
[Fact]
public async Task AsyncLocalTests()
{
Baggage.SetBaggage("key1", "value1");
await InnerTask().ConfigureAwait(false);
Baggage.SetBaggage("key4", "value4");
Assert.Equal(4, Baggage.Current.Count);
Assert.Equal("value1", Baggage.GetBaggage("key1"));
Assert.Equal("value2", Baggage.GetBaggage("key2"));
Assert.Equal("value3", Baggage.GetBaggage("key3"));
Assert.Equal("value4", Baggage.GetBaggage("key4"));
static async Task InnerTask()
{
Baggage.SetBaggage("key2", "value2");
await Task.Yield();
Baggage.SetBaggage("key3", "value3");
// key2 & key3 changes don't flow backward automatically
}
}
}
}