Skip to content
This repository has been archived by the owner on Jun 17, 2020. It is now read-only.

Correlationid middleware #309

Merged
merged 7 commits into from Apr 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
544 changes: 288 additions & 256 deletions SourceCode.Clay.sln

Large diffs are not rendered by default.

@@ -0,0 +1,39 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using MvcCorrelationCustomSample.Services;
using SourceCode.Clay.AspNetCore.Middleware.Correlation;

namespace MvcCorrelationCustomSample.Controllers
{
[Route("api/[controller]")]
public class ValuesController : Controller
{
private readonly ICorrelationContextAccessor _accessor;
private readonly ScopedService _scoped;
private readonly TransientService _transient;
private readonly SingletonService _singleton;

public ValuesController(ScopedService scoped, TransientService transient, SingletonService singleton, ICorrelationContextAccessor accessor)
{
_accessor = accessor;
_scoped = scoped;
_transient = transient;
_singleton = singleton;
}

// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
// Note that all of these will have the same value.
return new[]
{
$"accessor={_accessor.CorrelationContext.CorrelationId}",
$"transient={_transient.GetCorrelationId}",
$"scoped={_scoped.GetCorrelationId}",
$"singleton={_singleton.GetCorrelationId}",
$"traceIdentifier={HttpContext.TraceIdentifier}"
};
}
}
}
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<RuntimeIdentifiers>any</RuntimeIdentifiers>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\SourceCode.Clay.AspNetCore.Middleware.Correlation\SourceCode.Clay.AspNetCore.Middleware.Correlation.csproj" />
</ItemGroup>

</Project>
@@ -0,0 +1,18 @@
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;

namespace MvcCorrelationCustomSample
{
public sealed class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}

public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
@@ -0,0 +1,16 @@
using SourceCode.Clay.AspNetCore.Middleware.Correlation;

namespace MvcCorrelationCustomSample.Services
{
public class ScopedService
{
private readonly ICorrelationContextAccessor _correlationContextAccessor;

public ScopedService(ICorrelationContextAccessor correlationContextAccessor)
{
_correlationContextAccessor = correlationContextAccessor;
}

public string GetCorrelationId => _correlationContextAccessor.CorrelationContext.CorrelationId;
}
}
@@ -0,0 +1,16 @@
using SourceCode.Clay.AspNetCore.Middleware.Correlation;

namespace MvcCorrelationCustomSample.Services
{
public class SingletonService
{
private readonly ICorrelationContextAccessor _correlationContextAccessor;

public SingletonService(ICorrelationContextAccessor correlationContextAccessor)
{
_correlationContextAccessor = correlationContextAccessor;
}

public string GetCorrelationId => _correlationContextAccessor.CorrelationContext.CorrelationId;
}
}
@@ -0,0 +1,16 @@
using SourceCode.Clay.AspNetCore.Middleware.Correlation;

namespace MvcCorrelationCustomSample.Services
{
public class TransientService
{
private readonly ICorrelationContextAccessor _correlationContextAccessor;

public TransientService(ICorrelationContextAccessor correlationContextAccessor)
{
_correlationContextAccessor = correlationContextAccessor;
}

public string GetCorrelationId => _correlationContextAccessor.CorrelationContext.CorrelationId;
}
}
@@ -0,0 +1,39 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using MvcCorrelationCustomSample.Services;
using SourceCode.Clay.AspNetCore.Middleware.Correlation;

namespace MvcCorrelationCustomSample
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();

// Enable the dependency injection of a ICorrelationContextAccessor instance into
// controllers and services.
services.AddCorrelationId();

// Add services that gets the correlation context injected into there constructor.
services
.AddScoped<ScopedService>()
.AddTransient<TransientService>()
.AddSingleton<SingletonService>();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

// Enable incoming, generation when missing and outgoing correlation ids.
app.UseCorrelationId();

app.UseMvc();
}
}
}
@@ -0,0 +1,15 @@
{
"Logging": {
"IncludeScopes": false,
"Debug": {
"LogLevel": {
"Default": "Warning"
}
},
"Console": {
"LogLevel": {
"Default": "Warning"
}
}
}
}
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<RuntimeIdentifiers>any</RuntimeIdentifiers>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\SourceCode.Clay.AspNetCore.Middleware.Correlation\SourceCode.Clay.AspNetCore.Middleware.Correlation.csproj" />
</ItemGroup>

</Project>
@@ -0,0 +1,18 @@
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;

namespace MvcCorrelationMinimalSample
{
public sealed class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}

public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
@@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using SourceCode.Clay.AspNetCore.Middleware.Correlation;

namespace MvcCorrelationMinimalSample
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCorrelationId();

app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello world!")
.ConfigureAwait(false);
});
}
}
}
@@ -0,0 +1,65 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Options;

namespace SourceCode.Clay.AspNetCore.Middleware.Correlation
{
/// <summary>
/// Holds <see cref="CorrelationMiddleware"/> related extension methods.
/// </summary>
public static class CorrelationApplicationBuilderExtensions
{
/// <summary>
/// Adds <see cref="CorrelationMiddleware"/> to ASP.NET pipeline.
/// </summary>
/// <param name="app">The current <see cref="IApplicationBuilder"/>.</param>
/// <returns>The original <paramref name="app"/> for chaining purposes.</returns>
public static IApplicationBuilder UseCorrelationId(this IApplicationBuilder app)
{
if (app == null)
throw new ArgumentNullException(nameof(app));

return UseMiddleware(app, new CorrelationOptions());
}

/// <summary>
/// Adds <see cref="CorrelationMiddleware"/> to ASP.NET pipeline.
/// </summary>
/// <param name="app">The current <see cref="IApplicationBuilder"/>.</param>
/// <param name="options">The <see cref="CorrelationOptions"/> that control the middleware's behaviour.</param>
/// <returns>The original <paramref name="app"/> for chaining purposes.</returns>
public static IApplicationBuilder UseCorrelationId(this IApplicationBuilder app, CorrelationOptions options)
{
if (app == null)
throw new ArgumentNullException(nameof(app));
if (options == null)
throw new ArgumentNullException(nameof(options));

return UseMiddleware(app, options);
}

/// <summary>
/// Adds <see cref="CorrelationMiddleware"/> to ASP.NET pipeline.
/// </summary>
/// <param name="app">The current <see cref="IApplicationBuilder"/>.</param>
/// <param name="configure">An <see cref="System.Action{T}"/> used to configure the <see cref="CorrelationOptions"/>.</param>
/// <returns>The original <paramref name="app"/> targeted for chaining purposes.</returns>
public static IApplicationBuilder UseCorrelationId(this IApplicationBuilder app, Action<CorrelationOptions> configure)
{
if (app == null)
throw new ArgumentNullException(nameof(app));
if (configure == null)
throw new ArgumentNullException(nameof(configure));

var options = new CorrelationOptions();
configure.Invoke(options);

return UseMiddleware(app, options);
}

private static IApplicationBuilder UseMiddleware(IApplicationBuilder app, CorrelationOptions options)
{
return app.UseMiddleware<CorrelationMiddleware>(Options.Create(options));
}
}
}
@@ -0,0 +1,33 @@
using System;
using static SourceCode.Clay.AspNetCore.Middleware.Correlation.Properties.Resources;

namespace SourceCode.Clay.AspNetCore.Middleware.Correlation
{
/// <summary>
/// Provides access to per request correlation properties.
/// </summary>
public class CorrelationContext
{
internal CorrelationContext(string correlationId, string header)
{
if (string.IsNullOrWhiteSpace(correlationId))
throw new ArgumentException(ArgumentNullOrWhitespace, nameof(correlationId));

if (string.IsNullOrWhiteSpace(header))
throw new ArgumentException(ArgumentNullOrWhitespace, nameof(header));

CorrelationId = correlationId;
Header = header;
}

/// <summary>
/// The Correlation ID which is applicable to the current request.
/// </summary>
public string CorrelationId { get; }

/// <summary>
/// The name of the header from which the Correlation ID is read/written.
/// </summary>
public string Header { get; }
}
}
@@ -0,0 +1,17 @@
using System.Threading;

namespace SourceCode.Clay.AspNetCore.Middleware.Correlation
{
/// <inheritdoc />
public class CorrelationContextAccessor : ICorrelationContextAccessor
{
private static AsyncLocal<CorrelationContext> s_correlationContext = new AsyncLocal<CorrelationContext>();

/// <inheritdoc />
public CorrelationContext CorrelationContext
{
get => s_correlationContext.Value;
set => s_correlationContext.Value = value;
}
}
}