diff --git a/sample/SampleWebApp/SampleWebApp.csproj b/sample/SampleWebApp/SampleWebApp.csproj
index 1d6aebc..95007a8 100644
--- a/sample/SampleWebApp/SampleWebApp.csproj
+++ b/sample/SampleWebApp/SampleWebApp.csproj
@@ -8,9 +8,12 @@
-
+
+
+
+
diff --git a/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs b/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs
index 9083eda..a3219f1 100644
--- a/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs
+++ b/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs
@@ -1,9 +1,9 @@
-using System;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Primitives;
+using Microsoft.AspNetCore.Http;
using Serilog.Core;
using Serilog.Events;
+using Serilog.Preparers.CorrelationIds;
+#nullable enable
namespace Serilog.Enrichers;
///
@@ -11,9 +11,8 @@ public class CorrelationIdEnricher : ILogEventEnricher
{
private const string CorrelationIdItemKey = "Serilog_CorrelationId";
private const string PropertyName = "CorrelationId";
- private readonly bool _addValueIfHeaderAbsence;
private readonly IHttpContextAccessor _contextAccessor;
- private readonly string _headerKey;
+ private readonly CorrelationIdPreparerOptions _options;
///
/// Initializes a new instance of the class.
@@ -24,15 +23,22 @@ public class CorrelationIdEnricher : ILogEventEnricher
///
/// Determines whether to add a new correlation ID value if the header is absent.
///
- public CorrelationIdEnricher(string headerKey, bool addValueIfHeaderAbsence)
- : this(headerKey, addValueIfHeaderAbsence, new HttpContextAccessor())
+ public CorrelationIdEnricher(
+ string headerKey,
+ bool addValueIfHeaderAbsence)
+ : this(
+ headerKey,
+ addValueIfHeaderAbsence,
+ new HttpContextAccessor())
{
}
- internal CorrelationIdEnricher(string headerKey, bool addValueIfHeaderAbsence, IHttpContextAccessor contextAccessor)
+ internal CorrelationIdEnricher(
+ string headerKey,
+ bool addValueIfHeaderAbsence,
+ IHttpContextAccessor contextAccessor)
{
- _headerKey = headerKey;
- _addValueIfHeaderAbsence = addValueIfHeaderAbsence;
+ _options = new CorrelationIdPreparerOptions(addValueIfHeaderAbsence, headerKey);
_contextAccessor = contextAccessor;
}
@@ -42,7 +48,7 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
HttpContext httpContext = _contextAccessor.HttpContext;
if (httpContext == null) return;
- if (httpContext.Items.TryGetValue(CorrelationIdItemKey, out object value) &&
+ if (httpContext.Items.TryGetValue(CorrelationIdItemKey, out object? value) &&
value is LogEventProperty logEventProperty)
{
logEvent.AddPropertyIfAbsent(logEventProperty);
@@ -50,26 +56,16 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
// Ensure the string value is also available if not already stored
if (!httpContext.Items.ContainsKey(Constants.CorrelationIdValueKey))
{
- string correlationIdValue = ((ScalarValue)logEventProperty.Value).Value as string;
+ string? correlationIdValue = ((ScalarValue)logEventProperty.Value).Value as string;
httpContext.Items.Add(Constants.CorrelationIdValueKey, correlationIdValue);
}
return;
}
- StringValues requestHeader = httpContext.Request.Headers[_headerKey];
- StringValues responseHeader = httpContext.Response.Headers[_headerKey];
+ ICorrelationIdPreparer correlationIdPreparer = httpContext.GetCorrelationIdPreparer();
- string correlationId;
-
- if (!string.IsNullOrWhiteSpace(requestHeader))
- correlationId = requestHeader;
- else if (!string.IsNullOrWhiteSpace(responseHeader))
- correlationId = responseHeader;
- else if (_addValueIfHeaderAbsence)
- correlationId = Guid.NewGuid().ToString();
- else
- correlationId = null;
+ string? correlationId = correlationIdPreparer.PrepareCorrelationId(httpContext, _options);
LogEventProperty correlationIdProperty = new(PropertyName, new ScalarValue(correlationId));
logEvent.AddOrUpdateProperty(correlationIdProperty);
@@ -77,4 +73,5 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
httpContext.Items.Add(CorrelationIdItemKey, correlationIdProperty);
httpContext.Items.Add(Constants.CorrelationIdValueKey, correlationId);
}
-}
\ No newline at end of file
+}
+#nullable disable
\ No newline at end of file
diff --git a/src/Serilog.Enrichers.ClientInfo/Extensions/HttpContextExtensions.cs b/src/Serilog.Enrichers.ClientInfo/Extensions/HttpContextExtensions.cs
index babe1c9..16add24 100644
--- a/src/Serilog.Enrichers.ClientInfo/Extensions/HttpContextExtensions.cs
+++ b/src/Serilog.Enrichers.ClientInfo/Extensions/HttpContextExtensions.cs
@@ -1,4 +1,6 @@
using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Serilog.Preparers.CorrelationIds;
namespace Serilog.Enrichers;
@@ -14,4 +16,12 @@ public static class HttpContextExtensions
/// The correlation ID as a string, or null if not available.
public static string GetCorrelationId(this HttpContext httpContext)
=> httpContext?.Items[Constants.CorrelationIdValueKey] as string;
+
+ ///
+ /// Retrieves the correlation ID preparer for processing the current HTTP context.
+ ///
+ /// The HTTP context.
+ /// Correlation ID preparer.
+ internal static ICorrelationIdPreparer GetCorrelationIdPreparer(this HttpContext httpContext)
+ => httpContext.RequestServices.GetService() ?? new CorrelationIdPreparer();
}
\ No newline at end of file
diff --git a/src/Serilog.Enrichers.ClientInfo/Preparers/CorrelationIds/CorrelationIdPreparer.cs b/src/Serilog.Enrichers.ClientInfo/Preparers/CorrelationIds/CorrelationIdPreparer.cs
new file mode 100644
index 0000000..6adbbab
--- /dev/null
+++ b/src/Serilog.Enrichers.ClientInfo/Preparers/CorrelationIds/CorrelationIdPreparer.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Threading;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Primitives;
+
+#nullable enable
+namespace Serilog.Preparers.CorrelationIds
+{
+ internal class CorrelationIdPreparer : ICorrelationIdPreparer
+ {
+ protected AsyncLocal CorrelationId { get; } = new AsyncLocal();
+
+ ///
+ public string? PrepareCorrelationId(
+ HttpContext httpContext,
+ CorrelationIdPreparerOptions correlationIdPreparerOptions)
+ {
+ if (string.IsNullOrEmpty(CorrelationId.Value))
+ {
+ CorrelationId.Value = PrepareValueForCorrelationId(httpContext, correlationIdPreparerOptions);
+ }
+
+ return CorrelationId.Value;
+ }
+
+ protected string? PrepareValueForCorrelationId(
+ HttpContext httpContext,
+ CorrelationIdPreparerOptions correlationIdPreparerOptions)
+ {
+ StringValues requestHeader = httpContext.Request.Headers[correlationIdPreparerOptions.HeaderKey];
+
+ if (!string.IsNullOrWhiteSpace(requestHeader))
+ {
+ return requestHeader;
+ }
+
+ StringValues responseHeader = httpContext.Response.Headers[correlationIdPreparerOptions.HeaderKey];
+
+ if (!string.IsNullOrWhiteSpace(responseHeader))
+ {
+ return responseHeader;
+ }
+
+ if (correlationIdPreparerOptions.AddValueIfHeaderAbsence)
+ {
+ return Guid.NewGuid().ToString();
+ }
+
+ return null;
+ }
+ }
+}
+#nullable disable
\ No newline at end of file
diff --git a/src/Serilog.Enrichers.ClientInfo/Preparers/CorrelationIds/CorrelationIdPreparerOptions.cs b/src/Serilog.Enrichers.ClientInfo/Preparers/CorrelationIds/CorrelationIdPreparerOptions.cs
new file mode 100644
index 0000000..dbc688f
--- /dev/null
+++ b/src/Serilog.Enrichers.ClientInfo/Preparers/CorrelationIds/CorrelationIdPreparerOptions.cs
@@ -0,0 +1,31 @@
+namespace Serilog.Preparers.CorrelationIds
+{
+ ///
+ /// Settings for .
+ ///
+ public class CorrelationIdPreparerOptions
+ {
+ ///
+ /// Determines whether to add a new correlation ID value if the header is absent.
+ ///
+ public bool AddValueIfHeaderAbsence { get; }
+
+ ///
+ /// The header key used to retrieve the correlation ID from the HTTP request or response headers.
+ ///
+ public string HeaderKey { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Determines whether to add a new correlation ID value if the header is absent.
+ /// The header key used to retrieve the correlation ID from the HTTP request or response headers.
+ public CorrelationIdPreparerOptions(
+ bool addValueIfHeaderAbsence,
+ string headerKey)
+ {
+ AddValueIfHeaderAbsence = addValueIfHeaderAbsence;
+ HeaderKey = headerKey;
+ }
+ }
+}
diff --git a/src/Serilog.Enrichers.ClientInfo/Preparers/CorrelationIds/ICorrelationIdPreparer.cs b/src/Serilog.Enrichers.ClientInfo/Preparers/CorrelationIds/ICorrelationIdPreparer.cs
new file mode 100644
index 0000000..0efde0b
--- /dev/null
+++ b/src/Serilog.Enrichers.ClientInfo/Preparers/CorrelationIds/ICorrelationIdPreparer.cs
@@ -0,0 +1,22 @@
+using Microsoft.AspNetCore.Http;
+
+#nullable enable
+namespace Serilog.Preparers.CorrelationIds
+{
+ ///
+ /// Preparer for correlation ID.
+ ///
+ public interface ICorrelationIdPreparer
+ {
+ ///
+ /// Prepares the correlation ID.
+ ///
+ /// The HTTP context.
+ /// Options for preparation.
+ /// The correlation ID.
+ string? PrepareCorrelationId(
+ HttpContext httpContext,
+ CorrelationIdPreparerOptions correlationIdPreparerOptions);
+ }
+}
+#nullable disable
\ No newline at end of file
diff --git a/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs b/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs
index 77021e0..9f1b11f 100644
--- a/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs
+++ b/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs
@@ -1,8 +1,11 @@
-using Microsoft.AspNetCore.Http;
+using System;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
+using NSubstitute.ReturnsExtensions;
using Serilog.Core;
using Serilog.Events;
-using System;
+using Serilog.Preparers.CorrelationIds;
using Xunit;
namespace Serilog.Enrichers.ClientInfo.Tests;
@@ -15,18 +18,44 @@ public class CorrelationIdEnricherTests
public CorrelationIdEnricherTests()
{
- DefaultHttpContext httpContext = new();
+ IServiceProvider serviceProvider = Substitute.For();
+ serviceProvider.GetService().ReturnsNull();
+
+ DefaultHttpContext httpContext = new DefaultHttpContext
+ {
+ RequestServices = serviceProvider
+ };
_contextAccessor = Substitute.For();
_contextAccessor.HttpContext.Returns(httpContext);
}
[Fact]
- public void EnrichLogWithCorrelationId_WhenHttpRequestContainCorrelationHeader_ShouldCreateCorrelationIdProperty()
+ public void EnrichLogWithCorrelationId_WhenRequestServicesContainsICorrelationIdPreparer_ShouldUseCorrelationIdPreparerFromRequestServices()
{
// Arrange
string correlationId = Guid.NewGuid().ToString();
- _contextAccessor.HttpContext!.Request!.Headers[HeaderKey] = correlationId;
- CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor);
+
+ ICorrelationIdPreparer correlationIdPreparer = Substitute.For();
+ IServiceProvider serviceProvider = Substitute.For();
+
+ DefaultHttpContext httpContext = new DefaultHttpContext
+ {
+ RequestServices = serviceProvider
+ };
+ CorrelationIdPreparerOptions correlationIdPreparerOptions = new CorrelationIdPreparerOptions(false, HeaderKey);
+
+ correlationIdPreparer.PrepareCorrelationId(
+ httpContext,
+ Arg.Is(x =>
+ x.AddValueIfHeaderAbsence == correlationIdPreparerOptions.AddValueIfHeaderAbsence &&
+ x.HeaderKey == correlationIdPreparerOptions.HeaderKey))
+ .Returns(correlationId);
+
+ serviceProvider.GetService().Returns(correlationIdPreparer);
+ IHttpContextAccessor contextAccessor = Substitute.For();
+ contextAccessor.HttpContext.Returns(httpContext);
+
+ CorrelationIdEnricher correlationIdEnricher = new(correlationIdPreparerOptions.HeaderKey, correlationIdPreparerOptions.AddValueIfHeaderAbsence, contextAccessor);
LogEvent evt = null;
Logger log = new LoggerConfiguration()
@@ -44,8 +73,7 @@ public void EnrichLogWithCorrelationId_WhenHttpRequestContainCorrelationHeader_S
}
[Fact]
- public void
- EnrichLogWithCorrelationId_WhenHttpRequestContainCorrelationHeader_ShouldCreateCorrelationIdPropertyHasValue()
+ public void EnrichLogWithCorrelationId_WhenHttpRequestContainCorrelationHeader_ShouldCreateCorrelationIdProperty()
{
// Arrange
string correlationId = Guid.NewGuid().ToString();