Today there seems to be a lot of "adapting" that must be done to hook into the logging APIs exposed in the different platforms. Logging, while straight forward (to me anyway), can often require composing various bits together to produce the "right setup"; in this case Service Fabric and Stateless/Stateful Service logging. This example aims to demonstrate one way hooking things up. My intention is to jog your mind toward laying out a full fledged implementation of your own.
- ServiceFabric (Service Fabric Service)
- ASP.NET Core (Weblistener) targeting .NET Framework 4.6.1
- Serilog
- EventFlow (The Microsoft Diagnostic one; not to be confused with the component for event sourcing)
- Elasticsearch + Kibana
The "root" for the logging is Serilog. I chose Serilog because of the following:
- Decouple logging from ASP.NET Core
- Flexible enrichment capabilities
- Configurable through code and XML/JSON configuration
Elasticsearch gave the most open ended indexing capabilities, and didn't "artificially" limit the amount of events pushed to the data store. Need more capacity? Then increase the ES deployment capacity.
Deployment of Elasticsearch and Kibana is on a single VM using Docker. I borrowed the Resource Manager template directly from azure-quickstart-templates
- Serilog
- Serilog.Extensions.Logging
- Serilog.Settings.Configuration
- Serilog.Sinks.Literate
- Microsoft.Diagnostics.EventFlow
- Microsoft.Diagnostics.EventFlow.ServiceFabric
- Microsoft.Diagnostics.EventFlow.Inputs.Etw
- Microsoft.Diagnostics.EventFlow.Inputs.EventSource
- Microsoft.Diagnostics.EventFlow.Inputs.Serilog
- Microsoft.Diagnostics.EventFlow.Outputs.ElasticSearch
- Microsoft.Diagnostics.EventFlow.Outputs.StdOutput
When using Serilog for a SF Service, do NOT use the "global" Log.Logger. As noted here:
Service Fabric can host multiple instances of the same service type within a single process. If you use the static Log.Logger, the last writer of the property enrichers will show values for all instances that are running. This is one reason why the _logger variable is a private member variable of the service class. Also, you must make the _logger available to common code, which might be used across services.
It's not complex to figure out how NOT to do this. However, the code in LoggingConfigurator and Program show how I did this.
Logging's cross-cutting concern should guide you away from coupling your code to anything ASP.NET Core.
But given we're trying to capture all HTTP requests going through it, Serilog is adapted into it, and logging Middleware was created. The Middleware does the following:
- Logs all HTTP requests (any verb) including the request/response payload
- Logs exceptions occuring in any controller
- Centralizes the hook into handling exceptions and setting the appropriate status code: 403, 404, 500, etc.
Credit to Nicholas Blumhardt's post for Seq on the Middleware groundwork it laid for me.