Logging with Serilog in .NET
Logging is one of the fundamental aspects of our development because it helps us figure out our problems quickly.
We need to log our requests and response and then we can use them to identify the issues.
We have two famous logging libraries available in .NET
-
NLog
-
Serilog
Serilog specifically shines in the structured logging area, one could ask what structured logging is:
Well, structured logging says we follow a same structure to save our logs throughout the application.
Let’s set up Serilog.
Installing Serilog
Install-Packet Serilog.AspNetCore
The next step to configure is calling UseSerilog method over our host builder and configure configurations.
I prefer to keep this process in a different extension method like this :
using Serilog;
public static class SerilogBuilder
{
public static WebApplicationBuilder UseSerilog
(this WebApplicationBuilder builder)
{
builder.
Host.
UseSerilog((hostingContext,
loggerConfiguration) =>
{
loggerConfiguration.
ReadFrom.
Configuration(hostingContext.
Configuration);
});
return builder;
}
}
Now we can call this in Program.cs :
public static void Main(string[] args) {
var builder = WebApplication.CreateBuilder(args).UseSerilog();
var app = builder.Build();
if (app.Environment.IsDevelopment()) {
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
}
Understanding the Configurations
Configurations are a way to tell Serilog the following things :
-
Where should our logs be displayed console or file?
-
What should be the rolling interval?
-
Which additional details should be added in logs e.g. machine name, thread ID, etc
-
What should be the minimum level of our logs e.g. Warning, Error, or Information?
There are two ways to configure this :
-
Using app settings
-
Using code
How to set up configurations via app settings?
We have to add a section with the name Serilog in app settings like this :
{
"Serilog": {
"Using": [
"Serilog.Sinks.Console",
"Serilog.Sinks.File"
],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Information"
}
},
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "File",
"Args": {
"path": "Logs/log.txt",
"rollingInterval": "Day"
}
}
],
"Enrich": [
"FromLogContext",
"WithMachineName",
"WithThreadId"
]
}
}
This is all that we need to do, wondering how this would be read. This part is doing that magic
loggerConfiguration
.ReadFrom
.Configuration(hostingContext.Configuration);
I prefer setting up configuration via app setting because it is much more flexible than the second approach.
How to set up configurations via code?
Let’s set up the configuration in a file first, so we can call that later on :
public static void WriteConfiguration()
{
string logPath = @"Logs/Log-.txt";
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.Override("Microsoft", LogEventLevel.Information)
.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.Async(c => c.File(logPath, rollingInterval: RollingInterval.Day))
.WriteTo.Async(c => c.Console())
.CreateLogger();
}
Let’s call it in our Program.cs now
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
Logger.WriteConfiguration(); // Configure logging
builder.Host.UseSerilog(); // Use Serilog for logging
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
// Additional configuration (if needed)
app.Run();
}
How to use the logger in the controller or any other class?
Just inject ILogger with your desired class like this and use it :
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
_logger.LogInformation("Getting weather forecast");
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
// Define a sample WeatherForecast class
public class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
}
// Sample summary data
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool",
"Mild", "Warm", "Hot", "Sweltering", "Scorching"
};
}
If you are using Core you need to install Serilog.AspNetCore nuget package.
To write in the file you need to install Serilog.Sinks.File if not installed automatically
Where is structured logging in it?
This is the simplest example of log structure
var logStructure = new
{
Date = DateTime.Now,
Message = "Getting weather forecast"
};
_logger.LogInformation("Getting weather forecast {@logStructure}", logStructure);
You can modify it according to your needs.
Whenever you're ready, there are 3 ways I can help you:
- Subscribe to my youtube channel : For in-depth tutorials, coding tips, and industry insights.
- Promote yourself to 9,000+ subscribers : By sponsoring this newsletter
- Patreon community : Get access to all of my blogs and articles at one place