Skip to content

Commit 491a73a

Browse files
committed
2 parents 7f7a015 + 4f5d99e commit 491a73a

File tree

6 files changed

+193
-0
lines changed

6 files changed

+193
-0
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,26 @@ or
9494
}
9595
}
9696
```
97+
#### Retrieving Correlation ID
98+
You can easily retrieve the correlation ID from `HttpContext` using the `GetCorrelationId()` extension method:
99+
100+
```csharp
101+
public void SomeControllerAction()
102+
{
103+
// This will return the correlation ID that was enriched by the CorrelationIdEnricher
104+
var correlationId = HttpContext.GetCorrelationId();
105+
106+
// You can use this for error reporting, tracing, etc.
107+
if (!string.IsNullOrEmpty(correlationId))
108+
{
109+
// Show correlation ID to user for error reporting
110+
// or use it for additional logging/tracing
111+
}
112+
}
113+
```
114+
115+
This eliminates the need for manual casting and provides a clean API for accessing correlation IDs.
116+
97117
### RequestHeader
98118
You can use multiple `WithRequestHeader` to log different request headers. `WithRequestHeader` accepts two parameters; The first parameter `headerName` is the header name to log
99119
and the second parameter is `propertyName` which is the log property name.

nuget.exe

8.56 MB
Binary file not shown.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace Serilog;
2+
3+
internal static class Constants
4+
{
5+
public const string CorrelationIdValueKey = "Serilog_CorrelationId_Value";
6+
}

src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
4646
value is LogEventProperty logEventProperty)
4747
{
4848
logEvent.AddPropertyIfAbsent(logEventProperty);
49+
50+
// Ensure the string value is also available if not already stored
51+
if (!httpContext.Items.ContainsKey(Constants.CorrelationIdValueKey))
52+
{
53+
string correlationIdValue = ((ScalarValue)logEventProperty.Value).Value as string;
54+
httpContext.Items.Add(Constants.CorrelationIdValueKey, correlationIdValue);
55+
}
56+
4957
return;
5058
}
5159

@@ -67,5 +75,6 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
6775
logEvent.AddOrUpdateProperty(correlationIdProperty);
6876

6977
httpContext.Items.Add(CorrelationIdItemKey, correlationIdProperty);
78+
httpContext.Items.Add(Constants.CorrelationIdValueKey, correlationId);
7079
}
7180
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using Microsoft.AspNetCore.Http;
2+
3+
namespace Serilog.Enrichers;
4+
5+
/// <summary>
6+
/// Extension methods for <see cref="HttpContext" /> to access enriched values.
7+
/// </summary>
8+
public static class HttpContextExtensions
9+
{
10+
/// <summary>
11+
/// Retrieves the correlation ID value from the current HTTP context.
12+
/// </summary>
13+
/// <param name="httpContext">The HTTP context.</param>
14+
/// <returns>The correlation ID as a string, or null if not available.</returns>
15+
public static string GetCorrelationId(this HttpContext httpContext)
16+
=> httpContext?.Items[Constants.CorrelationIdValueKey] as string;
17+
}

test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,147 @@ public void
161161
Assert.Equal(requestCorrelationId, evt.Properties[LogPropertyName].LiteralValue().ToString());
162162
}
163163

164+
[Fact]
165+
public void GetCorrelationId_WhenHttpRequestContainCorrelationHeader_ShouldReturnCorrelationIdFromHttpContext()
166+
{
167+
// Arrange
168+
string correlationId = Guid.NewGuid().ToString();
169+
_contextAccessor.HttpContext!.Request!.Headers[HeaderKey] = correlationId;
170+
CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor);
171+
172+
LogEvent evt = null;
173+
Logger log = new LoggerConfiguration()
174+
.Enrich.With(correlationIdEnricher)
175+
.WriteTo.Sink(new DelegatingSink(e => evt = e))
176+
.CreateLogger();
177+
178+
// Act
179+
log.Information("Has a correlation id.");
180+
string retrievedCorrelationId = _contextAccessor.HttpContext!.GetCorrelationId();
181+
182+
// Assert
183+
Assert.NotNull(evt);
184+
Assert.Equal(correlationId, retrievedCorrelationId);
185+
}
186+
187+
[Fact]
188+
public void
189+
GetCorrelationId_WhenHttpRequestNotContainCorrelationHeaderAndAddDefaultValueIsTrue_ShouldReturnGeneratedCorrelationIdFromHttpContext()
190+
{
191+
// Arrange
192+
CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, true, _contextAccessor);
193+
194+
LogEvent evt = null;
195+
Logger log = new LoggerConfiguration()
196+
.Enrich.With(correlationIdEnricher)
197+
.WriteTo.Sink(new DelegatingSink(e => evt = e))
198+
.CreateLogger();
199+
200+
// Act
201+
log.Information("Has a correlation id.");
202+
string retrievedCorrelationId = _contextAccessor.HttpContext!.GetCorrelationId();
203+
204+
// Assert
205+
Assert.NotNull(evt);
206+
Assert.NotNull(retrievedCorrelationId);
207+
Assert.NotEmpty(retrievedCorrelationId);
208+
// Verify it's a valid GUID format
209+
Assert.True(Guid.TryParse(retrievedCorrelationId, out _));
210+
}
211+
212+
[Fact]
213+
public void
214+
GetCorrelationId_WhenHttpRequestNotContainCorrelationHeaderAndAddDefaultValueIsFalse_ShouldReturnNullFromHttpContext()
215+
{
216+
// Arrange
217+
CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor);
218+
219+
LogEvent evt = null;
220+
Logger log = new LoggerConfiguration()
221+
.Enrich.With(correlationIdEnricher)
222+
.WriteTo.Sink(new DelegatingSink(e => evt = e))
223+
.CreateLogger();
224+
225+
// Act
226+
log.Information("Has a correlation id.");
227+
string retrievedCorrelationId = _contextAccessor.HttpContext!.GetCorrelationId();
228+
229+
// Assert
230+
Assert.NotNull(evt);
231+
Assert.Null(retrievedCorrelationId);
232+
}
233+
234+
[Fact]
235+
public void GetCorrelationId_WhenCalledMultipleTimes_ShouldReturnSameCorrelationId()
236+
{
237+
// Arrange
238+
string correlationId = Guid.NewGuid().ToString();
239+
_contextAccessor.HttpContext!.Request!.Headers[HeaderKey] = correlationId;
240+
CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor);
241+
242+
Logger log = new LoggerConfiguration()
243+
.Enrich.With(correlationIdEnricher)
244+
.WriteTo.Sink(new DelegatingSink(_ => { }))
245+
.CreateLogger();
246+
247+
// Act
248+
log.Information("First log message.");
249+
string firstRetrieval = _contextAccessor.HttpContext!.GetCorrelationId();
250+
251+
log.Information("Second log message.");
252+
string secondRetrieval = _contextAccessor.HttpContext!.GetCorrelationId();
253+
254+
// Assert
255+
Assert.Equal(correlationId, firstRetrieval);
256+
Assert.Equal(correlationId, secondRetrieval);
257+
Assert.Equal(firstRetrieval, secondRetrieval);
258+
}
259+
260+
[Fact]
261+
public void GetCorrelationId_WhenHttpContextIsNull_ShouldReturnNull()
262+
{
263+
// Arrange & Act
264+
string result = HttpContextExtensions.GetCorrelationId(null);
265+
266+
// Assert
267+
Assert.Null(result);
268+
}
269+
270+
[Fact]
271+
public void EnrichLogWithCorrelationId_BackwardCompatibility_OldRetrievalMethodShouldStillWork()
272+
{
273+
// Arrange
274+
string correlationId = Guid.NewGuid().ToString();
275+
_contextAccessor.HttpContext!.Request!.Headers[HeaderKey] = correlationId;
276+
CorrelationIdEnricher correlationIdEnricher = new(HeaderKey, false, _contextAccessor);
277+
278+
LogEvent evt = null;
279+
Logger log = new LoggerConfiguration()
280+
.Enrich.With(correlationIdEnricher)
281+
.WriteTo.Sink(new DelegatingSink(e => evt = e))
282+
.CreateLogger();
283+
284+
// Act
285+
log.Information("Has a correlation id.");
286+
287+
// Test that the old way (hacky way) still works
288+
HttpContext httpContext = _contextAccessor.HttpContext!;
289+
string retrievedCorrelationIdOldWay = null;
290+
291+
if (httpContext.Items.TryGetValue("Serilog_CorrelationId", out object correlationIdItem) &&
292+
correlationIdItem is LogEventProperty { Name: "CorrelationId" } correlationIdProperty)
293+
retrievedCorrelationIdOldWay = ((ScalarValue)correlationIdProperty.Value).Value as string;
294+
295+
// Test that the new way also works
296+
string retrievedCorrelationIdNewWay = httpContext.GetCorrelationId();
297+
298+
// Assert
299+
Assert.NotNull(evt);
300+
Assert.Equal(correlationId, retrievedCorrelationIdOldWay);
301+
Assert.Equal(correlationId, retrievedCorrelationIdNewWay);
302+
Assert.Equal(retrievedCorrelationIdOldWay, retrievedCorrelationIdNewWay);
303+
}
304+
164305
[Fact]
165306
public void WithClientIp_ThenLoggerIsCalled_ShouldNotThrowException()
166307
{

0 commit comments

Comments
 (0)