Enriching Serilog Output with HttpContext Information in ASP.NET Core v1.0
I was working a bit with ASP.NET Core this week and I learned an interesting thing about HttpContext in this new world. Older versions of HttpContext provided the static property "Current" which, consistent with the nature of a static property, you could access from anywhere in the application. In ASP.NET Core this was replaced by IHttpContextAccessor. I think this is a good thing since I am, personally, not a huge fan of accessing things I don't inject myself. However...
I learned this because I was attempting to create an outputTemplate in Serilog that would decorate every log entry with the ID of the current session using a custom Enricher like this one here: https://github.com/serilog-web/classic/blob/master/src/SerilogWeb.Classic/Classic/Enrichers/HttpSessionIdEnricher.cs. My head dropped with a big DUH! when I realized I couldn't access HttpContext directly. Soon enough, the minor defeat gave way to the "what I am going to do now" question and I dug a bit deeper.
My immediate thought was to use IHttpContextAccessor since it is advertised as HttpContext.Current's hipper replacement. However, I hit a roadblock (probably just a mental one) when I remembered that the Serilog logger was being configured in the Startup class's constructor, prior to registering services where I'd have stuff to inject, meaning I would not have access to IHttpContextAccessor without changing around how and where Serilog was being configured, which I didn't want to do. A real pickle right?
Luckily enough I found that there is a builtin fixture to Serilog called LogContext which allows you to push and pop values to create an enriched context for logging (aptly named, am I right?). If you combine this tool with some custom ASP.NET Middleware (which I learned to do from this post: https://www.exceptionnotfound.net/writing-custom-middleware-in-asp-net-core-1-0/) you can easily enrich a Serilog log with information from the HttpContext.
To demonstrate, let's suppose you want to log the address of every incoming request in every log. You may setup the Log in Serilog like this:
Serilog will look for a "Address" value to replace "{Address}" with in the the log, which is what we want, but we have to provide this to it. This is done via middleware and LogContext like so:
Now if you add a logging call to your controller you can see the IP address is populated in log as expected. Here's a screenshot of my own tribulation proving this works:
Take a look at my full example on GitHub here: https://github.com/srakowski/serilog-httpcontext-example
I'm guessing there is a better way to do this, but, hey, it works! Thanks for reading :)