From 0583198fb294a4b92b2d93bcc9e6758ff997674c Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Sun, 19 Oct 2025 10:10:34 +0400 Subject: [PATCH 1/8] refactor(#61): Extraction of the logic for CorrelationId preparation into a separate method. --- .../Enrichers/CorrelationIdEnricher.cs | 47 ++++++++++++++----- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs b/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs index 9083eda..b5e8b28 100644 --- a/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs +++ b/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs @@ -57,19 +57,7 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) return; } - StringValues requestHeader = httpContext.Request.Headers[_headerKey]; - StringValues responseHeader = httpContext.Response.Headers[_headerKey]; - - 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 = PrepareCorrelationId(httpContext); LogEventProperty correlationIdProperty = new(PropertyName, new ScalarValue(correlationId)); logEvent.AddOrUpdateProperty(correlationIdProperty); @@ -77,4 +65,37 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) httpContext.Items.Add(CorrelationIdItemKey, correlationIdProperty); httpContext.Items.Add(Constants.CorrelationIdValueKey, correlationId); } + + private string PrepareCorrelationId(HttpContext httpContext) + { + StringValues requestHeader = httpContext.Request.Headers[_headerKey]; + string returnValue; + + if (!string.IsNullOrWhiteSpace(requestHeader)) + { + returnValue = requestHeader; + } + else + { + StringValues responseHeader = httpContext.Response.Headers[_headerKey]; + + if (!string.IsNullOrWhiteSpace(responseHeader)) + { + returnValue = responseHeader; + } + else + { + if (_addValueIfHeaderAbsence) + { + returnValue = Guid.NewGuid().ToString(); + } + else + { + returnValue = null; + } + } + } + + return returnValue; + } } \ No newline at end of file From 0f7948d0d65702009e7a85c7228e7cc43af8dc02 Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Sun, 19 Oct 2025 13:09:51 +0400 Subject: [PATCH 2/8] refactor(#61): Removing copy-pasted tests. --- .../CorrelationIdEnricherTests.cs | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs b/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs index 77021e0..ee6ee4b 100644 --- a/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs +++ b/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs @@ -43,30 +43,6 @@ public void EnrichLogWithCorrelationId_WhenHttpRequestContainCorrelationHeader_S Assert.Equal(correlationId, evt.Properties[LogPropertyName].LiteralValue().ToString()); } - [Fact] - public void - EnrichLogWithCorrelationId_WhenHttpRequestContainCorrelationHeader_ShouldCreateCorrelationIdPropertyHasValue() - { - // Arrange - string correlationId = Guid.NewGuid().ToString(); - _contextAccessor.HttpContext!.Request!.Headers[HeaderKey] = correlationId; - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor); - - LogEvent evt = null; - Logger log = new LoggerConfiguration() - .Enrich.With(correlationIdEnricher) - .WriteTo.Sink(new DelegatingSink(e => evt = e)) - .CreateLogger(); - - // Act - log.Information("Has a correlation id."); - - // Assert - Assert.NotNull(evt); - Assert.True(evt.Properties.ContainsKey(LogPropertyName)); - Assert.Equal(correlationId, evt.Properties[LogPropertyName].LiteralValue().ToString()); - } - [Fact] public void EnrichLogWithCorrelationId_WhenHttpRequestNotContainCorrelationHeaderAndAddDefaultValueIsFalse_ShouldCreateCorrelationIdPropertyWithNoValue() From d9244d6bfb4b4dd68e76366b9a7ad13cd51c0551 Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Sun, 19 Oct 2025 13:52:49 +0400 Subject: [PATCH 3/8] feat(#61): Add generated CorrelationId to response. --- sample/SampleWebApp/SampleWebApp.csproj | 5 ++- sample/SampleWebApp/appsettings.json | 3 +- .../Enrichers/CorrelationIdEnricher.cs | 33 +++++++++++++++++-- ...ClientInfoLoggerConfigurationExtensions.cs | 8 +++-- .../CorrelationIdEnricherTests.cs | 32 ++++++++++++------ 5 files changed, 64 insertions(+), 17 deletions(-) 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/sample/SampleWebApp/appsettings.json b/sample/SampleWebApp/appsettings.json index c21e1ca..09b7466 100644 --- a/sample/SampleWebApp/appsettings.json +++ b/sample/SampleWebApp/appsettings.json @@ -31,7 +31,8 @@ { "Name": "WithCorrelationId", "Args": { - "addValueIfHeaderAbsence": true + "addValueIfHeaderAbsence": true, + "addCorrelationIdToResponse": true } }, { diff --git a/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs b/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs index b5e8b28..431de7b 100644 --- a/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs +++ b/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs @@ -11,6 +11,7 @@ public class CorrelationIdEnricher : ILogEventEnricher { private const string CorrelationIdItemKey = "Serilog_CorrelationId"; private const string PropertyName = "CorrelationId"; + private readonly bool _addCorrelationIdToResponse; private readonly bool _addValueIfHeaderAbsence; private readonly IHttpContextAccessor _contextAccessor; private readonly string _headerKey; @@ -24,15 +25,30 @@ 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()) + /// + /// Determines whether to add correlation ID value to header collection. + /// + public CorrelationIdEnricher( + string headerKey, + bool addValueIfHeaderAbsence, + bool addCorrelationIdToResponse) + : this( + headerKey, + addValueIfHeaderAbsence, + addCorrelationIdToResponse, + new HttpContextAccessor()) { } - internal CorrelationIdEnricher(string headerKey, bool addValueIfHeaderAbsence, IHttpContextAccessor contextAccessor) + internal CorrelationIdEnricher( + string headerKey, + bool addValueIfHeaderAbsence, + bool addCorrelationIdToResponse, + IHttpContextAccessor contextAccessor) { _headerKey = headerKey; _addValueIfHeaderAbsence = addValueIfHeaderAbsence; + _addCorrelationIdToResponse = addCorrelationIdToResponse; _contextAccessor = contextAccessor; } @@ -59,6 +75,8 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) string correlationId = PrepareCorrelationId(httpContext); + AddCorrelationIdToResponse(httpContext, correlationId); + LogEventProperty correlationIdProperty = new(PropertyName, new ScalarValue(correlationId)); logEvent.AddOrUpdateProperty(correlationIdProperty); @@ -66,6 +84,15 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) httpContext.Items.Add(Constants.CorrelationIdValueKey, correlationId); } + private void AddCorrelationIdToResponse(HttpContext httpContext, in string correlationId) + { + if (_addCorrelationIdToResponse + && !httpContext.Response.Headers.ContainsKey(_headerKey)) + { + httpContext.Response.Headers[_headerKey] = correlationId; + } + } + private string PrepareCorrelationId(HttpContext httpContext) { StringValues requestHeader = httpContext.Request.Headers[_headerKey]; diff --git a/src/Serilog.Enrichers.ClientInfo/Extensions/ClientInfoLoggerConfigurationExtensions.cs b/src/Serilog.Enrichers.ClientInfo/Extensions/ClientInfoLoggerConfigurationExtensions.cs index e943688..bbcf8d2 100644 --- a/src/Serilog.Enrichers.ClientInfo/Extensions/ClientInfoLoggerConfigurationExtensions.cs +++ b/src/Serilog.Enrichers.ClientInfo/Extensions/ClientInfoLoggerConfigurationExtensions.cs @@ -56,16 +56,20 @@ public static LoggerConfiguration WithClientIp( /// Add generated correlation id value if correlation id header not available in /// header collection. /// + /// + /// Add correlation id value to header collection. + /// /// enrichmentConfiguration /// The logger configuration so that multiple calls can be chained. public static LoggerConfiguration WithCorrelationId( this LoggerEnrichmentConfiguration enrichmentConfiguration, string headerName = "x-correlation-id", - bool addValueIfHeaderAbsence = false) + bool addValueIfHeaderAbsence = false, + bool addCorrelationIdToResponse = false) { ArgumentNullException.ThrowIfNull(enrichmentConfiguration, nameof(enrichmentConfiguration)); - return enrichmentConfiguration.With(new CorrelationIdEnricher(headerName, addValueIfHeaderAbsence)); + return enrichmentConfiguration.With(new CorrelationIdEnricher(headerName, addValueIfHeaderAbsence, addCorrelationIdToResponse)); } /// diff --git a/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs b/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs index ee6ee4b..76b48c5 100644 --- a/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs +++ b/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs @@ -26,7 +26,7 @@ public void EnrichLogWithCorrelationId_WhenHttpRequestContainCorrelationHeader_S // Arrange string correlationId = Guid.NewGuid().ToString(); _contextAccessor.HttpContext!.Request!.Headers[HeaderKey] = correlationId; - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, true, _contextAccessor); LogEvent evt = null; Logger log = new LoggerConfiguration() @@ -41,6 +41,7 @@ public void EnrichLogWithCorrelationId_WhenHttpRequestContainCorrelationHeader_S Assert.NotNull(evt); Assert.True(evt.Properties.ContainsKey(LogPropertyName)); Assert.Equal(correlationId, evt.Properties[LogPropertyName].LiteralValue().ToString()); + Assert.Equal(correlationId, _contextAccessor.HttpContext!.Response.Headers[HeaderKey]); } [Fact] @@ -48,7 +49,7 @@ public void EnrichLogWithCorrelationId_WhenHttpRequestNotContainCorrelationHeaderAndAddDefaultValueIsFalse_ShouldCreateCorrelationIdPropertyWithNoValue() { // Arrange - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, false, _contextAccessor); LogEvent evt = null; Logger log = new LoggerConfiguration() @@ -63,6 +64,7 @@ public void Assert.NotNull(evt); Assert.True(evt.Properties.ContainsKey(LogPropertyName)); Assert.Null(evt.Properties[LogPropertyName].LiteralValue()); + Assert.False(_contextAccessor.HttpContext!.Response.Headers.ContainsKey(HeaderKey)); } [Fact] @@ -70,7 +72,7 @@ public void EnrichLogWithCorrelationId_WhenHttpRequestNotContainCorrelationHeaderAndAddDefaultValueIsTrue_ShouldCreateCorrelationIdPropertyHasValue() { // Arrange - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, true, _contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, true, true, _contextAccessor); LogEvent evt = null; Logger log = new LoggerConfiguration() @@ -85,6 +87,8 @@ public void Assert.NotNull(evt); Assert.True(evt.Properties.ContainsKey(LogPropertyName)); Assert.NotNull(evt.Properties[LogPropertyName].LiteralValue().ToString()); + Assert.NotNull(_contextAccessor.HttpContext!.Response.Headers[HeaderKey]); + Assert.Equal(evt.Properties[LogPropertyName].LiteralValue(), _contextAccessor.HttpContext!.Response.Headers[HeaderKey].ToString()); } [Fact] @@ -94,7 +98,7 @@ public void // Arrange string correlationId = Guid.NewGuid().ToString(); _contextAccessor.HttpContext!.Request!.Headers[HeaderKey] = correlationId; - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, true, _contextAccessor); LogEvent evt = null; Logger log = new LoggerConfiguration() @@ -109,6 +113,7 @@ public void Assert.NotNull(evt); Assert.True(evt.Properties.ContainsKey(LogPropertyName)); Assert.Equal(correlationId, evt.Properties[LogPropertyName].LiteralValue().ToString()); + Assert.Equal(correlationId, _contextAccessor.HttpContext!.Response.Headers[HeaderKey]); } [Fact] @@ -120,7 +125,7 @@ public void string responseCorrelationId = Guid.NewGuid().ToString(); _contextAccessor.HttpContext!.Request!.Headers[HeaderKey] = requestCorrelationId; _contextAccessor.HttpContext!.Response!.Headers[HeaderKey] = responseCorrelationId; - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, true, _contextAccessor); LogEvent evt = null; Logger log = new LoggerConfiguration() @@ -135,6 +140,7 @@ public void Assert.NotNull(evt); Assert.True(evt.Properties.ContainsKey(LogPropertyName)); Assert.Equal(requestCorrelationId, evt.Properties[LogPropertyName].LiteralValue().ToString()); + Assert.Equal(responseCorrelationId, _contextAccessor.HttpContext!.Response.Headers[HeaderKey]); } [Fact] @@ -143,7 +149,7 @@ public void GetCorrelationId_WhenHttpRequestContainCorrelationHeader_ShouldRetur // Arrange string correlationId = Guid.NewGuid().ToString(); _contextAccessor.HttpContext!.Request!.Headers[HeaderKey] = correlationId; - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, true, _contextAccessor); LogEvent evt = null; Logger log = new LoggerConfiguration() @@ -158,6 +164,7 @@ public void GetCorrelationId_WhenHttpRequestContainCorrelationHeader_ShouldRetur // Assert Assert.NotNull(evt); Assert.Equal(correlationId, retrievedCorrelationId); + Assert.Equal(correlationId, _contextAccessor.HttpContext!.Response.Headers[HeaderKey]); } [Fact] @@ -165,7 +172,7 @@ public void GetCorrelationId_WhenHttpRequestNotContainCorrelationHeaderAndAddDefaultValueIsTrue_ShouldReturnGeneratedCorrelationIdFromHttpContext() { // Arrange - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, true, _contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, true, true, _contextAccessor); LogEvent evt = null; Logger log = new LoggerConfiguration() @@ -176,11 +183,13 @@ public void // Act log.Information("Has a correlation id."); string retrievedCorrelationId = _contextAccessor.HttpContext!.GetCorrelationId(); + string correlationIdFromResponse = _contextAccessor.HttpContext!.Response.Headers[HeaderKey]; // Assert Assert.NotNull(evt); Assert.NotNull(retrievedCorrelationId); Assert.NotEmpty(retrievedCorrelationId); + Assert.Equal(retrievedCorrelationId, correlationIdFromResponse); // Verify it's a valid GUID format Assert.True(Guid.TryParse(retrievedCorrelationId, out _)); } @@ -190,7 +199,7 @@ public void GetCorrelationId_WhenHttpRequestNotContainCorrelationHeaderAndAddDefaultValueIsFalse_ShouldReturnNullFromHttpContext() { // Arrange - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, false, _contextAccessor); LogEvent evt = null; Logger log = new LoggerConfiguration() @@ -205,6 +214,7 @@ public void // Assert Assert.NotNull(evt); Assert.Null(retrievedCorrelationId); + Assert.False(_contextAccessor.HttpContext!.Response.Headers.ContainsKey(HeaderKey)); } [Fact] @@ -213,7 +223,7 @@ public void GetCorrelationId_WhenCalledMultipleTimes_ShouldReturnSameCorrelation // Arrange string correlationId = Guid.NewGuid().ToString(); _contextAccessor.HttpContext!.Request!.Headers[HeaderKey] = correlationId; - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, false, _contextAccessor); Logger log = new LoggerConfiguration() .Enrich.With(correlationIdEnricher) @@ -231,6 +241,7 @@ public void GetCorrelationId_WhenCalledMultipleTimes_ShouldReturnSameCorrelation Assert.Equal(correlationId, firstRetrieval); Assert.Equal(correlationId, secondRetrieval); Assert.Equal(firstRetrieval, secondRetrieval); + Assert.False(_contextAccessor.HttpContext!.Response.Headers.ContainsKey(HeaderKey)); } [Fact] @@ -249,7 +260,7 @@ public void EnrichLogWithCorrelationId_BackwardCompatibility_OldRetrievalMethodS // Arrange string correlationId = Guid.NewGuid().ToString(); _contextAccessor.HttpContext!.Request!.Headers[HeaderKey] = correlationId; - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, true, _contextAccessor); LogEvent evt = null; Logger log = new LoggerConfiguration() @@ -276,6 +287,7 @@ public void EnrichLogWithCorrelationId_BackwardCompatibility_OldRetrievalMethodS Assert.Equal(correlationId, retrievedCorrelationIdOldWay); Assert.Equal(correlationId, retrievedCorrelationIdNewWay); Assert.Equal(retrievedCorrelationIdOldWay, retrievedCorrelationIdNewWay); + Assert.Equal(correlationId, _contextAccessor.HttpContext!.Response.Headers[HeaderKey]); } [Fact] From e24fc825054a7b1e31b911a066f46f62122cb7b1 Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Thu, 30 Oct 2025 16:14:08 +0400 Subject: [PATCH 4/8] feat(#61): Applying Copilot recommendations. --- .../Enrichers/CorrelationIdEnricher.cs | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs b/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs index 431de7b..877f661 100644 --- a/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs +++ b/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs @@ -84,7 +84,7 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) httpContext.Items.Add(Constants.CorrelationIdValueKey, correlationId); } - private void AddCorrelationIdToResponse(HttpContext httpContext, in string correlationId) + private void AddCorrelationIdToResponse(HttpContext httpContext, string correlationId) { if (_addCorrelationIdToResponse && !httpContext.Response.Headers.ContainsKey(_headerKey)) @@ -96,33 +96,24 @@ private void AddCorrelationIdToResponse(HttpContext httpContext, in string corre private string PrepareCorrelationId(HttpContext httpContext) { StringValues requestHeader = httpContext.Request.Headers[_headerKey]; - string returnValue; if (!string.IsNullOrWhiteSpace(requestHeader)) { - returnValue = requestHeader; + return requestHeader; } - else + + StringValues responseHeader = httpContext.Response.Headers[_headerKey]; + + if (!string.IsNullOrWhiteSpace(responseHeader)) { - StringValues responseHeader = httpContext.Response.Headers[_headerKey]; + return responseHeader; + } - if (!string.IsNullOrWhiteSpace(responseHeader)) - { - returnValue = responseHeader; - } - else - { - if (_addValueIfHeaderAbsence) - { - returnValue = Guid.NewGuid().ToString(); - } - else - { - returnValue = null; - } - } + if (_addValueIfHeaderAbsence) + { + return Guid.NewGuid().ToString(); } - return returnValue; + return null; } } \ No newline at end of file From 2dedc05cdfe59f79d10d500d827d70347c8ecb13 Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Thu, 6 Nov 2025 12:56:40 +0400 Subject: [PATCH 5/8] feat(#61): Adding possibility for override algorithm for CorrelationId generation and use it by dependency injection. --- .../Enrichers/CorrelationIdEnricher.cs | 54 ++++++------------- .../Extensions/HttpContextExtensions.cs | 10 ++++ .../CorrelationIds/CorrelationIdPreparer.cs | 52 ++++++++++++++++++ .../CorrelationIdPreparerOptions.cs | 31 +++++++++++ .../CorrelationIds/ICorrelationIdPreparer.cs | 22 ++++++++ .../CorrelationIdEnricherTests.cs | 15 ++++-- 6 files changed, 143 insertions(+), 41 deletions(-) create mode 100644 src/Serilog.Enrichers.ClientInfo/Preparers/CorrelationIds/CorrelationIdPreparer.cs create mode 100644 src/Serilog.Enrichers.ClientInfo/Preparers/CorrelationIds/CorrelationIdPreparerOptions.cs create mode 100644 src/Serilog.Enrichers.ClientInfo/Preparers/CorrelationIds/ICorrelationIdPreparer.cs diff --git a/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs b/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs index 877f661..c17de07 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; /// @@ -12,9 +12,8 @@ public class CorrelationIdEnricher : ILogEventEnricher private const string CorrelationIdItemKey = "Serilog_CorrelationId"; private const string PropertyName = "CorrelationId"; private readonly bool _addCorrelationIdToResponse; - private readonly bool _addValueIfHeaderAbsence; private readonly IHttpContextAccessor _contextAccessor; - private readonly string _headerKey; + private readonly CorrelationIdPreparerOptions _options; /// /// Initializes a new instance of the class. @@ -46,8 +45,7 @@ internal CorrelationIdEnricher( bool addCorrelationIdToResponse, IHttpContextAccessor contextAccessor) { - _headerKey = headerKey; - _addValueIfHeaderAbsence = addValueIfHeaderAbsence; + _options = new CorrelationIdPreparerOptions(addValueIfHeaderAbsence, headerKey); _addCorrelationIdToResponse = addCorrelationIdToResponse; _contextAccessor = contextAccessor; } @@ -58,7 +56,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); @@ -66,14 +64,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; } - string correlationId = PrepareCorrelationId(httpContext); + ICorrelationIdPreparer correlationIdPreparer = httpContext.GetCorrelationIdPreparer(); + + string? correlationId = correlationIdPreparer.PrepareCorrelationId(httpContext, _options); AddCorrelationIdToResponse(httpContext, correlationId); @@ -84,36 +84,14 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) httpContext.Items.Add(Constants.CorrelationIdValueKey, correlationId); } - private void AddCorrelationIdToResponse(HttpContext httpContext, string correlationId) + private void AddCorrelationIdToResponse(HttpContext httpContext, string? correlationId) { if (_addCorrelationIdToResponse - && !httpContext.Response.Headers.ContainsKey(_headerKey)) - { - httpContext.Response.Headers[_headerKey] = correlationId; - } - } - - private string PrepareCorrelationId(HttpContext httpContext) - { - StringValues requestHeader = httpContext.Request.Headers[_headerKey]; - - if (!string.IsNullOrWhiteSpace(requestHeader)) + && !string.IsNullOrEmpty(correlationId) + && !httpContext.Response.Headers.ContainsKey(_options.HeaderKey)) { - return requestHeader; + httpContext.Response.Headers[_options.HeaderKey] = correlationId; } - - StringValues responseHeader = httpContext.Response.Headers[_headerKey]; - - if (!string.IsNullOrWhiteSpace(responseHeader)) - { - return responseHeader; - } - - if (_addValueIfHeaderAbsence) - { - return Guid.NewGuid().ToString(); - } - - return null; } -} \ 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..0f65ce2 --- /dev/null +++ b/src/Serilog.Enrichers.ClientInfo/Preparers/CorrelationIds/CorrelationIdPreparer.cs @@ -0,0 +1,52 @@ +using System; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; + +#nullable enable +namespace Serilog.Preparers.CorrelationIds +{ + internal class CorrelationIdPreparer : ICorrelationIdPreparer + { + protected string? CorrelationId { get; set; } + + /// + public string? PrepareCorrelationId( + HttpContext httpContext, + CorrelationIdPreparerOptions correlationIdPreparerOptions) + { + if (string.IsNullOrEmpty(CorrelationId)) + { + CorrelationId = PrepareValueForCorrelationId(httpContext, correlationIdPreparerOptions); + } + + return CorrelationId; + } + + 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 76b48c5..f5f2bbc 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,7 +18,13 @@ 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); } From 6967e10a24d6c65b7fc3ddf40d8b698594b96e9b Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Thu, 6 Nov 2025 14:25:19 +0400 Subject: [PATCH 6/8] feat(#61): Adding a test to verify the correctness of overriding the algorithm for CorrelationId generation by dependency injection. --- .../CorrelationIdEnricherTests.cs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs b/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs index f5f2bbc..1e73a46 100644 --- a/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs +++ b/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs @@ -29,6 +29,50 @@ public CorrelationIdEnricherTests() _contextAccessor.HttpContext.Returns(httpContext); } + [Fact] + public void EnrichLogWithCorrelationId_WhenRequestServicesContainsICorrelationIdPreparer_ShouldUseCorrelationIdPreparerFromRequestServices() + { + // Arrange + string correlationId = Guid.NewGuid().ToString(); + + 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, true, contextAccessor); + + LogEvent evt = null; + Logger log = new LoggerConfiguration() + .Enrich.With(correlationIdEnricher) + .WriteTo.Sink(new DelegatingSink(e => evt = e)) + .CreateLogger(); + + // Act + log.Information("Has a correlation id."); + + // Assert + Assert.NotNull(evt); + Assert.True(evt.Properties.ContainsKey(LogPropertyName)); + Assert.Equal(correlationId, evt.Properties[LogPropertyName].LiteralValue().ToString()); + Assert.Equal(correlationId, contextAccessor.HttpContext!.Response.Headers[HeaderKey]); + } + [Fact] public void EnrichLogWithCorrelationId_WhenHttpRequestContainCorrelationHeader_ShouldCreateCorrelationIdProperty() { From c2b6c9d8ef26c8594fd3741b45562784838fc549 Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Thu, 6 Nov 2025 15:53:07 +0400 Subject: [PATCH 7/8] feat(#61): Removing the functionality which adds the generated CorrelationId to the response. --- sample/SampleWebApp/appsettings.json | 3 +- .../Enrichers/CorrelationIdEnricher.cs | 22 +----------- ...ClientInfoLoggerConfigurationExtensions.cs | 8 ++--- .../CorrelationIdEnricherTests.cs | 35 ++++++------------- 4 files changed, 15 insertions(+), 53 deletions(-) diff --git a/sample/SampleWebApp/appsettings.json b/sample/SampleWebApp/appsettings.json index 09b7466..c21e1ca 100644 --- a/sample/SampleWebApp/appsettings.json +++ b/sample/SampleWebApp/appsettings.json @@ -31,8 +31,7 @@ { "Name": "WithCorrelationId", "Args": { - "addValueIfHeaderAbsence": true, - "addCorrelationIdToResponse": true + "addValueIfHeaderAbsence": true } }, { diff --git a/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs b/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs index c17de07..a3219f1 100644 --- a/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs +++ b/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs @@ -11,7 +11,6 @@ public class CorrelationIdEnricher : ILogEventEnricher { private const string CorrelationIdItemKey = "Serilog_CorrelationId"; private const string PropertyName = "CorrelationId"; - private readonly bool _addCorrelationIdToResponse; private readonly IHttpContextAccessor _contextAccessor; private readonly CorrelationIdPreparerOptions _options; @@ -24,17 +23,12 @@ public class CorrelationIdEnricher : ILogEventEnricher /// /// Determines whether to add a new correlation ID value if the header is absent. /// - /// - /// Determines whether to add correlation ID value to header collection. - /// public CorrelationIdEnricher( string headerKey, - bool addValueIfHeaderAbsence, - bool addCorrelationIdToResponse) + bool addValueIfHeaderAbsence) : this( headerKey, addValueIfHeaderAbsence, - addCorrelationIdToResponse, new HttpContextAccessor()) { } @@ -42,11 +36,9 @@ public CorrelationIdEnricher( internal CorrelationIdEnricher( string headerKey, bool addValueIfHeaderAbsence, - bool addCorrelationIdToResponse, IHttpContextAccessor contextAccessor) { _options = new CorrelationIdPreparerOptions(addValueIfHeaderAbsence, headerKey); - _addCorrelationIdToResponse = addCorrelationIdToResponse; _contextAccessor = contextAccessor; } @@ -75,23 +67,11 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) string? correlationId = correlationIdPreparer.PrepareCorrelationId(httpContext, _options); - AddCorrelationIdToResponse(httpContext, correlationId); - LogEventProperty correlationIdProperty = new(PropertyName, new ScalarValue(correlationId)); logEvent.AddOrUpdateProperty(correlationIdProperty); httpContext.Items.Add(CorrelationIdItemKey, correlationIdProperty); httpContext.Items.Add(Constants.CorrelationIdValueKey, correlationId); } - - private void AddCorrelationIdToResponse(HttpContext httpContext, string? correlationId) - { - if (_addCorrelationIdToResponse - && !string.IsNullOrEmpty(correlationId) - && !httpContext.Response.Headers.ContainsKey(_options.HeaderKey)) - { - httpContext.Response.Headers[_options.HeaderKey] = correlationId; - } - } } #nullable disable \ No newline at end of file diff --git a/src/Serilog.Enrichers.ClientInfo/Extensions/ClientInfoLoggerConfigurationExtensions.cs b/src/Serilog.Enrichers.ClientInfo/Extensions/ClientInfoLoggerConfigurationExtensions.cs index bbcf8d2..e943688 100644 --- a/src/Serilog.Enrichers.ClientInfo/Extensions/ClientInfoLoggerConfigurationExtensions.cs +++ b/src/Serilog.Enrichers.ClientInfo/Extensions/ClientInfoLoggerConfigurationExtensions.cs @@ -56,20 +56,16 @@ public static LoggerConfiguration WithClientIp( /// Add generated correlation id value if correlation id header not available in /// header collection. /// - /// - /// Add correlation id value to header collection. - /// /// enrichmentConfiguration /// The logger configuration so that multiple calls can be chained. public static LoggerConfiguration WithCorrelationId( this LoggerEnrichmentConfiguration enrichmentConfiguration, string headerName = "x-correlation-id", - bool addValueIfHeaderAbsence = false, - bool addCorrelationIdToResponse = false) + bool addValueIfHeaderAbsence = false) { ArgumentNullException.ThrowIfNull(enrichmentConfiguration, nameof(enrichmentConfiguration)); - return enrichmentConfiguration.With(new CorrelationIdEnricher(headerName, addValueIfHeaderAbsence, addCorrelationIdToResponse)); + return enrichmentConfiguration.With(new CorrelationIdEnricher(headerName, addValueIfHeaderAbsence)); } /// diff --git a/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs b/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs index 1e73a46..9f1b11f 100644 --- a/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs +++ b/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs @@ -55,7 +55,7 @@ public void EnrichLogWithCorrelationId_WhenRequestServicesContainsICorrelationId IHttpContextAccessor contextAccessor = Substitute.For(); contextAccessor.HttpContext.Returns(httpContext); - CorrelationIdEnricher correlationIdEnricher = new(correlationIdPreparerOptions.HeaderKey, correlationIdPreparerOptions.AddValueIfHeaderAbsence, true, contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(correlationIdPreparerOptions.HeaderKey, correlationIdPreparerOptions.AddValueIfHeaderAbsence, contextAccessor); LogEvent evt = null; Logger log = new LoggerConfiguration() @@ -70,7 +70,6 @@ public void EnrichLogWithCorrelationId_WhenRequestServicesContainsICorrelationId Assert.NotNull(evt); Assert.True(evt.Properties.ContainsKey(LogPropertyName)); Assert.Equal(correlationId, evt.Properties[LogPropertyName].LiteralValue().ToString()); - Assert.Equal(correlationId, contextAccessor.HttpContext!.Response.Headers[HeaderKey]); } [Fact] @@ -79,7 +78,7 @@ public void EnrichLogWithCorrelationId_WhenHttpRequestContainCorrelationHeader_S // Arrange string correlationId = Guid.NewGuid().ToString(); _contextAccessor.HttpContext!.Request!.Headers[HeaderKey] = correlationId; - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, true, _contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor); LogEvent evt = null; Logger log = new LoggerConfiguration() @@ -94,7 +93,6 @@ public void EnrichLogWithCorrelationId_WhenHttpRequestContainCorrelationHeader_S Assert.NotNull(evt); Assert.True(evt.Properties.ContainsKey(LogPropertyName)); Assert.Equal(correlationId, evt.Properties[LogPropertyName].LiteralValue().ToString()); - Assert.Equal(correlationId, _contextAccessor.HttpContext!.Response.Headers[HeaderKey]); } [Fact] @@ -102,7 +100,7 @@ public void EnrichLogWithCorrelationId_WhenHttpRequestNotContainCorrelationHeaderAndAddDefaultValueIsFalse_ShouldCreateCorrelationIdPropertyWithNoValue() { // Arrange - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, false, _contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor); LogEvent evt = null; Logger log = new LoggerConfiguration() @@ -117,7 +115,6 @@ public void Assert.NotNull(evt); Assert.True(evt.Properties.ContainsKey(LogPropertyName)); Assert.Null(evt.Properties[LogPropertyName].LiteralValue()); - Assert.False(_contextAccessor.HttpContext!.Response.Headers.ContainsKey(HeaderKey)); } [Fact] @@ -125,7 +122,7 @@ public void EnrichLogWithCorrelationId_WhenHttpRequestNotContainCorrelationHeaderAndAddDefaultValueIsTrue_ShouldCreateCorrelationIdPropertyHasValue() { // Arrange - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, true, true, _contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, true, _contextAccessor); LogEvent evt = null; Logger log = new LoggerConfiguration() @@ -140,8 +137,6 @@ public void Assert.NotNull(evt); Assert.True(evt.Properties.ContainsKey(LogPropertyName)); Assert.NotNull(evt.Properties[LogPropertyName].LiteralValue().ToString()); - Assert.NotNull(_contextAccessor.HttpContext!.Response.Headers[HeaderKey]); - Assert.Equal(evt.Properties[LogPropertyName].LiteralValue(), _contextAccessor.HttpContext!.Response.Headers[HeaderKey].ToString()); } [Fact] @@ -151,7 +146,7 @@ public void // Arrange string correlationId = Guid.NewGuid().ToString(); _contextAccessor.HttpContext!.Request!.Headers[HeaderKey] = correlationId; - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, true, _contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor); LogEvent evt = null; Logger log = new LoggerConfiguration() @@ -166,7 +161,6 @@ public void Assert.NotNull(evt); Assert.True(evt.Properties.ContainsKey(LogPropertyName)); Assert.Equal(correlationId, evt.Properties[LogPropertyName].LiteralValue().ToString()); - Assert.Equal(correlationId, _contextAccessor.HttpContext!.Response.Headers[HeaderKey]); } [Fact] @@ -178,7 +172,7 @@ public void string responseCorrelationId = Guid.NewGuid().ToString(); _contextAccessor.HttpContext!.Request!.Headers[HeaderKey] = requestCorrelationId; _contextAccessor.HttpContext!.Response!.Headers[HeaderKey] = responseCorrelationId; - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, true, _contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor); LogEvent evt = null; Logger log = new LoggerConfiguration() @@ -193,7 +187,6 @@ public void Assert.NotNull(evt); Assert.True(evt.Properties.ContainsKey(LogPropertyName)); Assert.Equal(requestCorrelationId, evt.Properties[LogPropertyName].LiteralValue().ToString()); - Assert.Equal(responseCorrelationId, _contextAccessor.HttpContext!.Response.Headers[HeaderKey]); } [Fact] @@ -202,7 +195,7 @@ public void GetCorrelationId_WhenHttpRequestContainCorrelationHeader_ShouldRetur // Arrange string correlationId = Guid.NewGuid().ToString(); _contextAccessor.HttpContext!.Request!.Headers[HeaderKey] = correlationId; - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, true, _contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor); LogEvent evt = null; Logger log = new LoggerConfiguration() @@ -217,7 +210,6 @@ public void GetCorrelationId_WhenHttpRequestContainCorrelationHeader_ShouldRetur // Assert Assert.NotNull(evt); Assert.Equal(correlationId, retrievedCorrelationId); - Assert.Equal(correlationId, _contextAccessor.HttpContext!.Response.Headers[HeaderKey]); } [Fact] @@ -225,7 +217,7 @@ public void GetCorrelationId_WhenHttpRequestNotContainCorrelationHeaderAndAddDefaultValueIsTrue_ShouldReturnGeneratedCorrelationIdFromHttpContext() { // Arrange - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, true, true, _contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, true, _contextAccessor); LogEvent evt = null; Logger log = new LoggerConfiguration() @@ -236,13 +228,11 @@ public void // Act log.Information("Has a correlation id."); string retrievedCorrelationId = _contextAccessor.HttpContext!.GetCorrelationId(); - string correlationIdFromResponse = _contextAccessor.HttpContext!.Response.Headers[HeaderKey]; // Assert Assert.NotNull(evt); Assert.NotNull(retrievedCorrelationId); Assert.NotEmpty(retrievedCorrelationId); - Assert.Equal(retrievedCorrelationId, correlationIdFromResponse); // Verify it's a valid GUID format Assert.True(Guid.TryParse(retrievedCorrelationId, out _)); } @@ -252,7 +242,7 @@ public void GetCorrelationId_WhenHttpRequestNotContainCorrelationHeaderAndAddDefaultValueIsFalse_ShouldReturnNullFromHttpContext() { // Arrange - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, false, _contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor); LogEvent evt = null; Logger log = new LoggerConfiguration() @@ -267,7 +257,6 @@ public void // Assert Assert.NotNull(evt); Assert.Null(retrievedCorrelationId); - Assert.False(_contextAccessor.HttpContext!.Response.Headers.ContainsKey(HeaderKey)); } [Fact] @@ -276,7 +265,7 @@ public void GetCorrelationId_WhenCalledMultipleTimes_ShouldReturnSameCorrelation // Arrange string correlationId = Guid.NewGuid().ToString(); _contextAccessor.HttpContext!.Request!.Headers[HeaderKey] = correlationId; - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, false, _contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor); Logger log = new LoggerConfiguration() .Enrich.With(correlationIdEnricher) @@ -294,7 +283,6 @@ public void GetCorrelationId_WhenCalledMultipleTimes_ShouldReturnSameCorrelation Assert.Equal(correlationId, firstRetrieval); Assert.Equal(correlationId, secondRetrieval); Assert.Equal(firstRetrieval, secondRetrieval); - Assert.False(_contextAccessor.HttpContext!.Response.Headers.ContainsKey(HeaderKey)); } [Fact] @@ -313,7 +301,7 @@ public void EnrichLogWithCorrelationId_BackwardCompatibility_OldRetrievalMethodS // Arrange string correlationId = Guid.NewGuid().ToString(); _contextAccessor.HttpContext!.Request!.Headers[HeaderKey] = correlationId; - CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, true, _contextAccessor); + CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor); LogEvent evt = null; Logger log = new LoggerConfiguration() @@ -340,7 +328,6 @@ public void EnrichLogWithCorrelationId_BackwardCompatibility_OldRetrievalMethodS Assert.Equal(correlationId, retrievedCorrelationIdOldWay); Assert.Equal(correlationId, retrievedCorrelationIdNewWay); Assert.Equal(retrievedCorrelationIdOldWay, retrievedCorrelationIdNewWay); - Assert.Equal(correlationId, _contextAccessor.HttpContext!.Response.Headers[HeaderKey]); } [Fact] From 8232311157b5883f375547b1e5bdda2ec6fb0e0a Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Fri, 7 Nov 2025 08:35:14 +0400 Subject: [PATCH 8/8] fix(#61): Switching CorrelationId to AsyncLocal. --- .../Preparers/CorrelationIds/CorrelationIdPreparer.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Serilog.Enrichers.ClientInfo/Preparers/CorrelationIds/CorrelationIdPreparer.cs b/src/Serilog.Enrichers.ClientInfo/Preparers/CorrelationIds/CorrelationIdPreparer.cs index 0f65ce2..6adbbab 100644 --- a/src/Serilog.Enrichers.ClientInfo/Preparers/CorrelationIds/CorrelationIdPreparer.cs +++ b/src/Serilog.Enrichers.ClientInfo/Preparers/CorrelationIds/CorrelationIdPreparer.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; @@ -7,19 +8,19 @@ namespace Serilog.Preparers.CorrelationIds { internal class CorrelationIdPreparer : ICorrelationIdPreparer { - protected string? CorrelationId { get; set; } + protected AsyncLocal CorrelationId { get; } = new AsyncLocal(); /// public string? PrepareCorrelationId( HttpContext httpContext, CorrelationIdPreparerOptions correlationIdPreparerOptions) { - if (string.IsNullOrEmpty(CorrelationId)) + if (string.IsNullOrEmpty(CorrelationId.Value)) { - CorrelationId = PrepareValueForCorrelationId(httpContext, correlationIdPreparerOptions); + CorrelationId.Value = PrepareValueForCorrelationId(httpContext, correlationIdPreparerOptions); } - return CorrelationId; + return CorrelationId.Value; } protected string? PrepareValueForCorrelationId(