Skip to content

HeaderProgation middleware: logger scope #11023

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Microsoft.AspNetCore.Builder
public static partial class HeaderPropagationApplicationBuilderExtensions
{
public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseHeaderPropagation(this Microsoft.AspNetCore.Builder.IApplicationBuilder app) { throw null; }
public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseHeaderPropagation(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, bool includeInLoggerScope) { throw null; }
}
}
namespace Microsoft.AspNetCore.HeaderPropagation
Expand Down Expand Up @@ -34,6 +35,11 @@ public void Add(string headerName, System.Func<Microsoft.AspNetCore.HeaderPropag
public void Add(string inboundHeaderName, string outboundHeaderName) { }
public void Add(string inboundHeaderName, string outboundHeaderName, System.Func<Microsoft.AspNetCore.HeaderPropagation.HeaderPropagationContext, Microsoft.Extensions.Primitives.StringValues> valueFilter) { }
}
public partial class HeaderPropagationLoggerScopeMiddleware
{
public HeaderPropagationLoggerScopeMiddleware(Microsoft.AspNetCore.Http.RequestDelegate next, Microsoft.Extensions.Logging.ILogger<Microsoft.AspNetCore.HeaderPropagation.HeaderPropagationLoggerScopeMiddleware> logger, Microsoft.AspNetCore.HeaderPropagation.IHeaderPropagationLoggerScopeBuilder loggerScopeBuilder) { }
public System.Threading.Tasks.Task Invoke(Microsoft.AspNetCore.Http.HttpContext context) { throw null; }
}
public partial class HeaderPropagationMessageHandler : System.Net.Http.DelegatingHandler
{
public HeaderPropagationMessageHandler(Microsoft.AspNetCore.HeaderPropagation.HeaderPropagationMessageHandlerOptions options, Microsoft.AspNetCore.HeaderPropagation.HeaderPropagationValues values) { }
Expand Down Expand Up @@ -69,7 +75,9 @@ public HeaderPropagationOptions() { }
public partial class HeaderPropagationValues
{
public HeaderPropagationValues() { }
public System.Collections.Generic.IDictionary<string, Microsoft.Extensions.Primitives.StringValues> Headers { get { throw null; } set { } }
}
public partial interface IHeaderPropagationLoggerScopeBuilder
{
}
}
namespace Microsoft.Extensions.DependencyInjection
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
Expand All @@ -11,5 +11,6 @@
<Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
<Reference Include="Microsoft.AspNetCore.StaticFiles" />
<Reference Include="Microsoft.Extensions.Hosting" />
<Reference Include="Microsoft.Extensions.Logging.Console" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace HeaderPropagationSample
{
Expand All @@ -16,6 +17,8 @@ public static IHostBuilder CreateHostBuilder(string[] args) =>
{
webBuilder.UseKestrel();
webBuilder.UseStartup<Startup>();
});
})
.ConfigureLogging((hostingContext, logging) =>
logging.AddConsole(options => options.IncludeScopes = true));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHttpCli
app.UseDeveloperExceptionPage();
}

app.UseHeaderPropagation();
// Add the propagated headers Middleware
// and configure it to include the propagated headers to the logger scope.
app.UseHeaderPropagation(includeInLoggerScope: true);

app.UseRouting();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,23 @@ public static IApplicationBuilder UseHeaderPropagation(this IApplicationBuilder

return app.UseMiddleware<HeaderPropagationMiddleware>();
}

/// <summary>
/// Adds a middleware that collect headers to be propagated to a <see cref="HttpClient"/>.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder"/> to add the middleware to.</param>
/// <param name="includeInLoggerScope">Includes the captured headers in the logger scope.</param>
/// <returns>A reference to the <paramref name="app"/> after the operation has completed.</returns>
public static IApplicationBuilder UseHeaderPropagation(this IApplicationBuilder app, bool includeInLoggerScope)
{
app.UseHeaderPropagation();

if (includeInLoggerScope)
{
app.UseMiddleware<HeaderPropagationLoggerScopeMiddleware>();
}

return app;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public static IServiceCollection AddHeaderPropagation(this IServiceCollection se
}

services.TryAddSingleton<HeaderPropagationValues>();
services.TryAddSingleton<IHeaderPropagationLoggerScopeBuilder, HeaderPropagationLoggerScopeBuilder>();

return services;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Microsoft.Extensions.Primitives;

namespace Microsoft.AspNetCore.HeaderPropagation
{
internal class HeaderPropagationLoggerScope : IReadOnlyList<KeyValuePair<string, object>>
{
private readonly List<string> _headerNames;
private readonly IDictionary<string, StringValues> _headerValues;
private string _cachedToString;

public HeaderPropagationLoggerScope(List<string> headerNames, IDictionary<string, StringValues> headerValues)
{
_headerNames = headerNames ?? throw new ArgumentNullException(nameof(headerNames));
_headerValues = headerValues ?? throw new ArgumentNullException(nameof(headerValues));
}

public int Count => _headerNames.Count;

public KeyValuePair<string, object> this[int index]
{
get
{
var headerName = _headerNames[index];
_headerValues.TryGetValue(headerName, out var value);
return new KeyValuePair<string, object>(headerName, value);
}
}

public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
for (var i = 0; i < Count; i++)
{
yield return this[i];
}
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

public override string ToString()
{
if (_cachedToString == null)
{
var sb = new StringBuilder();

for (int i = 0; i < Count; i++)
{
if (i > 0) sb.Append(' ');

var headerName = _headerNames[i];
_headerValues.TryGetValue(headerName, out var value);

sb.Append(string.Format(
CultureInfo.InvariantCulture,
"{0}:{1}",
headerName, value.ToString()));
}

_cachedToString = sb.ToString();
}

return _cachedToString;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using Microsoft.Extensions.Options;

namespace Microsoft.AspNetCore.HeaderPropagation
{

/// <summary>
/// A builder to build the <see cref="HeaderPropagationLoggerScope"/> for the <see cref="HeaderPropagationMiddleware"/>.
/// </summary>
internal class HeaderPropagationLoggerScopeBuilder : IHeaderPropagationLoggerScopeBuilder
{
private readonly List<string> _headerNames = new List<string>();
private readonly HeaderPropagationValues _values;

/// <summary>
/// Creates a new instance of the <see cref="HeaderPropagationLoggerScopeBuilder"/>.
/// </summary>
/// <param name="options">The options that define which headers are propagated.</param>
/// <param name="values">The values of the headers to be propagated populated by the
/// <see cref="HeaderPropagationMiddleware"/>.</param>
public HeaderPropagationLoggerScopeBuilder(IOptions<HeaderPropagationOptions> options, HeaderPropagationValues values)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
var headers = options.Value.Headers;

_values = values ?? throw new ArgumentNullException(nameof(values));

var uniqueHeaderNames = new HashSet<string>();

// Perf: not using directly the HashSet so we can iterate without allocating an enumerator
// and avoiding foreach since we don't define a struct-enumerator.
for (var i = 0; i < headers.Count; i++)
{
var headerName = headers[i].CapturedHeaderName;
if (uniqueHeaderNames.Add(headerName))
{
_headerNames.Add(headerName);
}
}
}

/// <summary>
/// Build the <see cref="HeaderPropagationLoggerScope"/> for the current async context.
/// </summary>
HeaderPropagationLoggerScope IHeaderPropagationLoggerScopeBuilder.Build() =>
new HeaderPropagationLoggerScope(_headerNames, _values.Headers);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace Microsoft.AspNetCore.HeaderPropagation
{
public class HeaderPropagationLoggerScopeMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<HeaderPropagationLoggerScopeMiddleware> _logger;
private readonly IHeaderPropagationLoggerScopeBuilder _loggerScopeBuilder;

public HeaderPropagationLoggerScopeMiddleware(RequestDelegate next, ILogger<HeaderPropagationLoggerScopeMiddleware> logger, IHeaderPropagationLoggerScopeBuilder loggerScopeBuilder)
{
_next = next ?? throw new ArgumentNullException(nameof(next));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_loggerScopeBuilder = loggerScopeBuilder ?? throw new ArgumentNullException(nameof(loggerScopeBuilder));
}

public Task Invoke(HttpContext context)
{
using (_logger.BeginScope(_loggerScopeBuilder.Build()))
{
return _next.Invoke(context);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Primitives;

namespace Microsoft.AspNetCore.HeaderPropagation
Expand All @@ -23,7 +21,7 @@ public class HeaderPropagationValues
/// <remarks>
/// The keys of <see cref="Headers"/> correspond to <see cref="HeaderPropagationEntry.CapturedHeaderName"/>.
/// </remarks>
public IDictionary<string, StringValues> Headers
internal IDictionary<string, StringValues> Headers
{
get
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace Microsoft.AspNetCore.HeaderPropagation
{
/// <summary>
/// A builder to build the <see cref="HeaderPropagationLoggerScope"/> for the <see cref="HeaderPropagationMiddleware"/>.
/// </summary>
public interface IHeaderPropagationLoggerScopeBuilder
{
/// <summary>
/// Build the <see cref="HeaderPropagationLoggerScope"/> for the current async context.
/// </summary>
internal HeaderPropagationLoggerScope Build();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Description>ASP.NET Core middleware to propagate HTTP headers from the incoming request to the outgoing HTTP Client requests</Description>
Expand All @@ -9,10 +9,6 @@
<PackageTags>aspnetcore;httpclient</PackageTags>
</PropertyGroup>

<ItemGroup>
<InternalsVisibleTo Include="Microsoft.AspNetCore.HeaderPropagation.Tests" />
</ItemGroup>

<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Http" />
<Reference Include="Microsoft.Extensions.Http" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Microsoft.AspNetCore.HeaderPropagation.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
Loading