Adding Serilog — logging in .NET application

Sajad Shafi
5 min readJun 21, 2024

--

Introduction

Logging is a critical aspect of modern software development, offering invaluable insights into the behavior and performance of applications. In the .NET ecosystem, Serilog stands out as a robust, flexible, and user-friendly logging library. Known for its structured logging capabilities, Serilog allows developers to record and manage logs in a way that is both human-readable and machine-processable, facilitating easier debugging, monitoring, and auditing.

This article aims to guide you through the process of integrating Serilog into your .NET applications. Whether you’re new to logging or looking to enhance your existing logging strategy, this guide will provide you with a comprehensive understanding of how to set up, configure, and leverage Serilog to its full potential. By the end of this article, you’ll be equipped with the knowledge to implement efficient logging practices that can significantly improve the maintainability and reliability of your applications.

Install package

In order to add logging we will need a package called Serilog.AspNetCore. In Visual Studio head over to the nuget manager and search for "Serilog.AspNetCore" and click install. In terminal just copy the following command in terminal and it will install the package:

dotnet dotnet add package Serilog.AspNetCore

You can add a version number at the end of this command to install a specific version like below one:

dotnet add package Serilog.AspNetCore --version 8.0.1

Dependency

Once the serilog package is installed the next step is to add service for serilog in the Program.cs, after the builder is initialized.

builder.Host.UseSerilog((context, config) =>
config.ReadFrom.Configuration(context.Configuration)
);

In the above code snippet we are adding serilog as our default logger, and we are telling it to fetch the serilog configuration from appsettings.json configuration which we haven't added yet.

Then, in the middleware pipeline add the below line:

app.UseSerilogRequestLogging();

Now depending on your project’s initial setup, your final Program.cs should look like below, if you are using controllers and if you are using minimal APIs then there will be a very small change but that has nothing to do with serilog:

using Serilog;

var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog((context, config) =>
config.ReadFrom.Configuration(context.Configuration)
);

// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.

if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseSerilogRequestLogging();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

Configuration

Next, we need to give serilog the configuration we promised it above, when we added config.ReadFrom.Configuration(context.Configuration). For that lets head over to the appsettings.json and add the below code:

"Serilog": {
"Using": [
"Serilog.Sinks.Console",
"Serilog.Sinks.File"
],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "File",
"Args": {
"path": "logs/log-.log",
"rollingInterval": "Day",
"rollOnFileSizeLimit": true
}
}
]
}

And, that is how you add logging into the .net application.

An issue

  • dotnet run command stucks at building.

I found this issue while I was configuring serilog, remember when we first create .net webapi application and we run it we get something like this in the terminal:

But, after you add serilog using the above steps you will be surprised to see that the above details are no more showing in the terminal.

It got me wondered for a while, at first I thought my application is freezing. Then, after searching for hours I finally found a fix which is to add "Microsoft.Hosting.Lifetime": "Information" in the appsettings.json in Serilog:MinimumLevel:Override section. The updated appsetting.json will look like this:

"Serilog": {
"Using": [
"Serilog.Sinks.Console",
"Serilog.Sinks.File"
],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "File",
"Args": {
"path": "logs/log-.log",
"rollingInterval": "Day",
"rollOnFileSizeLimit": true
}
}
]
}

And after you run the application again you will see the logs back.

The logs look a little different because serilog adds its own formatting. You can change that as well and add a json formatter to the serilog as well but in this article I won’t be doing that.

One more thing to remember is that the "path": "logs/log-.log" in WriteTo:File section's args object will tell the serilog where to save the files. When you add it as logs/log-.log and you give rollingInterval as "Day", it will create your log files in the root folder of you project and will append current date to the log file as you can see in the image:

If you set your path as “/logs/logs-.log” it will generate logs in the root directory. For example if your project is in D drive the logs folder will be in D:/logs/log-20240621.log.

Testing

Finally lets test it out. I have a controller UserController.cs where I have added a constructor that initializes a _logger object that is created just above the constructor:

private readonly ILogger<UserController> _logger;

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

And I have created a method inside this controller:

[HttpGet]
public IActionResult Get()
{
_logger.LogInformation("User found successfully!");
_logger.LogWarning("User was not found in good condition!");
_logger.LogCritical("User is very critical");
_logger.LogError("I am sorry but the user is already dead!");
return Ok("User found!");
}

I have added logs of different types:

and if we run the application using the dotnet run command or if you are in visual studio the by clicking run button. You will see swagger as below:

After you run this function you will see the result User found with 200 response. When you check the logs file you will see something like:

You can see the different labels in square brackets:

  • [WRN] — Warning
  • [FTL] — Critical
  • [ERR] — Error
  • [INF] — Information

Conclusion

Now this was a basic setup there is much more to do like writing your logs to Azure ApplicationInsights which by the way I have written in Logging and monitoring the dotnet API using serilog and Azure Application Insights article. Or, adding a structured logging by adding some configuration to make your logs more readable and write in a better format like json. This is it for now, subscribe for more .NET content like this.

Thank you!😇

--

--

Sajad Shafi
Sajad Shafi

Written by Sajad Shafi

Software engineer by profession experienced in C#, .Net, JavaScript Typescript, ReactJS and SQL.

No responses yet