Seriloog / SeriLoog / Extensions / HttpContextEnricher.cs
Code · 54 lines · 2038 bytes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54using Microsoft.AspNetCore.Http.Features;
using Microsoft.Net.Http.Headers;
using Serilog.Core;
using Serilog.Events;

namespace SeriLoog.Extensions;

/// <summary>
/// Enriches every log event with request context, but only when one actually
/// exists (i.e. we're inside an HTTP request). Background work, startup logs
/// and anything logged outside a request simply won't get these properties.
/// </summary>
public sealed class HttpContextEnricher : ILogEventEnricher
{
	private readonly IHttpContextAccessor _httpContextAccessor;

	public HttpContextEnricher(IHttpContextAccessor httpContextAccessor)
	{
		_httpContextAccessor = httpContextAccessor;
	}

	public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
	{
		var context = _httpContextAccessor.HttpContext;
		if (context is null)
			return;

		var request = context.Request;

		var ip = context.Connection.RemoteIpAddress?.ToString() ?? "unknown";
		var forwarded = request.Headers["X-Forwarded-For"].FirstOrDefault();
		if (!string.IsNullOrEmpty(forwarded))
		{
			ip = forwarded.Split(',').First().Trim();
		}

		var sessionId = context.Features.Get<ISessionFeature>() is not null
			? context.Session.Id
			: string.Empty;

		Set(logEvent, propertyFactory, "ClientIp", ip);
		Set(logEvent, propertyFactory, "RequestUrl", $"{request.Scheme}://{request.Host}{request.Path}{request.QueryString}");
		Set(logEvent, propertyFactory, "TraceId", context.TraceIdentifier);
		Set(logEvent, propertyFactory, "Referer", request.Headers[HeaderNames.Referer].FirstOrDefault() ?? string.Empty);
		Set(logEvent, propertyFactory, "UserAgent", request.Headers[HeaderNames.UserAgent].FirstOrDefault() ?? string.Empty);
		Set(logEvent, propertyFactory, "SessionId", sessionId);
		Set(logEvent, propertyFactory, "User", context.User?.Identity?.Name ?? "anonymous");
	}

	private static void Set(LogEvent logEvent, ILogEventPropertyFactory propertyFactory, string name, string value)
	{
		logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty(name, value));
	}
}