diff --git a/README.md b/README.md
index b5165ee..06a8124 100644
--- a/README.md
+++ b/README.md
@@ -46,10 +46,10 @@ or in `appsettings.json` file:
---
### ClientIp
-For `ClientIp` enricher you can configure the `x-forwarded-for` header if the proxy server uses a different header to forward the IP address.
+`ClientIp` enricher reads client IP from `HttpContext.Connection.RemoteIpAddress`. Since version 2.1, for [security reasons](https://nvd.nist.gov/vuln/detail/CVE-2023-22474), it no longer reads the `x-forwarded-for` header. To handle forwarded headers, configure [ForwardedHeadersOptions](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-7.0#forwarded-headers-middleware-order). If you still want to log `x-forwarded-for`, you can use the `RequestHeader` enricher.
```csharp
Log.Logger = new LoggerConfiguration()
- .Enrich.WithClientIp(headerName: "CF-Connecting-IP")
+ .Enrich.WithClientIp()
...
```
or
@@ -60,10 +60,7 @@ or
"Using": [ "Serilog.Enrichers.ClientInfo" ],
"Enrich": [
{
- "Name": "WithClientIp",
- "Args": {
- "headerName": "CF-Connecting-IP"
- }
+ "Name": "WithClientIp"
}
],
}
diff --git a/Serilog.Enrichers.ClientInfo.sln b/Serilog.Enrichers.ClientInfo.sln
index c894d1c..3935707 100644
--- a/Serilog.Enrichers.ClientInfo.sln
+++ b/Serilog.Enrichers.ClientInfo.sln
@@ -53,4 +53,4 @@ Global
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {785F0E90-8DC5-4003-AD7A-33DE806F3B3A}
EndGlobalSection
-EndGlobal
+EndGlobal
\ No newline at end of file
diff --git a/src/Serilog.Enrichers.ClientInfo/Accessors/HttpContextAccessor.cs b/src/Serilog.Enrichers.ClientInfo/Accessors/HttpContextAccessor.cs
deleted file mode 100644
index 1e32ab0..0000000
--- a/src/Serilog.Enrichers.ClientInfo/Accessors/HttpContextAccessor.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-#if NETFULL
-using System.Web;
-
-namespace Serilog.Enrichers.ClientInfo.Accessors
-{
- public interface IHttpContextAccessor
- {
- HttpContext HttpContext { get; }
- }
-
- internal class HttpContextAccessor : IHttpContextAccessor
- {
- public HttpContext HttpContext => HttpContext.Current;
- }
-}
-#endif
\ No newline at end of file
diff --git a/src/Serilog.Enrichers.ClientInfo/Enrichers/ClientHeaderEnricher.cs b/src/Serilog.Enrichers.ClientInfo/Enrichers/ClientHeaderEnricher.cs
index 579aa61..2bad2c3 100644
--- a/src/Serilog.Enrichers.ClientInfo/Enrichers/ClientHeaderEnricher.cs
+++ b/src/Serilog.Enrichers.ClientInfo/Enrichers/ClientHeaderEnricher.cs
@@ -1,14 +1,7 @@
-using Serilog.Core;
+using Microsoft.AspNetCore.Http;
+using Serilog.Core;
using Serilog.Events;
-#if NETFULL
-
-using Serilog.Enrichers.ClientInfo.Accessors;
-
-#else
-using Microsoft.AspNetCore.Http;
-#endif
-
namespace Serilog.Enrichers;
///
@@ -19,6 +12,11 @@ public class ClientHeaderEnricher : ILogEventEnricher
private readonly string _headerKey;
private readonly IHttpContextAccessor _contextAccessor;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The key of the header.
+ /// The name of the property.
public ClientHeaderEnricher(string headerKey, string propertyName)
: this(headerKey, propertyName, new HttpContextAccessor())
{
@@ -27,7 +25,7 @@ public ClientHeaderEnricher(string headerKey, string propertyName)
internal ClientHeaderEnricher(string headerKey, string propertyName, IHttpContextAccessor contextAccessor)
{
_headerKey = headerKey;
- _propertyName = string.IsNullOrWhiteSpace(propertyName)
+ _propertyName = string.IsNullOrWhiteSpace(propertyName)
? headerKey.Replace("-", "")
: propertyName;
_clientHeaderItemKey = $"Serilog_{headerKey}";
@@ -43,7 +41,9 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
var httpContext = _contextAccessor.HttpContext;
if (httpContext == null)
+ {
return;
+ }
if (httpContext.Items[_clientHeaderItemKey] is LogEventProperty logEventProperty)
{
@@ -59,4 +59,4 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
logEvent.AddPropertyIfAbsent(logProperty);
}
-}
+}
\ No newline at end of file
diff --git a/src/Serilog.Enrichers.ClientInfo/Enrichers/ClientIpEnricher.cs b/src/Serilog.Enrichers.ClientInfo/Enrichers/ClientIpEnricher.cs
index 26aa083..bff7982 100644
--- a/src/Serilog.Enrichers.ClientInfo/Enrichers/ClientIpEnricher.cs
+++ b/src/Serilog.Enrichers.ClientInfo/Enrichers/ClientIpEnricher.cs
@@ -1,89 +1,55 @@
-using Serilog.Core;
+using Microsoft.AspNetCore.Http;
+using Serilog.Core;
using Serilog.Events;
-using System.Linq;
using System.Runtime.CompilerServices;
-#if NETFULL
+[assembly: InternalsVisibleTo("Serilog.Enrichers.ClientInfo.Tests")]
-using Serilog.Enrichers.ClientInfo.Accessors;
+namespace Serilog.Enrichers;
-#else
-using Microsoft.AspNetCore.Http;
-#endif
+public class ClientIpEnricher : ILogEventEnricher
+{
+ private const string IpAddressPropertyName = "ClientIp";
+ private const string IpAddressItemKey = "Serilog_ClientIp";
-[assembly: InternalsVisibleTo("Serilog.Enrichers.ClientInfo.Tests")]
+ private readonly IHttpContextAccessor _contextAccessor;
-namespace Serilog.Enrichers
-{
- public class ClientIpEnricher : ILogEventEnricher
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ClientIpEnricher() : this(new HttpContextAccessor())
{
- private const string IpAddressPropertyName = "ClientIp";
- private const string IpAddressItemKey = "Serilog_ClientIp";
- private readonly string _forwardHeaderKey;
+ }
- private readonly IHttpContextAccessor _contextAccessor;
+ internal ClientIpEnricher(IHttpContextAccessor contextAccessor)
+ {
+ _contextAccessor = contextAccessor;
+ }
- public ClientIpEnricher(string forwardHeaderKey)
- : this(forwardHeaderKey, new HttpContextAccessor())
+ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
+ {
+ var httpContext = _contextAccessor.HttpContext;
+ if (httpContext == null)
{
+ return;
}
- internal ClientIpEnricher(string forwardHeaderKey, IHttpContextAccessor contextAccessor)
+ if (httpContext.Items[IpAddressItemKey] is LogEventProperty logEventProperty)
{
- _forwardHeaderKey = forwardHeaderKey;
- _contextAccessor = contextAccessor;
+ logEvent.AddPropertyIfAbsent(logEventProperty);
+ return;
}
- public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
- {
- var httpContext = _contextAccessor.HttpContext;
- if (httpContext == null)
- return;
-
- if (httpContext.Items[IpAddressItemKey] is LogEventProperty logEventProperty)
- {
- logEvent.AddPropertyIfAbsent(logEventProperty);
- return;
- }
-
- var ipAddress = GetIpAddress();
-
- if (string.IsNullOrWhiteSpace(ipAddress))
- ipAddress = "unknown";
+ var ipAddress = _contextAccessor.HttpContext?.Connection?.RemoteIpAddress?.ToString();
- var ipAddressProperty = new LogEventProperty(IpAddressPropertyName, new ScalarValue(ipAddress));
- httpContext.Items.Add(IpAddressItemKey, ipAddressProperty);
-
- logEvent.AddPropertyIfAbsent(ipAddressProperty);
- }
-
-#if NETFULL
-
- private string GetIpAddress()
+ if (string.IsNullOrWhiteSpace(ipAddress))
{
- var ipAddress = _contextAccessor.HttpContext.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
-
- return !string.IsNullOrEmpty(ipAddress)
- ? GetIpAddressFromProxy(ipAddress)
- : _contextAccessor.HttpContext.Request.ServerVariables["REMOTE_ADDR"];
+ ipAddress = "unknown";
}
-#else
- private string GetIpAddress()
- {
- var ipAddress = _contextAccessor.HttpContext?.Request?.Headers[_forwardHeaderKey].FirstOrDefault();
+ var ipAddressProperty = new LogEventProperty(IpAddressPropertyName, new ScalarValue(ipAddress));
+ httpContext.Items.Add(IpAddressItemKey, ipAddressProperty);
- return !string.IsNullOrEmpty(ipAddress)
- ? GetIpAddressFromProxy(ipAddress)
- : _contextAccessor.HttpContext?.Connection?.RemoteIpAddress?.ToString();
- }
-#endif
-
- private string GetIpAddressFromProxy(string proxifiedIpList)
- {
- var addresses = proxifiedIpList.Split(',');
-
- return addresses.Length == 0 ? string.Empty : addresses[0].Trim();
- }
+ logEvent.AddPropertyIfAbsent(ipAddressProperty);
}
}
\ No newline at end of file
diff --git a/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs b/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs
index d3e042a..dfa82d1 100644
--- a/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs
+++ b/src/Serilog.Enrichers.ClientInfo/Enrichers/CorrelationIdEnricher.cs
@@ -1,15 +1,8 @@
-using Serilog.Core;
+using Microsoft.AspNetCore.Http;
+using Serilog.Core;
using Serilog.Events;
using System;
-#if NETFULL
-
-using Serilog.Enrichers.ClientInfo.Accessors;
-
-#else
-using Microsoft.AspNetCore.Http;
-#endif
-
namespace Serilog.Enrichers;
///
@@ -21,6 +14,15 @@ public class CorrelationIdEnricher : ILogEventEnricher
private readonly bool _addValueIfHeaderAbsence;
private readonly IHttpContextAccessor _contextAccessor;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The header key used to retrieve the correlation ID from the HTTP request or response headers.
+ ///
+ ///
+ /// 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())
{
@@ -33,6 +35,7 @@ internal CorrelationIdEnricher(string headerKey, bool addValueIfHeaderAbsence, I
_contextAccessor = contextAccessor;
}
+ ///
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
var httpContext = _contextAccessor.HttpContext;
@@ -47,10 +50,27 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
return;
}
- var header = httpContext.Request.Headers[_headerKey].ToString();
- var correlationId = !string.IsNullOrWhiteSpace(header)
- ? header
- : (_addValueIfHeaderAbsence ? Guid.NewGuid().ToString() : null);
+ var requestHeader = httpContext.Request.Headers[_headerKey];
+ var 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;
+ }
var correlationIdProperty = new LogEventProperty(PropertyName, new ScalarValue(correlationId));
logEvent.AddOrUpdateProperty(correlationIdProperty);
diff --git a/src/Serilog.Enrichers.ClientInfo/Extensions/ClientInfoLoggerConfigurationExtensions.cs b/src/Serilog.Enrichers.ClientInfo/Extensions/ClientInfoLoggerConfigurationExtensions.cs
index 8239a05..a2b243a 100644
--- a/src/Serilog.Enrichers.ClientInfo/Extensions/ClientInfoLoggerConfigurationExtensions.cs
+++ b/src/Serilog.Enrichers.ClientInfo/Extensions/ClientInfoLoggerConfigurationExtensions.cs
@@ -1,15 +1,7 @@
-using Serilog.Configuration;
+using Microsoft.AspNetCore.Http;
+using Serilog.Configuration;
using Serilog.Enrichers;
using System;
-using System.Web;
-
-#if NETFULL
-
-using Serilog.Enrichers.ClientInfo.Accessors;
-
-#else
-using Microsoft.AspNetCore.Http;
-#endif
namespace Serilog;
@@ -38,7 +30,7 @@ public static LoggerConfiguration WithClientIp(
throw new ArgumentNullException(nameof(enrichmentConfiguration));
}
- return enrichmentConfiguration.With(new ClientIpEnricher(headerName));
+ return enrichmentConfiguration.With();
}
///
diff --git a/src/Serilog.Enrichers.ClientInfo/Serilog.Enrichers.ClientInfo.csproj b/src/Serilog.Enrichers.ClientInfo/Serilog.Enrichers.ClientInfo.csproj
index 85d1699..ed86c55 100644
--- a/src/Serilog.Enrichers.ClientInfo/Serilog.Enrichers.ClientInfo.csproj
+++ b/src/Serilog.Enrichers.ClientInfo/Serilog.Enrichers.ClientInfo.csproj
@@ -1,7 +1,7 @@
- net462;netstandard2.0;netstandard2.1;net6.0;net7.0
+ net6.0;net7.0;net8.0
Serilog.Enrichers.ClientInfo
Serilog
latest
@@ -12,30 +12,11 @@
2.0.3
-
- NETCORE;NETSTANDARD;NETSTANDARD2_0
-
-
- NETCORE;NETSTANDARD;NETSTANDARD2_1
-
-
- NET45;NETFULL
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/test/Serilog.Enrichers.ClientInfo.Tests/ClientIpEnricherTests.cs b/test/Serilog.Enrichers.ClientInfo.Tests/ClientIpEnricherTests.cs
index 186d60a..674efcc 100644
--- a/test/Serilog.Enrichers.ClientInfo.Tests/ClientIpEnricherTests.cs
+++ b/test/Serilog.Enrichers.ClientInfo.Tests/ClientIpEnricherTests.cs
@@ -8,7 +8,6 @@ namespace Serilog.Enrichers.ClientInfo.Tests;
public class ClientIpEnricherTests
{
- private const string ForwardHeaderKey = "x-forwarded-for";
private readonly IHttpContextAccessor _contextAccessor;
public ClientIpEnricherTests()
@@ -29,7 +28,7 @@ public void EnrichLogWithClientIp_ShouldCreateClientIpPropertyWithValue(string i
var ipAddress = IPAddress.Parse(ip);
_contextAccessor.HttpContext.Connection.RemoteIpAddress = ipAddress;
- var ipEnricher = new ClientIpEnricher(ForwardHeaderKey, _contextAccessor);
+ var ipEnricher = new ClientIpEnricher(_contextAccessor);
LogEvent evt = null;
var log = new LoggerConfiguration()
@@ -51,7 +50,7 @@ public void EnrichLogWithClientIp_WhenLogMoreThanOnce_ShouldReadClientIpValueFro
{
//Arrange
_contextAccessor.HttpContext.Connection.RemoteIpAddress = IPAddress.Loopback;
- var ipEnricher = new ClientIpEnricher(ForwardHeaderKey, _contextAccessor);
+ var ipEnricher = new ClientIpEnricher(_contextAccessor);
LogEvent evt = null;
var log = new LoggerConfiguration()
@@ -69,60 +68,6 @@ public void EnrichLogWithClientIp_WhenLogMoreThanOnce_ShouldReadClientIpValueFro
Assert.Equal(IPAddress.Loopback.ToString(), evt.Properties["ClientIp"].LiteralValue());
}
- [Theory]
- [InlineData("::1")]
- [InlineData("192.168.1.1")]
- [InlineData("2001:0db8:85a3:0000:0000:8a2e:0370:7334")]
- [InlineData("2001:db8:85a3:8d3:1319:8a2e:370:7348")]
- public void EnrichLogWithClientIp_WhenRequestContainForwardHeader_ShouldCreateClientIpPropertyWithValue(string ip)
- {
- //Arrange
- var ipAddress = IPAddress.Parse(ip);
- _contextAccessor.HttpContext.Connection.RemoteIpAddress = IPAddress.Loopback;
- _contextAccessor.HttpContext.Request.Headers.Add(ForwardHeaderKey, ipAddress.ToString());
-
- var ipEnricher = new ClientIpEnricher(ForwardHeaderKey, _contextAccessor);
-
- LogEvent evt = null;
- var log = new LoggerConfiguration()
- .Enrich.With(ipEnricher)
- .WriteTo.Sink(new DelegatingSink(e => evt = e))
- .CreateLogger();
-
- // Act
- log.Information(@"Has an IP property");
-
- // Assert
- Assert.NotNull(evt);
- Assert.True(evt.Properties.ContainsKey("ClientIp"));
- Assert.Equal(ipAddress.ToString(), evt.Properties["ClientIp"].LiteralValue());
- }
-
- [Fact]
- public void EnrichLogWithClientIp_WithCustomForwardHeaderAndRequest_ShouldCreateClientIpPropertyWithValue()
- {
- //Arrange
- const string customForwardHeader = "CustomForwardHeader";
- _contextAccessor.HttpContext.Connection.RemoteIpAddress = IPAddress.Loopback;
- _contextAccessor.HttpContext.Request.Headers.Add(customForwardHeader, IPAddress.Broadcast.ToString());
-
- var ipEnricher = new ClientIpEnricher(customForwardHeader, _contextAccessor);
-
- LogEvent evt = null;
- var log = new LoggerConfiguration()
- .Enrich.With(ipEnricher)
- .WriteTo.Sink(new DelegatingSink(e => evt = e))
- .CreateLogger();
-
- // Act
- log.Information(@"Has an IP property");
-
- // Assert
- Assert.NotNull(evt);
- Assert.True(evt.Properties.ContainsKey("ClientIp"));
- Assert.Equal(IPAddress.Broadcast.ToString(), evt.Properties["ClientIp"].LiteralValue());
- }
-
[Fact]
public void WithClientIp_ThenLoggerIsCalled_ShouldNotThrowException()
{
diff --git a/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs b/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs
index ae35f1d..093dc01 100644
--- a/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs
+++ b/test/Serilog.Enrichers.ClientInfo.Tests/CorrelationIdEnricherTests.cs
@@ -108,6 +108,54 @@ public void EnrichLogWithCorrelationId_WhenHttpRequestNotContainCorrelationHeade
Assert.NotNull(evt.Properties[LogPropertyName].LiteralValue().ToString());
}
+ [Fact]
+ public void EnrichLogWithCorrelationId_WhenHttpResponseContainsCorrelationIdHeader_ShouldCreateCorrelationIdProperty()
+ {
+ // Arrange
+ var correlationId = Guid.NewGuid().ToString();
+ _contextAccessor.HttpContext.Response.Headers.Add(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.");
+
+ // Assert
+ Assert.NotNull(evt);
+ Assert.True(evt.Properties.ContainsKey(LogPropertyName));
+ Assert.Equal(correlationId, evt.Properties[LogPropertyName].LiteralValue().ToString());
+ }
+
+ [Fact]
+ public void EnrichLogWithCorrelationId_WhenHttpRequestAndResponseContainCorrelationIdHeader_ShouldCreateCorrelationIdPropertyFromHttpRequest()
+ {
+ // Arrange
+ var requestCorrelationId = Guid.NewGuid().ToString();
+ var responseCorrelationId = Guid.NewGuid().ToString();
+ _contextAccessor.HttpContext.Request.Headers.Add(HeaderKey, requestCorrelationId);
+ _contextAccessor.HttpContext.Response.Headers.Add(HeaderKey, responseCorrelationId);
+ 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.");
+
+ // Assert
+ Assert.NotNull(evt);
+ Assert.True(evt.Properties.ContainsKey(LogPropertyName));
+ Assert.Equal(requestCorrelationId, evt.Properties[LogPropertyName].LiteralValue().ToString());
+ }
+
[Fact]
public void WithClientIp_ThenLoggerIsCalled_ShouldNotThrowException()
{
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 2266178..6974c71 100644
--- a/test/Serilog.Enrichers.ClientInfo.Tests/Serilog.Enrichers.ClientInfo.Tests.csproj
+++ b/test/Serilog.Enrichers.ClientInfo.Tests/Serilog.Enrichers.ClientInfo.Tests.csproj
@@ -1,19 +1,19 @@
- net7.0
+ net8.0
false
-
-
-
-
+
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all