diff --git a/src/Hosting/Hosting/src/Internal/HostingApplication.cs b/src/Hosting/Hosting/src/Internal/HostingApplication.cs index cd3e2d9fb2..416c49f218 100644 --- a/src/Hosting/Hosting/src/Internal/HostingApplication.cs +++ b/src/Hosting/Hosting/src/Internal/HostingApplication.cs @@ -62,6 +62,7 @@ namespace Microsoft.AspNetCore.Hosting.Internal public long StartTimestamp { get; set; } public bool EventLogEnabled { get; set; } public Activity Activity { get; set; } + internal bool HasDiagnosticListener { get; set; } } } } diff --git a/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs b/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs index 1a676affd9..14aa549139 100644 --- a/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs +++ b/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs @@ -48,7 +48,8 @@ namespace Microsoft.AspNetCore.Hosting.Internal if (loggingEnabled || (diagnosticListenerEnabled && _diagnosticListener.IsEnabled(ActivityName, httpContext))) { - context.Activity = StartActivity(httpContext); + context.Activity = StartActivity(httpContext, out var hasDiagnosticListener); + context.HasDiagnosticListener = hasDiagnosticListener; } if (diagnosticListenerEnabled) @@ -132,7 +133,7 @@ namespace Microsoft.AspNetCore.Hosting.Internal // Always stop activity if it was started if (activity != null) { - StopActivity(httpContext, activity); + StopActivity(httpContext, activity, context.HasDiagnosticListener); } if (context.EventLogEnabled && exception != null) @@ -229,9 +230,10 @@ namespace Microsoft.AspNetCore.Hosting.Internal } [MethodImpl(MethodImplOptions.NoInlining)] - private Activity StartActivity(HttpContext httpContext) + private Activity StartActivity(HttpContext httpContext, out bool hasDiagnosticListener) { var activity = new Activity(ActivityName); + hasDiagnosticListener = false; var headers = httpContext.Request.Headers; if (!headers.TryGetValue(HeaderNames.TraceParent, out var requestId)) @@ -266,6 +268,7 @@ namespace Microsoft.AspNetCore.Hosting.Internal if (_diagnosticListener.IsEnabled(ActivityStartKey)) { + hasDiagnosticListener = true; _diagnosticListener.StartActivity(activity, new { HttpContext = httpContext }); } else @@ -277,9 +280,16 @@ namespace Microsoft.AspNetCore.Hosting.Internal } [MethodImpl(MethodImplOptions.NoInlining)] - private void StopActivity(HttpContext httpContext, Activity activity) + private void StopActivity(HttpContext httpContext, Activity activity, bool hasDiagnosticListener) { - _diagnosticListener.StopActivity(activity, new { HttpContext = httpContext }); + if (hasDiagnosticListener) + { + _diagnosticListener.StopActivity(activity, new { HttpContext = httpContext }); + } + else + { + activity.Stop(); + } } } } diff --git a/src/Hosting/Hosting/test/HostingApplicationTests.cs b/src/Hosting/Hosting/test/HostingApplicationTests.cs index 5fbaf5fe7a..7454d738ef 100644 --- a/src/Hosting/Hosting/test/HostingApplicationTests.cs +++ b/src/Hosting/Hosting/test/HostingApplicationTests.cs @@ -56,6 +56,51 @@ namespace Microsoft.AspNetCore.Hosting.Tests Assert.Equal(Activity.Current.Id, pairs["ActivityId"].ToString()); } + [Fact] + public void ActivityStopDoesNotFireIfNoListenerAttachedForStart() + { + // Arrange + var diagnosticSource = new DiagnosticListener("DummySource"); + var logger = new LoggerWithScopes(isEnabled: true); + var hostingApplication = CreateApplication(out var features, diagnosticSource: diagnosticSource, logger: logger); + var startFired = false; + var stopFired = false; + + diagnosticSource.Subscribe(new CallbackDiagnosticListener(pair => + { + // This should not fire + if (pair.Key == "Microsoft.AspNetCore.Hosting.HttpRequestIn.Start") + { + startFired = true; + } + + // This should not fire + if (pair.Key == "Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop") + { + stopFired = true; + } + }), + (s, o, arg3) => + { + // The events are off + return false; + }); + + + // Act + var context = hostingApplication.CreateContext(features); + + Assert.Single(logger.Scopes); + var pairs = ((IReadOnlyList>)logger.Scopes[0]).ToDictionary(p => p.Key, p => p.Value); + Assert.Equal(Activity.Current.Id, pairs["ActivityId"].ToString()); + + hostingApplication.DisposeContext(context, exception: null); + + Assert.False(startFired); + Assert.False(stopFired); + Assert.Null(Activity.Current); + } + [Fact] public void ActivityIsNotCreatedWhenIsEnabledForActivityIsFalse() {