Special Offer: My C#/.NET Bootcamp Course is out now. Get 10% OFF using the code FRIENDS10.

Logging is an important part of any application. As long as everything runs smoothly, you don’t need it, but when you face any issue with your application, you better have logs that help you find the problem.

Today, we will learn about Serilog and integrate it into an ASP.NET Core Web API application running on .NET 6.

Let’s take a look at Serilog.

What is Serilog?

Serilog is a .NET library that provides diagnostic logging to files, the console, and almost everywhere you would like. 

Serilog can be used in classic .NET Framework applications and for applications running on the latest and greatest .NET 6.

One of the biggest strengths of Serilog is that it has been built with structured logging in mind.

You can find more information on Serilog’s website.

Why do I use Serilog?

Now that we know more about Serilog let me tell you my personal experience with it. I used Serilog in a few applications with different logging requirements, and I was always satisfied with my results.

I cannot speak much about performance because I don’t work with large distributed ultra-scalable applications, but the performance was never an issue in my use cases.

I like Serilog’s simple integration into .NET applications, the ease of use, how simple you can start, and what more advanced features are available and can be used later in your journey.

Let’s create an application and integrate Serilog.

Creating an ASP.NET Core Web API Project

In Visual Studio 2022, we create a new project based on the ASP.NET Core Web API project template.

We choose SerilogDemo as the project name and, on the next dialog page, we stick with the defaults and use .NET 6.

We click on Create and wait until Visual Studio generated the project for us.

Install Serilog

Installing Serilog is simple. First, we open the NuGet Package Manager and search for the Serilog.AspNetCore package and install the latest stable version.

After a few seconds, Serilog is installed.

Configure Serilog-Logging in Program.cs

Next, let’s integrate Serilog into our application by registering it as a logging provider.

var logger = new LoggerConfiguration()
  .ReadFrom.Configuration(builder.Configuration)
  .Enrich.FromLogContext()
  .CreateLogger();

builder.Logging.ClearProviders();
builder.Logging.AddSerilog(logger);

I insert a code snippet that defines a new LoggerConfiguration. We configure the configuration to read the application configuration from the application builder. We also enrich Serilog with the log context.

Next, we clear all existing logging providers. The WebApplication builder adds, for example, the console logging provider, and we want to get rid of that.

Last but not least, we add Serilog to the logging providers for our application and provide the configured LoggerConfiguration object as its sole argument.

Configure Serilog in appsettings.json

We configured the logger to use the settings from the application configuration. Let’s open the appsettings.json file and configure Serilog.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Serilog": {
    "Using": [ "Serilog.Sinks.File" ],
    "MinimumLevel": {
      "Default": "Information"
    },
    "WriteTo": [
      {
        "Name": "File",
        "Args": {
          "path": "../logs/webapi-.log",
          "rollingInterval": "Day",
          "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} {CorrelationId} {Level:u3}] {Username} {Message:lj}{NewLine}{Exception}"
        }
      }
    ]
  }
}

I insert a sample configuration. Let’s take a look at it. First, there is the using property where we define an array of log targets. In Serilog, log targets are called sinks. We configure an array with a single sink, the Serilog.Sinks.File sink.

Next, we can configure log levels and, more importantly, the WriteTo section. In the WriteTo section, we configure information for the File sink. The name property with the value “File” defines that the property provided in the “Args” property will be used for the File sink.

We configure the path where our log file should be written. The rollingInterval property defines when a new file should be created. And the outputTemplate defines the structure of the log output for each log statement.

Add a Log Statement in the WeatherForecastController

Now that Serilog is configured, we open the WeatherForecastController class and insert a log statement in the Get method.

_logger.LogInformation("Weather Forecast executing...");

Now let’s start the application.

Sending a Get Request

We use Swagger to send a simple get request to the WeatherForecast controller. Open the entry in the endpoints list and click on the “Try it out” button. We click the big blue Execute button to send a request.

We receive an array with weather data.

However, let’s close the application and look at the log files.

Exploring the Log File

We open the project directory and open the logs folder. The file name contains the prefix we configured in the appsettings.json file, and the part after the dash is the current day.

With the current configuration, Serilog creates a separate file for every day. We collect all the log statements for any given day in its file.

[2022-01-22 17:15:54.496 +01:00  INF]  Now listening on: https://localhost:7068
[2022-01-22 17:15:54.520 +01:00  INF]  Now listening on: http://localhost:5068
[2022-01-22 17:15:54.524 +01:00  INF]  Application started. Press Ctrl+C to shut down.
[2022-01-22 17:15:54.524 +01:00  INF]  Hosting environment: Development
[...]
[2022-01-22 17:16:06.353 +01:00  INF]  Request starting HTTP/2 GET https://localhost:7068/WeatherForecast - -
[2022-01-22 17:16:06.359 +01:00  INF]  Executing endpoint 'SerilogDemo.Controllers.WeatherForecastController.Get (SerilogDemo)'
[2022-01-22 17:16:06.367 +01:00  INF]  Route matched with {action = "Get", controller = "WeatherForecast"}. Executing controller action with signature System.Collections.Generic.IEnumerable`1[SerilogDemo.WeatherForecast] Get() on controller SerilogDemo.Controllers.WeatherForecastController (SerilogDemo).
[2022-01-22 17:16:06.369 +01:00  INF]  Weather Forecast executing...
[2022-01-22 17:16:06.371 +01:00  INF]  Executing ObjectResult, writing value of type 'SerilogDemo.WeatherForecast[]'.
[2022-01-22 17:16:06.380 +01:00  INF]  Executed action SerilogDemo.Controllers.WeatherForecastController.Get (SerilogDemo) in 9.1166ms
[2022-01-22 17:16:06.381 +01:00  INF]  Executed endpoint 'SerilogDemo.Controllers.WeatherForecastController.Get (SerilogDemo)'
[2022-01-22 17:16:06.382 +01:00  INF]  Request finished HTTP/2 GET https://localhost:7068/WeatherForecast - - - 200 - application/json;+charset=utf-8 28.4984ms

Let’s open the file and scroll through it. We can see a timestamp and our log statement that we added to the WeatherForecastController class.

Provided Sinks

Now that we learned about the basics of Serilog and integrated it into our application let’s look at the provided sinks.

As we can see, there are sinks for popular cloud providers, including Amazon CloudWatch, Azure Analytics, and many other cloud services. There are also sinks for the Windows event log, Microsoft Teams, and other interesting log targets.

Additional features

If you have any questions or want me to cover additional Serilog features in the future, leave your comment below.

Remember to subscribe to not miss out on future .NET content and watch this video next.

 

Claudio Bernasconi

I'm an enthusiastic Software Engineer with a passion for teaching .NET development on YouTube, writing articles about my journey on my blog, and making people smile.