Logging in ASP.NET Core 5 using Serilog

Logging in ASP.NET Core 5 using Serilog

Serilog is one of the most famous logging libraries for .NET applications. As per NuGet, the total downloads of this library already crossed 175M which clearly shows the popularity of this library among .NET developers. It is easy to set up and provides a huge collection of powerful features. It is portable between different .NET platforms and also provides a clean API. It has many extension points that allow developers to plug their custom code into it and extend the functionality of this cool library even further. In this tutorial, I will show you how to set up this library and start using it within minutes. I will also cover how you can log output in text and JSON format and at the end of this tutorial, I will show you how you can log messages to text based log files.

If you are not familiar with the .NET built-in logging framework then you can read my Step by Step Guide To Logging in ASP.NET Core 5

What is Serilog?

Serilog is a structural logging library for.NET applications that can be used for adding some cool diagnostic features to your application. This library provides a huge collection of new logging related features that are not available in the .NET built-in logging framework. It allows developers to log their messages to hundreds of different destinations including files, the console, on-premises, and cloud-based log servers, databases, and message queues. It also has native support of producing log output in plain text and JSON formats. It supports rich integration with .NET Core including ASP.NET Core. You can read the full list of features available here.

Structured Logging

Structured logging is the practice of implementing a consistent, predetermined message format for application logs that allows them to be treated as data sets rather than text. The idea of structured logging is to take an application log that is delivered as a string of text and convert it into a simple relational data set that can be more easily searched and analyzed.

source: DevOps Glossary by sumo logic

Introducing Serilog Sinks

Serilog Sinks are packages used to write log events to various formats. There is a long list of sinks developed and supported by a big Serilog community. Some of the popular sinks are the following:

READ ALSO:  A Developer Guide to ASP.NET Core Tag Helpers

The list of available sinks is huge and you can check all available sinks here.

Getting Started

Let’s begin by creating a “Services” folder in your project and then add the following IMathService interface inside the Services folder.

public interface IMathService
{
    decimal Divide(decimal a, decimal b);
}

Next, add a MatchService class in the same Services folder and implement IMathService method on this class. You can see that the class is not only providing the definition of the Divide method but also has some basic logging and error handling statements in it.

If you are not familiar with the .NET built-in logging then you can read my step by step guide about .NET logging A Step by Step Guide to Logging in ASP.NET Core 5

public class MathService : IMathService
{
    private readonly ILogger<MathService> _logger;

    public MathService(ILogger<MathService> logger)
    {
        _logger = logger;
    }

    public decimal Divide(decimal a, decimal b)
    {
        _logger.LogInformation("Parameter 1: " + a);
        _logger.LogInformation("Parameter 2: " + b);

        decimal result = 0;

        try
        {
            result = a / b;
        }
        catch (DivideByZeroException ex)
        {
            _logger.LogWarning(ex, "You cannot divide by zero.");
            throw ex;
        }

        return result;
    }
}

Next, we need to register our service using .NET Core built-in DI Container. If you are not familiar with the ASP.NET Core dependency injection, then read my Step by Step Guide to ASP.NET Core Dependency Injection.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();

    services.AddTransient<IMathService, MathService>();
}

Finally, I am injecting the IMathService and ILogger in my HomeController and calling the Divide method with two decimal parameters.

public class HomeController : Controller
{
    private readonly ILogger<HomeController> _logger;
    private readonly IMathService _mathService;

    public HomeController(ILogger<HomeController> logger, IMathService mathService)
    {
        _logger = logger;
        _mathService = mathService;
    }

    public IActionResult Index()
    {
        try
        {
            decimal result = _mathService.Divide(5, 0);
        }
        catch (DivideByZeroException ex)
        {
            _logger.LogWarning(ex, "An exception occured while dividing two numbers");
        }

        return View();
    }
}

As we cannot divide any decimal number to 0, so running the Index action of the HomeController will result in the following output where you can see that an exception is thrown and appropriate messages are logged from both MathService as well as HomeController.

AspNetCore5LoggingDemo.Services.MathService: Information: Parameter 1: 5
AspNetCore5LoggingDemo.Services.MathService: Information: Parameter 2: 0
…
AspNetCore5LoggingDemo.Services.MathService: Warning: You cannot divide by zero.
…
AspNetCore5LoggingDemo.Controllers.HomeController: Warning: An exception occured while dividing two numbers

Configuring Serilog with ASP.NET Core

So far, I was only using the basic .NET logging infrastructure so it is now time to configure the Serilog library. Serilog can be downloaded and installed as a NuGet package and because I will be using Serilog in ASP.NET Core application in this tutorial so I will also install Serilog.AspNetCore package. I will also install Serilog.Sinks.File sink from Nuget as I want to show you how to log events to files later in this tutorial. You can download and install all of these packages using the NuGet Package Manager available in Visual Studio.

READ ALSO:  A Step by Step Guide to ASP.NET Core Dependency Injection
Download and Install Serilog from NuGet

The first thing you need to do is to remove the built-in .NET logging configuration from the appsettings.json file. This is because the Serilog uses a simple C# API to configure logging. If you still want to use an external appsettings.json file to configure Serilog then you have to download and use Serilog.Settings.AppSettings (.NET Framework) or Serilog.Settings.Configuration (.NET Core) packages. For now, I will configure Serilog using C# code so I am commenting on the following “Logging” section from the project appsettings.json file.

{
  //"Logging": {
  //  "LogLevel": {
  //    "Default": "Warning",
  //    "Microsoft": "Warning",
  //    "Microsoft.Hosting.Lifetime": "Information"
  //  }
  //},

  "AllowedHosts": "*"
}

Next, we need to create a Serilog logger using LoggerConfiguration class and the easiest way to do this is to use Serilog’s global, statically accessible logger “Log.Logger”. Open the Program.cs file and configure the Serilog logger as shown in the code snippet below. You also need to add UseSerilog to the Generic Host in CreateHostBuilder method.

public class Program
{
    public static void Main(string[] args)
    {
        Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Information()
            .WriteTo.Debug()
            .CreateLogger();

        try
        {
            Log.Information("Starting Web Host");
            CreateHostBuilder(args).Build().Run();
        }
        catch (Exception ex)
        {
            Log.Fatal(ex, "Host terminated unexpectedly");
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .UseSerilog()
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

This is all you need to configure Serilog in the ASP.NET Core project. You don’t need to change your Controllers, Services, or application code. If you will run the project now, you will see the following log messages in the “Debug” window.

[13:06:51 INF] Starting Web Host
…
[13:09:37 INF] Parameter 1: 5
[13:09:37 INF] Parameter 2: 0
…
[13:09:37 WRN] You cannot divide by zero.
…
[13:09:37 WRN] An exception occured while dividing two numbers

Logging output in JSON format

Some of the common sinks such as Console, Debug, and File have native support of JSON formatted output and you can also add this support by including Serilog.Formatting.Compact package. To write log messages in JSON format, you can either pass a CompactJsonFormatter or RenderedCompactJsonFormatter to the sink configuration method as shown in the code snippet below:

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Information()
    .WriteTo.Debug(new RenderedCompactJsonFormatter())
    .CreateLogger();

Now run the project once again and this time you will notice that log messages or exceptions are all logged in JSON format.

{
   "@t":"2020-12-22T08:14:21.7606275Z",
   "@m":"Parameter 1: 5",
   "@i":"aeda7d94",
   "SourceContext":"AspNetCore5LoggingDemo.Services.MathService",
   "ActionId":"bc37216c-cc3d-4a71-b99d-e9b9b350de98",
   "ActionName":"AspNetCore5LoggingDemo.Controllers.HomeController.Index (AspNetCore5LoggingDemo)",
   "RequestId":"8000008c-0002-f700-b63f-84710c7967bb",
   "RequestPath":"/AspNetCore5LoggingDemo"
}

Logging messages to Files

You can also log messages to files using Serilog.Sinks.File package. To make sure you don’t bring down other apps by filling the disk space, the default file size limit is set to 1 GB. Once the limit is reached, no further events will be written until the next roll point. There are different ways to configure rolling policies. For example, if you want to create one log file per hour, day, or some other period, you can specify the rollingInterval as shown below:

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Information()
    .WriteTo.Debug(new RenderedCompactJsonFormatter())
    .WriteTo.File("logs.txt", rollingInterval: RollingInterval.Day)
    .CreateLogger();

Now run the project and you will see a log file with today’s date appended in the file name e.g. logs20201226.txt will be generated.

READ ALSO:  Creating Custom Tag Helpers in ASP.NET Core
Logging output in file using Serilog File Sink

Summary

Serilog is a huge library and we can easily write a whole book to cover all its features. If you haven’t used Serilog before then I will recommend you to start using it in your projects for all of your diagnostics related requirements.

Leave a Reply