ASP.NET Core 2.0 Structured Logging

Problem

How to work with structured logging in ASP.NET Core and Serilog

Solution

Starting from the previous post on logging, add NuGet packages:

  • Serilog.AspNetCore
  • Serilog.Sinks.Literate
  • Serilog.Sinks.Seq

In Program.cs, configure Serilog using its LoggerConfiguration class and storing an instance of ILogger (returned by CreateLogger) in Serilog’s static Log class.

  1. public static void Main(string[] args)  
  2.       {  
  3.           Log.Logger = new LoggerConfiguration()  
  4.                       .WriteTo.LiterateConsole()  
  5.                       .CreateLogger();  
  6.   
  7.          BuildWebHost(args).Run();  
  8.       }  
  9.   
  10.       public static IWebHost BuildWebHost(string[] args) =>  
  11.           WebHost.CreateDefaultBuilder(args)  
  12.               .UseStartup<Startup>()  
  13.               .UseSerilog()  
  14.               .Build();  

Using the ILogger is the same process as described in our previous post, however, with Serilog we can do structured logging.

  1. public async Task Invoke(HttpContext context)  
  2.     {  
  3.         var message = new  
  4.         {  
  5.             GreetingTo = "James Bond",  
  6.             GreetingTime = "Morning",  
  7.             GreetingType = "Good"  
  8.         };  
  9.         this.logger.LogInformation("Inoke executing {@message}", message);  
  10.   
  11.         await context.Response.WriteAsync("Hello Logging!");  
  12.   
  13.         this.logger.LogInformation(  
  14.             "Inoke executed by {developer} at {time}""Tahir", DateTime.Now);  
  15.     } 

Running the application will show messages in Console window.

ASP.NET CORE

Discussion

Structured logging is a technique to include semantic information as part of the messages being logged. This helps ‘machine readability’ of these messages and tools can be written to analyze raw log messages and produce interesting information.

Serilog uses message template, similar to string.Format() in .NET. Few interesting aspects of template syntax are,

  • Use {} to enclose property names e.g. {developer} in above solution. These will be stored as metadata and can be queried using structured data storage (e.g. Seq, Azure).
  • Use @ to preserve object structure e.g. in solution above the anonymous object is serialized into JSON representation.
Enrichers

In Serilog, enrichers are used to attach information to every log event that can then be used by structured data storage (e.g. Seq, Azure) for viewing and filtering. A simple way to do this is by using .Enrich.WithProperty() when configuring Serilog,

  1. Log.Logger = new LoggerConfiguration()  
  2.                            .Enrich.WithProperty("ApiVersion""1.2.5000")  
  3.                            .WriteTo.LiterateConsole()  
  4.                            .CreateLogger();  
Context

As we saw in the previous post, a category can be attached to the logged messages, which normally is the fully qualified name of the class. This information could be used by structured data storage (e.g. Seq, Azure). Serilog provides this mechanism by attaching Context via ForContext() method,

  1. Log.Logger = new LoggerConfiguration()  
  2.                     .Enrich.WithProperty("ApiVersion""1.2.5000")  
  3.                     .WriteTo.LiterateConsole()  
  4.                     .CreateLogger()  
  5.                     .ForContext<HelloLoggingMiddleware>();  
Sinks

Sinks in Serilog refer to destination of log messages e.g. file, database or console (in our example). There are several sinks available (refer to link below). I’ll use Seq as an example sink to show how all the metadata we’ve added is available in a structured storage,

  1. Log.Logger = new LoggerConfiguration()  
  2.                           .Enrich.WithProperty("ApiVersion""1.2.5000")  
  3.                           .WriteTo.LiterateConsole()  
  4.                           .WriteTo.Seq("http://localhost:5341")  
  5.                           .CreateLogger()  
  6.                           .ForContext<HelloLoggingMiddleware>();  

ASP.NET CORE

Notice how data we added via enricher, context and custom object appears as key/value pairs. This can now be used for filtering data and creating dashboards within Seq.

Note

Refer to Seq website (link below) for installation instructions (it’s very simple!).

Source Code

GitHub

Useful Links

Serilog: https://serilog.net/
Serilog Sinks: https://github.com/serilog/serilog/wiki/Provided-Sinks
Seq: https://getseq.net/