'Serilog: Create and track a unique log context for each request?
How would one go about preserving a unique log context for each request in a ASP.NET Core MVC application?
I've been looking for some time at how I might accomplish this. I can only assume I'm missing something obvious.
Basically, for instance, let's say I have an endpoint that accepts some payload. It then pushes properties to the logging context to enrich the logs (either from the payload or retrieved from a repository while processing the request, etc).
What I want to ensure is that if I have multiple asynchronous HTTP requests coming in that the context is unique to that request and discarded when the request is finished processing so things don't get mixed up.
Is there a way to accomplish this? Is it already considered? I'm setting up Serilog in the Main method and adding it in the WebHostBuilder. I'd imagine I need to do some kind of DI, but I don't want to lose the logs that are output before the MVC application completely starts.
EDIT: how it's currently being setup
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration)
.CreateLogger();
try
{
Log.Information("Starting web host...");
BuildWebHost(args).Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
}
private static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseSerilog()
.UseKestrel()
.UseUrls("http://*:5000")
.UseStartup<Startup>()
.Build();
Solution 1:[1]
I use middleware for this functionality in one of my projects:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
public class LoggingEnrichmentMiddleware
{
private readonly ILogger<LoggingEnrichmentMiddleware> _logger;
private readonly RequestDelegate _next;
public LoggingEnrichmentMiddleware(
RequestDelegate next,
ILogger<LoggingEnrichmentMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
using (_logger.BeginScope(
"{Method} {Path} {Query} {RemoteIp} {LocalIp}",
context.Request.Method,
context.Request.Path,
context.Request.QueryString,
context.Connection.RemoteIpAddress,
context.Connection.LocalIpAddress))
{
await _next(context);
}
}
}
Be sure to add the middleware in your Startup.Configure method:
app.UseMiddleware<LoggingEnrichmentMiddleware>();
Solution 2:[2]
Another way is to write your own Enricher by implementing ILogEventEnricher. Enricher is the feature that will add your custom properties to every LogEvent (or everytimes you log, also other code that log eg. framework or library you use).
You can have a look at these examples (it's already simple one)
- EnvironmentNameEnricher - Simple one
- ClientIpEnricher - in case you want to access HttpContext from your Enricher. This is the one that you wanna use because you want per HttpRequest.
Then add to Serilog at configuration-time.
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration)
.WriteTo.Console()
.Enrich.With<EnvironmentNameEnricher>() // Replace with your enricher class name
.CreateLogger();
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | wanton |
| Solution 2 | N. Wigi |
