diff --git a/build/Common.props b/build/Common.props index 8f9490f903..0455be2fb7 100644 --- a/build/Common.props +++ b/build/Common.props @@ -34,6 +34,7 @@ Refer to https://docs.microsoft.com/en-us/nuget/concepts/package-versioning for semver syntax. --> [6.0.0,7.0) + [9.0.0,) [2.1.0,5.0) [9.0.0,) [9.0.0,) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/PublicAPI.Unshipped.txt index e69de29bb2..8bd0a39e12 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/PublicAPI.Unshipped.txt @@ -0,0 +1,2 @@ +OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnableAspNetCoreSignalRSupport.get -> bool +OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnableAspNetCoreSignalRSupport.set -> void diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationTracerProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationTracerProviderBuilderExtensions.cs index 09a60868c6..d9cbef0403 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationTracerProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationTracerProviderBuilderExtensions.cs @@ -68,7 +68,7 @@ public static TracerProviderBuilder AddAspNetCoreInstrumentation( { deferredTracerProviderBuilder.Configure((sp, builder) => { - AddAspNetCoreInstrumentationSources(builder, sp); + AddAspNetCoreInstrumentationSources(builder, name, sp); }); } @@ -84,9 +84,12 @@ public static TracerProviderBuilder AddAspNetCoreInstrumentation( // Note: This is used by unit tests. internal static TracerProviderBuilder AddAspNetCoreInstrumentation( this TracerProviderBuilder builder, - HttpInListener listener) + HttpInListener listener, + string? optionsName = null) { - builder.AddAspNetCoreInstrumentationSources(); + optionsName ??= Options.DefaultName; + + builder.AddAspNetCoreInstrumentationSources(optionsName); #pragma warning disable CA2000 return builder.AddInstrumentation( @@ -96,6 +99,7 @@ internal static TracerProviderBuilder AddAspNetCoreInstrumentation( private static void AddAspNetCoreInstrumentationSources( this TracerProviderBuilder builder, + string optionsName, IServiceProvider? serviceProvider = null) { // For .NET7.0 onwards activity will be created using activitySource. @@ -121,5 +125,16 @@ private static void AddAspNetCoreInstrumentationSources( builder.AddSource(HttpInListener.ActivitySourceName); builder.AddLegacySource(HttpInListener.ActivityOperationName); // for the activities created by AspNetCore } + + // SignalR activities first added in .NET 9.0 + if (Environment.Version.Major >= 9) + { + var options = serviceProvider?.GetRequiredService>().Get(optionsName); + if (options is null || options.EnableAspNetCoreSignalRSupport) + { + // https://github.com/dotnet/aspnetcore/blob/6ae3ea387b20f6497b82897d613e9b8a6e31d69c/src/SignalR/server/Core/src/Internal/SignalRServerActivitySource.cs#L13C35-L13C70 + builder.AddSource("Microsoft.AspNetCore.SignalR.Server"); + } + } } } diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreTraceInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreTraceInstrumentationOptions.cs index 2a35fdc3ba..566e89a216 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreTraceInstrumentationOptions.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreTraceInstrumentationOptions.cs @@ -93,6 +93,14 @@ internal AspNetCoreTraceInstrumentationOptions(IConfiguration configuration) /// public bool RecordException { get; set; } + /// + /// Gets or sets a value indicating whether SignalR activities are recorded. + /// + /// + /// Defaults to true. + /// + public bool EnableAspNetCoreSignalRSupport { get; set; } = true; + /// /// Gets or sets a value indicating whether RPC attributes are added to an Activity when using Grpc.AspNetCore. /// diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs index ac7a5929ec..a9ecc6f357 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.SignalR.Client; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -1114,6 +1115,104 @@ public async Task ValidateUrlQueryRedaction(string urlQuery, string expectedUrlQ Assert.Equal(expectedUrlQuery, activity.GetTagValue(SemanticConventions.AttributeUrlQuery)); } +#if NET9_0_OR_GREATER + [Fact] + public async Task SignalRActivitesAreListenedTo() + { + var exportedItems = new List(); + void ConfigureTestServices(IServiceCollection services) + { + this.tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddAspNetCoreInstrumentation() + .AddInMemoryExporter(exportedItems) + .Build(); + } + + // Arrange + using (var server = this.factory + .WithWebHostBuilder(builder => + { + builder.ConfigureTestServices(ConfigureTestServices); + builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); + })) + { + await using var client = new HubConnectionBuilder() + .WithUrl(server.Server.BaseAddress + "testHub", o => + { + o.HttpMessageHandlerFactory = _ => server.Server.CreateHandler(); + o.Transports = Microsoft.AspNetCore.Http.Connections.HttpTransportType.LongPolling; + }).Build(); + await client.StartAsync(); + + await client.SendAsync("Send", "text"); + + await client.StopAsync(); + } + + WaitForActivityExport(exportedItems, 11); + + var hubActivity = exportedItems + .Where(a => a.DisplayName.StartsWith("TestApp.AspNetCore.TestHub", StringComparison.InvariantCulture)); + + Assert.Equal(3, hubActivity.Count()); + Assert.Collection( + hubActivity, + one => + { + Assert.Equal("TestApp.AspNetCore.TestHub/OnConnectedAsync", one.DisplayName); + }, + two => + { + Assert.Equal("TestApp.AspNetCore.TestHub/Send", two.DisplayName); + }, + three => + { + Assert.Equal("TestApp.AspNetCore.TestHub/OnDisconnectedAsync", three.DisplayName); + }); + } + + [Fact] + public async Task SignalRActivitesCanBeDisabled() + { + var exportedItems = new List(); + void ConfigureTestServices(IServiceCollection services) + { + this.tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddAspNetCoreInstrumentation(o => o.EnableAspNetCoreSignalRSupport = false) + .AddInMemoryExporter(exportedItems) + .Build(); + } + + // Arrange + using (var server = this.factory + .WithWebHostBuilder(builder => + { + builder.ConfigureTestServices(ConfigureTestServices); + builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); + })) + { + await using var client = new HubConnectionBuilder() + .WithUrl(server.Server.BaseAddress + "testHub", o => + { + o.HttpMessageHandlerFactory = _ => server.Server.CreateHandler(); + o.Transports = Microsoft.AspNetCore.Http.Connections.HttpTransportType.LongPolling; + }).Build(); + await client.StartAsync(); + + await client.SendAsync("Send", "text"); + + await client.StopAsync(); + } + + WaitForActivityExport(exportedItems, 8); + + var hubActivity = exportedItems + .Where(a => a.DisplayName.StartsWith("TestApp.AspNetCore.TestHub", StringComparison.InvariantCulture)); + + Assert.Empty(hubActivity); + } +#endif + public void Dispose() { this.tracerProvider?.Dispose(); diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/OpenTelemetry.Instrumentation.AspNetCore.Tests.csproj b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/OpenTelemetry.Instrumentation.AspNetCore.Tests.csproj index 11def19721..d0bf16a02e 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/OpenTelemetry.Instrumentation.AspNetCore.Tests.csproj +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/OpenTelemetry.Instrumentation.AspNetCore.Tests.csproj @@ -18,6 +18,7 @@ + diff --git a/test/TestApp.AspNetCore/Program.cs b/test/TestApp.AspNetCore/Program.cs index 5cbe2b5e3a..110693b6fa 100644 --- a/test/TestApp.AspNetCore/Program.cs +++ b/test/TestApp.AspNetCore/Program.cs @@ -20,6 +20,8 @@ public static void Main(string[] args) builder.Services.AddMvc(); + builder.Services.AddSignalR(); + builder.Services.AddSingleton(); builder.Services.AddSingleton( @@ -43,6 +45,8 @@ public static void Main(string[] args) app.MapControllers(); + app.MapHub("/testHub"); + app.UseMiddleware(); app.UseMiddleware(); diff --git a/test/TestApp.AspNetCore/TestHub.cs b/test/TestApp.AspNetCore/TestHub.cs new file mode 100644 index 0000000000..73a8a09bb0 --- /dev/null +++ b/test/TestApp.AspNetCore/TestHub.cs @@ -0,0 +1,15 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Microsoft.AspNetCore.SignalR; + +namespace TestApp.AspNetCore; + +public class TestHub : Hub +{ + public override Task OnConnectedAsync() => base.OnConnectedAsync(); + + public void Send(string message) + { + } +}