diff --git a/correlationIdMiddleware/correlationIdMiddleware/CorrelationContext.cs b/correlationIdMiddleware/correlationIdMiddleware/CorrelationContext.cs
index 1e57354..9c21791 100644
--- a/correlationIdMiddleware/correlationIdMiddleware/CorrelationContext.cs
+++ b/correlationIdMiddleware/correlationIdMiddleware/CorrelationContext.cs
@@ -1,14 +1,18 @@
-using Ardalis.GuardClauses;
+namespace Dfe.Academisation.CorrelationIdMiddleware;
-namespace Dfe.Academisation.CorrelationIdMiddleware;
-
-public record CorrelationContext() : ICorrelationContext
+///
+public class CorrelationContext : ICorrelationContext
{
- public string? CorrelationId { get; private set; }
- public void SetContext(string correlationId)
+ ///
+ public Guid CorrelationId { get; private set; }
+
+ ///
+ public void SetContext(Guid correlationId)
{
- this.CorrelationId = Guard.Against.NullOrWhiteSpace(correlationId);
+ if (correlationId == Guid.Empty)
+ {
+ throw new ArgumentException("Guid cannot be empty", nameof(correlationId));
+ }
+ this.CorrelationId = correlationId;
}
-
- public string HeaderKey { get => "x-correlation-id"; }
}
\ No newline at end of file
diff --git a/correlationIdMiddleware/correlationIdMiddleware/CorrelationIdMiddleware.cs b/correlationIdMiddleware/correlationIdMiddleware/CorrelationIdMiddleware.cs
index be9e04f..d51ccea 100644
--- a/correlationIdMiddleware/correlationIdMiddleware/CorrelationIdMiddleware.cs
+++ b/correlationIdMiddleware/correlationIdMiddleware/CorrelationIdMiddleware.cs
@@ -1,13 +1,11 @@
-using Ardalis.GuardClauses;
+namespace Dfe.Academisation.CorrelationIdMiddleware;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
-namespace Dfe.Academisation.CorrelationIdMiddleware;
-
///
/// Middleware that checks incoming requests for a correlation and causation id header. If not found then default values will be created.
/// Saves these values in the correlationContext instance. Be sure to register correlation context as scoped or the equivalent in you ioc container.
-/// Header used in requests is 'x-correlation-id'
+/// Header used in requests is 'x-correlationId'
///
public class CorrelationIdMiddleware
{
@@ -17,34 +15,43 @@ public class CorrelationIdMiddleware
public CorrelationIdMiddleware(RequestDelegate next, ILogger logger)
{
_next = next;
- _logger = Guard.Against.Null(logger);
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
// ReSharper disable once UnusedMember.Global
// Invoked by asp.net
public Task Invoke(HttpContext httpContext, ICorrelationContext correlationContext)
{
- string thisCorrelationId;
+ Guid thisCorrelationId;
// correlation id. An ID that spans many requests
- if (httpContext.Request.Headers.ContainsKey(correlationContext.HeaderKey) &&
- !string.IsNullOrWhiteSpace(httpContext.Request.Headers[correlationContext.HeaderKey]))
+ if (httpContext.Request.Headers.ContainsKey(Keys.HeaderKey)
+ && !string.IsNullOrWhiteSpace(httpContext.Request.Headers[Keys.HeaderKey]))
{
- thisCorrelationId = httpContext.Request.Headers[correlationContext.HeaderKey];
- _logger.LogInformation("CorrelationId detected from header: {correlationId}", thisCorrelationId);
+ if (!Guid.TryParse(httpContext.Request.Headers[Keys.HeaderKey], out thisCorrelationId))
+ {
+ thisCorrelationId = Guid.NewGuid();
+ this._logger.LogWarning("Detected header x-correlationId, but value cannot be parsed to a GUID. Other values are not supported. Generated a new one: {correlationId}", thisCorrelationId);
+ }
+ else
+ {
+ _logger.LogInformation("CorrelationIdMiddleware:Invoke - x-correlationId detected in request headers: {correlationId}", thisCorrelationId);
+ }
}
else
{
- thisCorrelationId = Guid.NewGuid().ToString();
- _logger.LogInformation("CorrelationId not detected from headers. Generated a new one: {correlationId}",
- thisCorrelationId);
+ thisCorrelationId = Guid.NewGuid();
+ _logger.LogWarning("CorrelationIdMiddleware:Invoke - x-correlationId not detected in request headers. Generated a new one: {correlationId}", thisCorrelationId);
}
- httpContext.Request.Headers[correlationContext.HeaderKey] = thisCorrelationId;
+ httpContext.Request.Headers[Keys.HeaderKey] = thisCorrelationId.ToString();
correlationContext.SetContext(thisCorrelationId);
- httpContext.Response.Headers[correlationContext.HeaderKey] = thisCorrelationId;
- return _next(httpContext);
+ httpContext.Response.Headers[Keys.HeaderKey] = thisCorrelationId.ToString();
+ using (_logger.BeginScope("x-correlationId {x-correlationId}", correlationContext.CorrelationId.ToString()))
+ {
+ return _next(httpContext);
+ }
}
}
\ No newline at end of file
diff --git a/correlationIdMiddleware/correlationIdMiddleware/Dfe.Academisation.CorrelationIdMiddleware.csproj b/correlationIdMiddleware/correlationIdMiddleware/Dfe.Academisation.CorrelationIdMiddleware.csproj
index 4a71630..15f4407 100644
--- a/correlationIdMiddleware/correlationIdMiddleware/Dfe.Academisation.CorrelationIdMiddleware.csproj
+++ b/correlationIdMiddleware/correlationIdMiddleware/Dfe.Academisation.CorrelationIdMiddleware.csproj
@@ -13,7 +13,7 @@
dfe;academisation;correlation;
4ac4e7ef-aaff-48a4-9e4d-44371c231191
README.md
- 1.0.0
+ 2.0.0
DFE-Digital
@@ -25,10 +25,8 @@
-
-
-
+
diff --git a/correlationIdMiddleware/correlationIdMiddleware/ICorrelationContext.cs b/correlationIdMiddleware/correlationIdMiddleware/ICorrelationContext.cs
index 31cf350..24da7b2 100644
--- a/correlationIdMiddleware/correlationIdMiddleware/ICorrelationContext.cs
+++ b/correlationIdMiddleware/correlationIdMiddleware/ICorrelationContext.cs
@@ -1,8 +1,19 @@
namespace Dfe.Academisation.CorrelationIdMiddleware;
+///
+/// Provides access to the current correlation id. You should register this as a scoped / per web request
+/// dependency in your IoC/DI container.
+///
public interface ICorrelationContext
{
- public string? CorrelationId { get; }
- public void SetContext(string correlationId);
- public string HeaderKey { get; }
+ ///
+ /// Returns the current correlation id if it has been set
+ ///
+ public Guid CorrelationId { get; }
+
+ ///
+ /// Used by the middleware to store the current correlation id. Do not call this method yourself.
+ ///
+ ///
+ public void SetContext(Guid correlationId);
}
\ No newline at end of file
diff --git a/correlationIdMiddleware/correlationIdMiddleware/Keys.cs b/correlationIdMiddleware/correlationIdMiddleware/Keys.cs
new file mode 100644
index 0000000..ddd16ad
--- /dev/null
+++ b/correlationIdMiddleware/correlationIdMiddleware/Keys.cs
@@ -0,0 +1,13 @@
+namespace Dfe.Academisation.CorrelationIdMiddleware;
+///
+/// The keys used by the correlation id middleware.
+///
+
+public class Keys
+{
+ ///
+ /// The header key use to detect incoming correlation ids, and to send them in responses.
+ /// Use this key if you are making subsequent requests so that correlation flows between services
+ ///
+ public const string HeaderKey = "x-correlationId";
+}
diff --git a/correlationIdMiddleware/correlationIdMiddleware/README.md b/correlationIdMiddleware/correlationIdMiddleware/README.md
index 828316c..8ba7019 100644
--- a/correlationIdMiddleware/correlationIdMiddleware/README.md
+++ b/correlationIdMiddleware/correlationIdMiddleware/README.md
@@ -2,6 +2,50 @@
## What does this do ?
+This package contains middleware that you can register in your AspNet application to enable detection of `x-correlationId` request headers that can then be propagated and use in logging, further requests, and responses.
+
## Why does it do it ?
-## How to use it
\ No newline at end of file
+CorrelationIds that are passed between services and recorded in all logs help to dramatically reduce the complexity involved in debugging applications, particularly where multiple services are involved.
+
+## How to use it
+
+* Reference the Nuget package.
+
+* In your application start-up, register the correlation context as a scoped dependency. For example
+`services.AddScoped();`
+
+* Add the CorrelationId middleware to your AspNet middleware pipeline. You should add this as early as possible so that correlationIds can be logged.
+`app.UseMiddleware();`
+
+* Anywhere that you need access to the current correlation id, inject `ICorrelationContext` and access the current context using the `CorrelationId` property. It will return a `string?` that you can use in subsequent requests or wherever you need it.
+
+---
+
+## Default AspNet Logger
+The middleware will add the current correlation id as scoped data before calling the next middleware in the AspNet middleware pipeline.
+If you are using the default AspNet logger, to see this in your log output you need to enable scopes in the output.
+One simple way of doing this is to use the console output by adding the following to your logger configuration.
+Alternatively use something like Seq to view logs locally.
+
+```json
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Information",
+ "Microsoft.Hosting.Lifetime": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ },
+ "Console": {
+ "FormatterName": "simple",
+ "FormatterOptions": {
+ "IncludeScopes": "true"
+ }
+ }
+ },
+```
+
+## Not using the default AspNet Logger ?
+The middleware will push an `x-correlationId` scope property onto the default Microsoft logging implementation.
+
+If you are using another logger, create a middleware and register it as the next one in the pipeline after the `CorrelationIdMiddleware`. Inject the `ICorrelationContext` into your middleware and use the `ICorrelationContext.CorrelationId` property to access the correlationId value and include it in your own logs. For example Serilog.