Skip to content

Commit 13dc0f3

Browse files
authored
Merge pull request #22 from serilog-contrib/correlationid-header-enricher
CorrelationId and RequestHeader enrichers are added.
2 parents d1fa0e5 + 0e2673b commit 13dc0f3

17 files changed

+741
-347
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,3 +328,5 @@ ASALocalRun/
328328

329329
# MFractors (Xamarin productivity tool) working folder
330330
.mfractor/
331+
332+
.vshistory/

README.md

Lines changed: 86 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# serilog-enrichers-clientinfo [![NuGet](http://img.shields.io/nuget/v/Serilog.Enrichers.ClientInfo.svg?style=flat)](https://www.nuget.org/packages/Serilog.Enrichers.ClientInfo/)
2-
Enrich logs with client IP and UserAgent.
2+
Enrich logs with client IP, Correlation Id and HTTP request headers.
33

44
Install the _Serilog.Enrichers.ClientInfo_ [NuGet package](https://www.nuget.org/packages/Serilog.Enrichers.ClientInfo/)
55

@@ -16,7 +16,8 @@ Apply the enricher to your `LoggerConfiguration` in code:
1616
```csharp
1717
Log.Logger = new LoggerConfiguration()
1818
.Enrich.WithClientIp()
19-
.Enrich.WithClientAgent()
19+
.Enrich.WithCorrelationId()
20+
.Enrich.WithRequestHeader("Header-Name1")
2021
// ...other configuration...
2122
.CreateLogger();
2223
```
@@ -27,22 +28,30 @@ or in `appsettings.json` file:
2728
"Serilog": {
2829
"MinimumLevel": "Debug",
2930
"Using": [ "Serilog.Enrichers.ClientInfo" ],
30-
"Enrich": [ "WithClientIp", "WithClientAgent"],
31+
"Enrich": [
32+
"WithClientIp",
33+
"WithCorrelationId",
34+
{
35+
"Name": "WithRequestHeader",
36+
"Args": { "headerName": "Cache-Control"}
37+
}
38+
],
3139
"WriteTo": [
3240
{ "Name": "Console" }
3341
]
3442
}
3543
}
3644
```
3745

38-
The `WithClientIp()` enricher will add a `ClientIp` property and the `WithClientAgent()` enricher will add a `ClientAgent` property to produced events.
46+
---
3947

40-
For `ClientIp` enricher you can configure the `X-forwarded-for` header if the proxy server uses a different header to forward IP address.
48+
For `ClientIp` enricher you can configure the `x-forwarded-for` header if the proxy server uses a different header to forward the IP address.
4149
```csharp
4250
Log.Logger = new LoggerConfiguration()
43-
.Enrich.WithClientIp(xForwardHeaderName: "CF-Connecting-IP")
51+
.Enrich.WithClientIp(headerName: "CF-Connecting-IP")
4452
...
4553
```
54+
or
4655
```json
4756
{
4857
"Serilog": {
@@ -53,14 +62,81 @@ Log.Logger = new LoggerConfiguration()
5362
{
5463
"Name": "WithClientIp",
5564
"Args": {
56-
"xForwardHeaderName": "CF-Connecting-IP"
65+
"headerName": "CF-Connecting-IP"
66+
}
67+
}
68+
],
69+
}
70+
}
71+
```
72+
73+
For `CorrelationId` enricher you can:
74+
- Configure the header name and default header name is `x-correlation-id`
75+
- Set value for correlation id when the header is not available in request header collection and the default value is false
76+
```csharp
77+
Log.Logger = new LoggerConfiguration()
78+
.Enrich.WithCorrelationId(headerName: "correlation-id", addValueIfHeaderAbsence: true)
79+
...
80+
```
81+
or
82+
```json
83+
{
84+
"Serilog": {
85+
"MinimumLevel": "Debug",
86+
"Using": [ "Serilog.Enrichers.ClientInfo" ],
87+
"Enrich": [
88+
"WithClientAgent",
89+
{
90+
"Name": "WithCorrelationId",
91+
"Args": {
92+
"headerName": "correlation-id"
93+
"addValueIfHeaderAbsence": true
94+
}
95+
}
96+
],
97+
}
98+
}
99+
```
100+
101+
You can use multiple `WithRequestHeader` to log different request headers.
102+
```csharp
103+
Log.Logger = new LoggerConfiguration()
104+
.Enrich.WithRequestHeader(headerName: "header-name-1")
105+
.Enrich.WithRequestHeader(headerName: "header-name-2")
106+
...
107+
```
108+
or
109+
```json
110+
{
111+
"Serilog": {
112+
"MinimumLevel": "Debug",
113+
"Using": [ "Serilog.Enrichers.ClientInfo" ],
114+
"Enrich": [
115+
{
116+
"Name": "WithRequestHeader",
117+
"Args": {
118+
"headerName": "Cache-Control"
119+
}
120+
},
121+
{
122+
"Name": "WithRequestHeader",
123+
"Args": {
124+
"headerName": "Connection"
57125
}
58126
}
59127
],
60128
}
61129
}
62130
```
63131

132+
#### Note
133+
To include logged headers in `OutputTemplate`, the header name without `-` should be used. For example, if the header name is `Cache-Contol`, you should use `CacheContol`.
134+
```csharp
135+
Log.Logger = new LoggerConfiguration()
136+
.MinimumLevel.Debug()
137+
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss}] {Level:u3} {CacheContol} {Message:lj}{NewLine}{Exception}")
138+
```
139+
64140
## Installing into an ASP.NET Core Web Application
65141
You need to register the `IHttpContextAccessor` singleton so the enrichers have access to the requests `HttpContext` to extract client IP and client agent.
66142
This is what your `Startup` class should contain in order for this enricher to work as expected:
@@ -81,9 +157,10 @@ namespace MyWebApp
81157
{
82158
Log.Logger = new LoggerConfiguration()
83159
.MinimumLevel.Debug()
84-
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3} {ClientIp} {ClientAgent}] {Message:lj}{NewLine}{Exception}")
160+
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss}] {Level:u3} CLient IP: {ClientIp} Correlation Id: {CorrelationId} header-name: {headername} {Message:lj}{NewLine}{Exception}")
85161
.Enrich.WithClientIp()
86-
.Enrich.WithClientAgent()
162+
.Enrich.WithCorrelationId()
163+
.Enrich.WithRequestHeader("header-name")
87164
.CreateLogger();
88165
}
89166

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,37 @@
11
<?xml version="1.0"?>
22
<package>
3-
<metadata>
4-
<id>Serilog.Enrichers.ClientInfo</id>
5-
<version>$version$</version>
6-
<title>Serilog.Enrichers.ClientInfo</title>
7-
<authors>mo.esmp</authors>
8-
<owners>mo.esmp</owners>
9-
<projectUrl>https://github.com/mo-esmp/serilog-enrichers-clientinfo</projectUrl>
10-
<license type="expression">MIT</license>
11-
<requireLicenseAcceptance>false</requireLicenseAcceptance>
12-
<description>Enrich logs with client IP and UserAgent..</description>
13-
<readme>docs\README.md</readme>
14-
<releaseNotes></releaseNotes>
15-
<copyright></copyright>
16-
<tags>serilog enrichers enricher ip useragent user-agent</tags>
17-
<dependencies>
18-
<group targetFramework="net462">
19-
<dependency id="Serilog" version="2.4.0" />
20-
</group>
21-
<group targetFramework="netstandard2.0">
22-
<dependency id="Microsoft.AspNetCore.Http" version="2.1.1" />
23-
<dependency id="Serilog" version="2.7.1" />
24-
</group>
25-
<group targetFramework="netstandard2.1">
26-
<dependency id="Microsoft.AspNetCore.Http" version="2.2.2" />
27-
<dependency id="Serilog" version="2.9.0" />
28-
</group>
29-
</dependencies>
30-
</metadata>
31-
<files>
32-
<file src="src\Serilog.Enrichers.ClientInfo\bin\$target$\net462\Serilog.Enrichers.ClientInfo.dll" target="lib/net462" />
33-
<file src="src\Serilog.Enrichers.ClientInfo\bin\$target$\netstandard2.0\Serilog.Enrichers.ClientInfo.dll" target="lib/netstandard2.0" />
34-
<file src="src\Serilog.Enrichers.ClientInfo\bin\$target$\netstandard2.1\Serilog.Enrichers.ClientInfo.dll" target="lib/netstandard2.1" />
35-
<file src="README.md" target="docs\" />
36-
</files>
3+
<metadata>
4+
<id>Serilog.Enrichers.ClientInfo</id>
5+
<version>$version$</version>
6+
<title>Serilog.Enrichers.ClientInfo</title>
7+
<authors>mo.esmp</authors>
8+
<owners>mo.esmp</owners>
9+
<projectUrl>https://github.com/mo-esmp/serilog-enrichers-clientinfo</projectUrl>
10+
<license type="expression">MIT</license>
11+
<requireLicenseAcceptance>false</requireLicenseAcceptance>
12+
<description>Enrich logs with client IP, CorrelationId and HTTP request headers.</description>
13+
<readme>docs\README.md</readme>
14+
<releaseNotes></releaseNotes>
15+
<copyright></copyright>
16+
<tags>serilog enrichers enricher ip correlation-id request header</tags>
17+
<dependencies>
18+
<group targetFramework="net462">
19+
<dependency id="Serilog" version="2.4.0" />
20+
</group>
21+
<group targetFramework="netstandard2.0">
22+
<dependency id="Microsoft.AspNetCore.Http" version="2.1.1" />
23+
<dependency id="Serilog" version="2.7.1" />
24+
</group>
25+
<group targetFramework="netstandard2.1">
26+
<dependency id="Microsoft.AspNetCore.Http" version="2.2.2" />
27+
<dependency id="Serilog" version="2.9.0" />
28+
</group>
29+
</dependencies>
30+
</metadata>
31+
<files>
32+
<file src="src\Serilog.Enrichers.ClientInfo\bin\release\net462\Serilog.Enrichers.ClientInfo.dll" target="lib/net462" />
33+
<file src="src\Serilog.Enrichers.ClientInfo\bin\release\netstandard2.0\Serilog.Enrichers.ClientInfo.dll" target="lib/netstandard2.0" />
34+
<file src="src\Serilog.Enrichers.ClientInfo\bin\release\netstandard2.1\Serilog.Enrichers.ClientInfo.dll" target="lib/netstandard2.1" />
35+
<file src="README.md" target="docs\" />
36+
</files>
3737
</package>

Serilog.Enrichers.ClientInfo.sln

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio Version 16
4-
VisualStudioVersion = 16.0.29411.108
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.1.32210.238
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{84342DE9-9E61-4802-9E77-305CFFD0D2B5}"
77
EndProject
88
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{F5A0A932-DD02-47C1-B81E-3F34F879FEEF}"
99
EndProject
1010
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Enrichers.ClientInfo", "src\Serilog.Enrichers.ClientInfo\Serilog.Enrichers.ClientInfo.csproj", "{232EFC21-2E8D-44B0-8506-C0DCE122AAA2}"
1111
EndProject
12-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serilog.Enrichers.ClientInfo.Tests", "test\Serilog.Enrichers.ClientInfo.Tests\Serilog.Enrichers.ClientInfo.Tests.csproj", "{A6AFC898-47F8-4F6D-9391-C79689885B7A}"
12+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Enrichers.ClientInfo.Tests", "test\Serilog.Enrichers.ClientInfo.Tests\Serilog.Enrichers.ClientInfo.Tests.csproj", "{A6AFC898-47F8-4F6D-9391-C79689885B7A}"
13+
EndProject
14+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{931596BA-056D-4E8C-9FED-6C60C0D1AB83}"
15+
ProjectSection(SolutionItems) = preProject
16+
.gitignore = .gitignore
17+
LICENSE = LICENSE
18+
README.md = README.md
19+
Serilog.Enrichers.ClientInfo.nuspec = Serilog.Enrichers.ClientInfo.nuspec
20+
EndProjectSection
1321
EndProject
1422
Global
1523
GlobalSection(SolutionConfigurationPlatforms) = preSolution

src/Serilog.Enrichers.ClientInfo/Enrichers/ClientAgentEnricher.cs

Lines changed: 0 additions & 50 deletions
This file was deleted.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using Serilog.Core;
2+
using Serilog.Events;
3+
4+
#if NETFULL
5+
6+
using Serilog.Enrichers.ClientInfo.Accessors;
7+
8+
#else
9+
using Microsoft.AspNetCore.Http;
10+
#endif
11+
12+
namespace Serilog.Enrichers;
13+
14+
/// <inheritdoc/>
15+
public class ClientHeaderEnricher : ILogEventEnricher
16+
{
17+
private readonly string _clientHeaderItemKey;
18+
private readonly string _propertyName;
19+
private readonly string _headerKey;
20+
private readonly IHttpContextAccessor _contextAccessor;
21+
22+
public ClientHeaderEnricher(string headerKey)
23+
: this(headerKey, new HttpContextAccessor())
24+
{
25+
}
26+
27+
internal ClientHeaderEnricher(string headerKey, IHttpContextAccessor contextAccessor)
28+
{
29+
_headerKey = headerKey;
30+
_propertyName = headerKey.Replace("-", "");
31+
_clientHeaderItemKey = $"Serilog_{headerKey}";
32+
_contextAccessor = contextAccessor;
33+
}
34+
35+
internal ClientHeaderEnricher(IHttpContextAccessor contextAccessor)
36+
{
37+
_contextAccessor = contextAccessor;
38+
}
39+
40+
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
41+
{
42+
var httpContext = _contextAccessor.HttpContext;
43+
if (httpContext == null)
44+
return;
45+
46+
if (httpContext.Items[_clientHeaderItemKey] is LogEventProperty logEventProperty)
47+
{
48+
logEvent.AddPropertyIfAbsent(logEventProperty);
49+
return;
50+
}
51+
52+
var headerValue = httpContext.Request.Headers[_headerKey].ToString();
53+
headerValue = string.IsNullOrWhiteSpace(headerValue) ? null : headerValue;
54+
55+
var logProperty = new LogEventProperty(_propertyName, new ScalarValue(headerValue));
56+
httpContext.Items.Add(_clientHeaderItemKey, logProperty);
57+
58+
logEvent.AddPropertyIfAbsent(logProperty);
59+
}
60+
}

src/Serilog.Enrichers.ClientInfo/Enrichers/ClientIpEnricher.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,18 @@ public class ClientIpEnricher : ILogEventEnricher
1919
{
2020
private const string IpAddressPropertyName = "ClientIp";
2121
private const string IpAddressItemKey = "Serilog_ClientIp";
22+
private readonly string _forwardHeaderKey;
2223

2324
private readonly IHttpContextAccessor _contextAccessor;
2425

25-
public ClientIpEnricher()
26+
public ClientIpEnricher(string forwardHeaderKey)
27+
: this(forwardHeaderKey, new HttpContextAccessor())
2628
{
27-
_contextAccessor = new HttpContextAccessor();
2829
}
2930

30-
internal ClientIpEnricher(IHttpContextAccessor contextAccessor)
31+
internal ClientIpEnricher(string forwardHeaderKey, IHttpContextAccessor contextAccessor)
3132
{
33+
_forwardHeaderKey = forwardHeaderKey;
3234
_contextAccessor = contextAccessor;
3335
}
3436

@@ -69,7 +71,7 @@ private string GetIpAddress()
6971
#else
7072
private string GetIpAddress()
7173
{
72-
var ipAddress = _contextAccessor.HttpContext?.Request?.Headers[ClinetIpConfiguration.XForwardHeaderName].FirstOrDefault();
74+
var ipAddress = _contextAccessor.HttpContext?.Request?.Headers[_forwardHeaderKey].FirstOrDefault();
7375

7476
return !string.IsNullOrEmpty(ipAddress)
7577
? GetIpAddressFromProxy(ipAddress)

0 commit comments

Comments
 (0)