diff --git a/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs b/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs index d71e8145dbdc..db385520c1f4 100644 --- a/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs +++ b/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs @@ -87,6 +87,7 @@ public GenericWebHostBuilder(IHostBuilder builder, WebHostBuilderOptions options services.TryAddSingleton(sp => new DiagnosticListener("Microsoft.AspNetCore")); services.TryAddSingleton(sp => sp.GetRequiredService()); services.TryAddSingleton(sp => new ActivitySource("Microsoft.AspNetCore")); + services.TryAddSingleton(DistributedContextPropagator.Current); services.TryAddSingleton(); services.TryAddScoped(); diff --git a/src/Hosting/Hosting/src/GenericHost/GenericWebHostedService.cs b/src/Hosting/Hosting/src/GenericHost/GenericWebHostedService.cs index 69d11d8f5187..15fa43e9b4c6 100644 --- a/src/Hosting/Hosting/src/GenericHost/GenericWebHostedService.cs +++ b/src/Hosting/Hosting/src/GenericHost/GenericWebHostedService.cs @@ -21,17 +21,17 @@ namespace Microsoft.AspNetCore.Hosting { internal sealed partial class GenericWebHostService : IHostedService { - public GenericWebHostService( - IOptions options, - IServer server, - ILoggerFactory loggerFactory, - DiagnosticListener diagnosticListener, - ActivitySource activitySource, - IHttpContextFactory httpContextFactory, - IApplicationBuilderFactory applicationBuilderFactory, - IEnumerable startupFilters, - IConfiguration configuration, - IWebHostEnvironment hostingEnvironment) + public GenericWebHostService(IOptions options, + IServer server, + ILoggerFactory loggerFactory, + DiagnosticListener diagnosticListener, + ActivitySource activitySource, + DistributedContextPropagator propagator, + IHttpContextFactory httpContextFactory, + IApplicationBuilderFactory applicationBuilderFactory, + IEnumerable startupFilters, + IConfiguration configuration, + IWebHostEnvironment hostingEnvironment) { Options = options.Value; Server = server; @@ -39,6 +39,7 @@ public GenericWebHostService( LifetimeLogger = loggerFactory.CreateLogger("Microsoft.Hosting.Lifetime"); DiagnosticListener = diagnosticListener; ActivitySource = activitySource; + Propagator = propagator; HttpContextFactory = httpContextFactory; ApplicationBuilderFactory = applicationBuilderFactory; StartupFilters = startupFilters; @@ -53,6 +54,7 @@ public GenericWebHostService( public ILogger LifetimeLogger { get; } public DiagnosticListener DiagnosticListener { get; } public ActivitySource ActivitySource { get; } + public DistributedContextPropagator Propagator { get; } public IHttpContextFactory HttpContextFactory { get; } public IApplicationBuilderFactory ApplicationBuilderFactory { get; } public IEnumerable StartupFilters { get; } @@ -116,7 +118,7 @@ public async Task StartAsync(CancellationToken cancellationToken) application = ErrorPageBuilder.BuildErrorPageApplication(HostingEnvironment.ContentRootFileProvider, Logger, showDetailedErrors, ex); } - var httpApplication = new HostingApplication(application, Logger, DiagnosticListener, ActivitySource, HttpContextFactory); + var httpApplication = new HostingApplication(application, Logger, DiagnosticListener, ActivitySource, Propagator, HttpContextFactory); await Server.StartAsync(httpApplication, cancellationToken); diff --git a/src/Hosting/Hosting/src/Internal/HostingApplication.cs b/src/Hosting/Hosting/src/Internal/HostingApplication.cs index 7e7e934511f2..e51b9d2495df 100644 --- a/src/Hosting/Hosting/src/Internal/HostingApplication.cs +++ b/src/Hosting/Hosting/src/Internal/HostingApplication.cs @@ -24,10 +24,11 @@ public HostingApplication( ILogger logger, DiagnosticListener diagnosticSource, ActivitySource activitySource, + DistributedContextPropagator propagator, IHttpContextFactory httpContextFactory) { _application = application; - _diagnostics = new HostingApplicationDiagnostics(logger, diagnosticSource, activitySource); + _diagnostics = new HostingApplicationDiagnostics(logger, diagnosticSource, activitySource, propagator); if (httpContextFactory is DefaultHttpContextFactory factory) { _defaultHttpContextFactory = factory; diff --git a/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs b/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs index 7d8a18db82db..a5148fda6902 100644 --- a/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs +++ b/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs @@ -7,12 +7,9 @@ using System.Diagnostics; using System.Globalization; using System.Runtime.CompilerServices; -using System.Web; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Primitives; -using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Hosting { @@ -31,13 +28,19 @@ internal class HostingApplicationDiagnostics private readonly ActivitySource _activitySource; private readonly DiagnosticListener _diagnosticListener; + private readonly DistributedContextPropagator _propagator; private readonly ILogger _logger; - public HostingApplicationDiagnostics(ILogger logger, DiagnosticListener diagnosticListener, ActivitySource activitySource) + public HostingApplicationDiagnostics( + ILogger logger, + DiagnosticListener diagnosticListener, + ActivitySource activitySource, + DistributedContextPropagator propagator) { _logger = logger; _diagnosticListener = diagnosticListener; _activitySource = activitySource; + _propagator = propagator; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -279,38 +282,37 @@ private static void RecordRequestStartEventLog(HttpContext httpContext) { return null; } - var headers = httpContext.Request.Headers; - var requestId = headers.TraceParent; - if (requestId.Count == 0) - { - requestId = headers.RequestId; - } - - if (!StringValues.IsNullOrEmpty(requestId)) + _propagator.ExtractTraceIdAndState(headers, + static (object? carrier, string fieldName, out string? fieldValue, out IEnumerable? fieldValues) => + { + fieldValues = default; + var headers = (IHeaderDictionary)carrier!; + fieldValue = headers[fieldName]; + }, + out var requestId, + out var traceState); + + if (!string.IsNullOrEmpty(requestId)) { activity.SetParentId(requestId); - var traceState = headers.TraceState; - if (traceState.Count > 0) + if (!string.IsNullOrEmpty(traceState)) { activity.TraceStateString = traceState; } - - // We expect baggage to be empty by default - // Only very advanced users will be using it in near future, we encourage them to keep baggage small (few items) - var baggage = headers.GetCommaSeparatedValues(HeaderNames.Baggage); - if (baggage.Length == 0) + var baggage = _propagator.ExtractBaggage(headers, static (object? carrier, string fieldName, out string? fieldValue, out IEnumerable? fieldValues) => { - baggage = headers.GetCommaSeparatedValues(HeaderNames.CorrelationContext); - } + fieldValues = default; + var headers = (IHeaderDictionary)carrier!; + fieldValue = headers[fieldName]; + }); - // AddBaggage adds items at the beginning of the list, so we need to add them in reverse to keep the same order as the client - // An order could be important if baggage has two items with the same key (that is allowed by the contract) - for (var i = baggage.Length - 1; i >= 0; i--) + // Order could be important if baggage has two items with the same key (that is allowed by the contract) + if (baggage is not null) { - if (NameValueHeaderValue.TryParse(baggage[i], out var baggageItem)) + foreach (var baggageItem in baggage) { - activity.AddBaggage(baggageItem.Name.ToString(), HttpUtility.UrlDecode(baggageItem.Value.ToString())); + activity.AddBaggage(baggageItem.Key, baggageItem.Value); } } } diff --git a/src/Hosting/Hosting/src/Internal/WebHost.cs b/src/Hosting/Hosting/src/Internal/WebHost.cs index 207f2b6ee20c..42552aa93373 100644 --- a/src/Hosting/Hosting/src/Internal/WebHost.cs +++ b/src/Hosting/Hosting/src/Internal/WebHost.cs @@ -157,8 +157,9 @@ public async Task StartAsync(CancellationToken cancellationToken = default) var diagnosticSource = _applicationServices.GetRequiredService(); var activitySource = _applicationServices.GetRequiredService(); + var propagator = _applicationServices.GetRequiredService(); var httpContextFactory = _applicationServices.GetRequiredService(); - var hostingApp = new HostingApplication(application, _logger, diagnosticSource, activitySource, httpContextFactory); + var hostingApp = new HostingApplication(application, _logger, diagnosticSource, activitySource, propagator, httpContextFactory); await Server.StartAsync(hostingApp, cancellationToken).ConfigureAwait(false); _startedServer = true; diff --git a/src/Hosting/Hosting/src/WebHostBuilder.cs b/src/Hosting/Hosting/src/WebHostBuilder.cs index 53c3c8863c87..5628110df244 100644 --- a/src/Hosting/Hosting/src/WebHostBuilder.cs +++ b/src/Hosting/Hosting/src/WebHostBuilder.cs @@ -293,6 +293,7 @@ private IServiceCollection BuildCommonServices(out AggregateException? hostingSt services.TryAddSingleton(sp => new DiagnosticListener("Microsoft.AspNetCore")); services.TryAddSingleton(sp => sp.GetRequiredService()); services.TryAddSingleton(sp => new ActivitySource("Microsoft.AspNetCore")); + services.TryAddSingleton(DistributedContextPropagator.Current); services.AddTransient(); services.AddTransient(); diff --git a/src/Hosting/Hosting/test/HostingApplicationDiagnosticsTests.cs b/src/Hosting/Hosting/test/HostingApplicationDiagnosticsTests.cs index 364d8a3e4af2..85ea78c2647b 100644 --- a/src/Hosting/Hosting/test/HostingApplicationDiagnosticsTests.cs +++ b/src/Hosting/Hosting/test/HostingApplicationDiagnosticsTests.cs @@ -397,7 +397,7 @@ public void ActivityBaggagePreservesItemsOrder() KeyValuePair.Create("Key1","value3") }; - Assert.Equal(expectedBaggage, Activity.Current.Baggage); + Assert.Equal(expectedBaggage, Activity.Current.Baggage.ToArray()); } [Fact] @@ -552,6 +552,7 @@ private static HostingApplication CreateApplication(out FeatureCollection featur logger ?? new NullScopeLogger(), diagnosticListener ?? new NoopDiagnosticListener(), activitySource ?? new ActivitySource("Microsoft.AspNetCore"), + DistributedContextPropagator.CreateDefaultPropagator(), httpContextFactory.Object); return hostingApplication; diff --git a/src/Hosting/Hosting/test/HostingApplicationTests.cs b/src/Hosting/Hosting/test/HostingApplicationTests.cs index 217d81e79613..7bafdf05b16a 100644 --- a/src/Hosting/Hosting/test/HostingApplicationTests.cs +++ b/src/Hosting/Hosting/test/HostingApplicationTests.cs @@ -196,6 +196,7 @@ private static HostingApplication CreateApplication(IHttpContextFactory httpCont NullLogger.Instance, new DiagnosticListener("Microsoft.AspNetCore"), activitySource ?? new ActivitySource("Microsoft.AspNetCore"), + DistributedContextPropagator.CreateDefaultPropagator(), httpContextFactory); return hostingApplication;