diff --git a/README.md b/README.md
index 959f354..c3c71db 100644
--- a/README.md
+++ b/README.md
@@ -94,6 +94,26 @@ or
}
}
```
+
+#### Retrieving Correlation ID
+You can easily retrieve the correlation ID from `HttpContext` using the `GetCorrelationId()` extension method:
+
+```csharp
+public void SomeControllerAction()
+{
+ // This will return the correlation ID that was enriched by the CorrelationIdEnricher
+ var correlationId = HttpContext.GetCorrelationId();
+
+ // You can use this for error reporting, tracing, etc.
+ if (!string.IsNullOrEmpty(correlationId))
+ {
+ // Show correlation ID to user for error reporting
+ // or use it for additional logging/tracing
+ }
+}
+```
+
+This eliminates the need for manual casting and provides a clean API for accessing correlation IDs.
### RequestHeader
You can use multiple `WithRequestHeader` to log different request headers. `WithRequestHeader` accepts two parameters; The first parameter `headerName` is the header name to log
and the second parameter is `propertyName` which is the log property name.
diff --git a/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs b/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs
index dfa82d1..187dcca 100644
--- a/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs
+++ b/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs
@@ -9,6 +9,7 @@ namespace Serilog.Enrichers;
public class CorrelationIdEnricher : ILogEventEnricher
{
private const string CorrelationIdItemKey = "Serilog_CorrelationId";
+ private const string CorrelationIdValueKey = "Serilog_CorrelationId_Value";
private const string PropertyName = "CorrelationId";
private readonly string _headerKey;
private readonly bool _addValueIfHeaderAbsence;
@@ -47,6 +48,14 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
if (httpContext.Items[CorrelationIdItemKey] is LogEventProperty logEventProperty)
{
logEvent.AddPropertyIfAbsent(logEventProperty);
+
+ // Ensure the string value is also available if not already stored
+ if (!httpContext.Items.ContainsKey(CorrelationIdValueKey))
+ {
+ var correlationIdValue = ((ScalarValue)logEventProperty.Value).Value as string;
+ httpContext.Items.Add(CorrelationIdValueKey, correlationIdValue);
+ }
+
return;
}
@@ -76,5 +85,6 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
logEvent.AddOrUpdateProperty(correlationIdProperty);
httpContext.Items.Add(CorrelationIdItemKey, correlationIdProperty);
+ httpContext.Items.Add(CorrelationIdValueKey, correlationId);
}
}
\ No newline at end of file
diff --git a/src/Serilog.Enrichers.ClientInfo/Extensions/HttpContextExtensions.cs b/src/Serilog.Enrichers.ClientInfo/Extensions/HttpContextExtensions.cs
new file mode 100644
index 0000000..63eda75
--- /dev/null
+++ b/src/Serilog.Enrichers.ClientInfo/Extensions/HttpContextExtensions.cs
@@ -0,0 +1,21 @@
+using Microsoft.AspNetCore.Http;
+
+namespace Serilog.Enrichers;
+
+///
+/// Extension methods for to access enriched values.
+///
+public static class HttpContextExtensions
+{
+ private const string CorrelationIdValueKey = "Serilog_CorrelationId_Value";
+
+ ///
+ /// Retrieves the correlation ID value from the current HTTP context.
+ ///
+ /// The HTTP context.
+ /// The correlation ID as a string, or null if not available.
+ public static string GetCorrelationId(this HttpContext httpContext)
+ {
+ return httpContext?.Items[CorrelationIdValueKey] as string;
+ }
+}
\ No newline at end of file
diff --git a/src/Serilog.Enrichers.ClientInfo/Serilog.Enrichers.ClientInfo.csproj b/src/Serilog.Enrichers.ClientInfo/Serilog.Enrichers.ClientInfo.csproj
index a06cc56..1dbbf2a 100644
--- a/src/Serilog.Enrichers.ClientInfo/Serilog.Enrichers.ClientInfo.csproj
+++ b/src/Serilog.Enrichers.ClientInfo/Serilog.Enrichers.ClientInfo.csproj
@@ -1,6 +1,6 @@
- net8.0;net9.0
+ net8.0
Serilog.Enrichers.ClientInfo
Serilog
latest
diff --git a/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs b/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs
index 34c1793..0f52e4d 100644
--- a/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs
+++ b/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Http;
using NSubstitute;
+using Serilog.Enrichers;
using Serilog.Events;
using System;
using Xunit;
@@ -170,4 +171,145 @@ public void WithClientIp_ThenLoggerIsCalled_ShouldNotThrowException()
// Assert
Assert.Null(exception);
}
+
+ [Fact]
+ public void GetCorrelationId_WhenHttpRequestContainCorrelationHeader_ShouldReturnCorrelationIdFromHttpContext()
+ {
+ // Arrange
+ var correlationId = Guid.NewGuid().ToString();
+ _contextAccessor.HttpContext!.Request!.Headers[HeaderKey] = correlationId;
+ var correlationIdEnricher = new CorrelationIdEnricher(HeaderKey, false, _contextAccessor);
+
+ LogEvent evt = null;
+ var log = new LoggerConfiguration()
+ .Enrich.With(correlationIdEnricher)
+ .WriteTo.Sink(new DelegatingSink(e => evt = e))
+ .CreateLogger();
+
+ // Act
+ log.Information(@"Has a correlation id.");
+ var retrievedCorrelationId = _contextAccessor.HttpContext!.GetCorrelationId();
+
+ // Assert
+ Assert.NotNull(evt);
+ Assert.Equal(correlationId, retrievedCorrelationId);
+ }
+
+ [Fact]
+ public void GetCorrelationId_WhenHttpRequestNotContainCorrelationHeaderAndAddDefaultValueIsTrue_ShouldReturnGeneratedCorrelationIdFromHttpContext()
+ {
+ // Arrange
+ var correlationIdEnricher = new CorrelationIdEnricher(HeaderKey, true, _contextAccessor);
+
+ LogEvent evt = null;
+ var log = new LoggerConfiguration()
+ .Enrich.With(correlationIdEnricher)
+ .WriteTo.Sink(new DelegatingSink(e => evt = e))
+ .CreateLogger();
+
+ // Act
+ log.Information(@"Has a correlation id.");
+ var retrievedCorrelationId = _contextAccessor.HttpContext!.GetCorrelationId();
+
+ // Assert
+ Assert.NotNull(evt);
+ Assert.NotNull(retrievedCorrelationId);
+ Assert.NotEmpty(retrievedCorrelationId);
+ // Verify it's a valid GUID format
+ Assert.True(Guid.TryParse(retrievedCorrelationId, out _));
+ }
+
+ [Fact]
+ public void GetCorrelationId_WhenHttpRequestNotContainCorrelationHeaderAndAddDefaultValueIsFalse_ShouldReturnNullFromHttpContext()
+ {
+ // Arrange
+ var correlationIdEnricher = new CorrelationIdEnricher(HeaderKey, false, _contextAccessor);
+
+ LogEvent evt = null;
+ var log = new LoggerConfiguration()
+ .Enrich.With(correlationIdEnricher)
+ .WriteTo.Sink(new DelegatingSink(e => evt = e))
+ .CreateLogger();
+
+ // Act
+ log.Information(@"Has a correlation id.");
+ var retrievedCorrelationId = _contextAccessor.HttpContext!.GetCorrelationId();
+
+ // Assert
+ Assert.NotNull(evt);
+ Assert.Null(retrievedCorrelationId);
+ }
+
+ [Fact]
+ public void GetCorrelationId_WhenCalledMultipleTimes_ShouldReturnSameCorrelationId()
+ {
+ // Arrange
+ var correlationId = Guid.NewGuid().ToString();
+ _contextAccessor.HttpContext!.Request!.Headers[HeaderKey] = correlationId;
+ var correlationIdEnricher = new CorrelationIdEnricher(HeaderKey, false, _contextAccessor);
+
+ var log = new LoggerConfiguration()
+ .Enrich.With(correlationIdEnricher)
+ .WriteTo.Sink(new DelegatingSink(_ => { }))
+ .CreateLogger();
+
+ // Act
+ log.Information(@"First log message.");
+ var firstRetrieval = _contextAccessor.HttpContext!.GetCorrelationId();
+
+ log.Information(@"Second log message.");
+ var secondRetrieval = _contextAccessor.HttpContext!.GetCorrelationId();
+
+ // Assert
+ Assert.Equal(correlationId, firstRetrieval);
+ Assert.Equal(correlationId, secondRetrieval);
+ Assert.Equal(firstRetrieval, secondRetrieval);
+ }
+
+ [Fact]
+ public void GetCorrelationId_WhenHttpContextIsNull_ShouldReturnNull()
+ {
+ // Arrange & Act
+ var result = HttpContextExtensions.GetCorrelationId(null);
+
+ // Assert
+ Assert.Null(result);
+ }
+
+ [Fact]
+ public void EnrichLogWithCorrelationId_BackwardCompatibility_OldRetrievalMethodShouldStillWork()
+ {
+ // Arrange
+ var correlationId = Guid.NewGuid().ToString();
+ _contextAccessor.HttpContext!.Request!.Headers[HeaderKey] = correlationId;
+ var correlationIdEnricher = new CorrelationIdEnricher(HeaderKey, false, _contextAccessor);
+
+ LogEvent evt = null;
+ var log = new LoggerConfiguration()
+ .Enrich.With(correlationIdEnricher)
+ .WriteTo.Sink(new DelegatingSink(e => evt = e))
+ .CreateLogger();
+
+ // Act
+ log.Information(@"Has a correlation id.");
+
+ // Test that the old way (hacky way) still works
+ var httpContext = _contextAccessor.HttpContext!;
+ string retrievedCorrelationIdOldWay = null;
+
+ if (httpContext.Items.TryGetValue("Serilog_CorrelationId", out var correlationIdItem) &&
+ correlationIdItem is LogEventProperty { Name: "CorrelationId" } correlationIdProperty)
+ {
+ retrievedCorrelationIdOldWay = ((ScalarValue)correlationIdProperty.Value).Value as string;
+ }
+
+ // Test that the new way also works
+ var retrievedCorrelationIdNewWay = httpContext.GetCorrelationId();
+
+ // Assert
+ Assert.NotNull(evt);
+ Assert.Equal(correlationId, retrievedCorrelationIdOldWay);
+ Assert.Equal(correlationId, retrievedCorrelationIdNewWay);
+ Assert.Equal(retrievedCorrelationIdOldWay, retrievedCorrelationIdNewWay);
+ }
}
\ No newline at end of file
diff --git a/test/Serilog.Enrichers.ClientInfo.Tests/Serilog.Enrichers.ClientInfo.Tests.csproj b/test/Serilog.Enrichers.ClientInfo.Tests/Serilog.Enrichers.ClientInfo.Tests.csproj
index dc3f541..4b62ef0 100644
--- a/test/Serilog.Enrichers.ClientInfo.Tests/Serilog.Enrichers.ClientInfo.Tests.csproj
+++ b/test/Serilog.Enrichers.ClientInfo.Tests/Serilog.Enrichers.ClientInfo.Tests.csproj
@@ -1,13 +1,13 @@
- net9.0
+ net8.0
false
-
-
+
+