Logging in ASP.NET Core 5 using Serilog
Tags: aspnetcore, logging, serilog, diagnostics
Published: 2021-08-23 18:12:37

[Serilog](https://serilog.net/) is one of the most famous logging libraries for .NET applications. As per [NuGet](https://www.nuget.org/packages/Serilog), 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](https://www.ezzylearning.net/tutorial/a-step-by-step-guide-to-logging-in-asp-net-core-5)

## **What is Serilog**?

[Serilog](https://serilog.net/) 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](https://github.com/serilog/serilog-aspnetcore). You can read the full list of features available [here](https://github.com/serilog/serilog).

> **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](https://www.sumologic.com/glossary/structured-logging/) _by_ [sumo logic](https://www.sumologic.com/)

## **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:

- [Serilog.Sinks.Console](https://github.com/serilog/serilog-sinks-console) – To write log events to the Windows Console or an ANSI terminal via standard output
- [Serilog.Sinks.File](https://github.com/serilog/serilog-sinks-file) – To writes log events to one or more text files
- [Serilog.Sinks.Email](https://github.com/serilog/serilog-sinks-email) – To send log events by SMTP email
- [Serilog.Sinks.MSSqlServer](https://github.com/serilog/serilog-sinks-mssqlserver) – To write log events to Microsoft SQL Server

The list of available sinks is huge and you can check all available sinks [here](https://github.com/serilog/serilog/wiki/Provided-Sinks).

## **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](https://www.ezzylearning.net/tutorial/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](https://www.ezzylearning.net/tutorial/a-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](https://www.nuget.org/packages/Serilog) package and because I will be using Serilog in ASP.NET Core application in this tutorial so I will also install [Serilog.AspNetCore](https://github.com/serilog/serilog-aspnetcore) package. I will also install [Serilog.Sinks.File](https://github.com/serilog/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.

![Download and Install Serilog from NuGet](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3fwx23ttn99hbi12laok.png)

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](https://github.com/serilog/serilog-settings-appsettings) (.NET Framework) or [Serilog.Settings.Configuration](http://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](https://github.com/serilog/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.

![Logging output in file using Serilog File Sink](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e870dufmrce8oxur8btj.png)

## **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.