Skip to content

Commit 7c88389

Browse files
[.NET7.0] AspNetCore ActivitySource Migration (#3391)
1 parent 3a90287 commit 7c88389

File tree

3 files changed

+89
-5
lines changed

3 files changed

+89
-5
lines changed

src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
3535
internal class HttpInListener : ListenerHandler
3636
{
3737
internal const string ActivityOperationName = "Microsoft.AspNetCore.Hosting.HttpRequestIn";
38+
#if NET7_0_OR_GREATER
39+
// https://github.com/dotnet/aspnetcore/blob/8d6554e655b64da75b71e0e20d6db54a3ba8d2fb/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs#L85
40+
internal static readonly string AspNetCoreActivitySourceName = "Microsoft.AspNetCore";
41+
#endif
3842
internal static readonly AssemblyName AssemblyName = typeof(HttpInListener).Assembly.GetName();
3943
internal static readonly string ActivitySourceName = AssemblyName.Name;
4044
internal static readonly Version Version = AssemblyName.Version;
@@ -96,8 +100,14 @@ public override void OnStartActivity(Activity activity, object payload)
96100
// Create a new activity with its parent set from the extracted context.
97101
// This makes the new activity as a "sibling" of the activity created by
98102
// Asp.Net Core.
103+
#if NET7_0_OR_GREATER
104+
// For NET7.0 onwards activity is created using ActivitySource so,
105+
// we will use the source of the activity to create the new one.
106+
Activity newOne = activity.Source.CreateActivity(ActivityOperationName, ActivityKind.Server, ctx.ActivityContext);
107+
#else
99108
Activity newOne = new Activity(ActivityOperationName);
100109
newOne.SetParentId(ctx.ActivityContext.TraceId, ctx.ActivityContext.SpanId, ctx.ActivityContext.TraceFlags);
110+
#endif
101111
newOne.TraceStateString = ctx.ActivityContext.TraceState;
102112

103113
newOne.SetTag("IsCreatedByInstrumentation", bool.TrueString);
@@ -135,8 +145,10 @@ public override void OnStartActivity(Activity activity, object payload)
135145
return;
136146
}
137147

148+
#if !NET7_0_OR_GREATER
138149
ActivityInstrumentationHelper.SetActivitySourceProperty(activity, ActivitySource);
139150
ActivityInstrumentationHelper.SetKindProperty(activity, ActivityKind.Server);
151+
#endif
140152

141153
var path = (request.PathBase.HasValue || request.Path.HasValue) ? (request.PathBase + request.Path).ToString() : "/";
142154
activity.DisplayName = path;

src/OpenTelemetry.Instrumentation.AspNetCore/TracerProviderBuilderExtensions.cs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
// </copyright>
1616

1717
using System;
18+
#if NET7_0_OR_GREATER
19+
using System.Diagnostics;
20+
using Microsoft.Extensions.DependencyInjection;
21+
#endif
1822
using OpenTelemetry.Instrumentation.AspNetCore;
1923
using OpenTelemetry.Instrumentation.AspNetCore.Implementation;
2024
using OpenTelemetry.Internal;
@@ -42,7 +46,7 @@ public static TracerProviderBuilder AddAspNetCoreInstrumentation(
4246
{
4347
return deferredTracerProviderBuilder.Configure((sp, builder) =>
4448
{
45-
AddAspNetCoreInstrumentation(builder, sp.GetOptions<AspNetCoreInstrumentationOptions>(), configureAspNetCoreInstrumentationOptions);
49+
AddAspNetCoreInstrumentation(builder, sp.GetOptions<AspNetCoreInstrumentationOptions>(), configureAspNetCoreInstrumentationOptions, sp);
4650
});
4751
}
4852

@@ -51,22 +55,44 @@ public static TracerProviderBuilder AddAspNetCoreInstrumentation(
5155

5256
internal static TracerProviderBuilder AddAspNetCoreInstrumentation(
5357
this TracerProviderBuilder builder,
54-
AspNetCoreInstrumentation instrumentation)
58+
AspNetCoreInstrumentation instrumentation,
59+
IServiceProvider serviceProvider = null)
5560
{
61+
// For .NET7.0 onwards activity will be created using activitySource.
62+
// https://github.com/dotnet/aspnetcore/blob/bf3352f2422bf16fa3ca49021f0e31961ce525eb/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs#L327
63+
// For .NET6.0 and below, we will continue to use legacy way.
64+
#if NET7_0_OR_GREATER
65+
// TODO: Check with .NET team to see if this can be prevented
66+
// as this allows user to override the ActivitySource.
67+
var activitySourceService = serviceProvider?.GetService<ActivitySource>();
68+
if (activitySourceService != null)
69+
{
70+
builder.AddSource(activitySourceService.Name);
71+
}
72+
else
73+
{
74+
// For users not using hosting package?
75+
builder.AddSource(HttpInListener.AspNetCoreActivitySourceName);
76+
}
77+
#else
5678
builder.AddSource(HttpInListener.ActivitySourceName);
5779
builder.AddLegacySource(HttpInListener.ActivityOperationName); // for the activities created by AspNetCore
80+
#endif
81+
5882
return builder.AddInstrumentation(() => instrumentation);
5983
}
6084

6185
private static TracerProviderBuilder AddAspNetCoreInstrumentation(
6286
TracerProviderBuilder builder,
6387
AspNetCoreInstrumentationOptions options,
64-
Action<AspNetCoreInstrumentationOptions> configure = null)
88+
Action<AspNetCoreInstrumentationOptions> configure = null,
89+
IServiceProvider serviceProvider = null)
6590
{
6691
configure?.Invoke(options);
6792
return AddAspNetCoreInstrumentation(
6893
builder,
69-
new AspNetCoreInstrumentation(new HttpInListener(options)));
94+
new AspNetCoreInstrumentation(new HttpInListener(options)),
95+
serviceProvider);
7096
}
7197
}
7298
}

test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ public async Task ExtractContextIrrespectiveOfSamplingDecision(SamplingDecision
348348
var expectedTraceId = ActivityTraceId.CreateRandom();
349349
var expectedParentSpanId = ActivitySpanId.CreateRandom();
350350
var expectedTraceState = "rojo=1,congo=2";
351-
var activityContext = new ActivityContext(expectedTraceId, expectedParentSpanId, ActivityTraceFlags.Recorded, expectedTraceState);
351+
var activityContext = new ActivityContext(expectedTraceId, expectedParentSpanId, ActivityTraceFlags.Recorded, expectedTraceState, true);
352352
var expectedBaggage = Baggage.SetBaggage("key1", "value1").SetBaggage("key2", "value2");
353353
Sdk.SetDefaultTextMapPropagator(new ExtractOnlyPropagator(activityContext, expectedBaggage));
354354

@@ -553,6 +553,47 @@ void ConfigureTestServices(IServiceCollection services)
553553
Assert.Equal(shouldEnrichBeCalled, enrichCalled);
554554
}
555555

556+
#if NET7_0_OR_GREATER
557+
[Fact]
558+
public async Task UserRegisteredActivitySourceIsUsedForActivityCreationByAspNetCore()
559+
{
560+
var exportedItems = new List<Activity>();
561+
void ConfigureTestServices(IServiceCollection services)
562+
{
563+
services.AddOpenTelemetryTracing(options =>
564+
{
565+
options.AddAspNetCoreInstrumentation()
566+
.AddInMemoryExporter(exportedItems);
567+
});
568+
569+
// Register ActivitySource here so that it will be used
570+
// by ASP.NET Core to create activities
571+
// https://github.com/dotnet/aspnetcore/blob/0e5cbf447d329a1e7d69932c3decd1c70a00fbba/src/Hosting/Hosting/src/Internal/WebHost.cs#L152
572+
services.AddSingleton(sp => new ActivitySource("UserRegisteredActivitySource"));
573+
}
574+
575+
// Arrange
576+
using (var client = this.factory
577+
.WithWebHostBuilder(builder =>
578+
builder.ConfigureTestServices(ConfigureTestServices))
579+
.CreateClient())
580+
{
581+
// Act
582+
var response = await client.GetAsync("/api/values");
583+
584+
// Assert
585+
response.EnsureSuccessStatusCode(); // Status Code 200-299
586+
587+
WaitForActivityExport(exportedItems, 1);
588+
}
589+
590+
Assert.Single(exportedItems);
591+
var activity = exportedItems[0];
592+
593+
Assert.Equal("UserRegisteredActivitySource", activity.Source.Name);
594+
}
595+
#endif
596+
556597
public void Dispose()
557598
{
558599
this.tracerProvider?.Dispose();
@@ -575,8 +616,13 @@ private static void WaitForActivityExport(List<Activity> exportedItems, int coun
575616
private static void ValidateAspNetCoreActivity(Activity activityToValidate, string expectedHttpPath)
576617
{
577618
Assert.Equal(ActivityKind.Server, activityToValidate.Kind);
619+
#if NET7_0_OR_GREATER
620+
Assert.Equal(HttpInListener.AspNetCoreActivitySourceName, activityToValidate.Source.Name);
621+
Assert.Empty(activityToValidate.Source.Version);
622+
#else
578623
Assert.Equal(HttpInListener.ActivitySourceName, activityToValidate.Source.Name);
579624
Assert.Equal(HttpInListener.Version.ToString(), activityToValidate.Source.Version);
625+
#endif
580626
Assert.Equal(expectedHttpPath, activityToValidate.GetTagValue(SemanticConventions.AttributeHttpTarget) as string);
581627
}
582628

0 commit comments

Comments
 (0)