How to set up logging using Serilog in ASP.NET with or without dependency injection

The easiest way to setup serilog is to add the Nuget packages and then configure it in your program.cs or startup.cs file. In this post I will use a standard ASP.NET web template in Visual Studio 2022 using .Net 6, here is the default program.cs file which we will use as an example:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();
//WE WILL ADD NEW STUFF HERE.
var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

The above might look a little different depending on your project. You might also be using a startup.cs file for configuring your ASP.NET pipeline and dependency injection. However keep on reading, because the only difference is that the registrations need to be put into the ConfigureService() part of our startup.cs.

Setting things up

You need to install the Serilog Nuget package Serilog and we also need a sink, we will start with the Serilog.Sinks.Console package. If you do not know how to install Nuget packages please see this page, in the end you should end up with package references in your project (.csproj) file like the following (you will likely have newer versions):

<ItemGroup>
  <PackageReference Include="Serilog" Version="2.12.0" />
  <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
</ItemGroup>

Maybe you are wondering what a sink is? Well in simple terms it is where your logs will be put, for example the console sink will put your logs in the console, the file sink will put them in a file. There are many different types of sinks and you can add multiple serilog sinks to your application. You can search the Nuget feed with “Serilog.Sinks” to get an overview.

Now that we have the necessary packages we will add a couple of lines to our program.cs file. We will add them in the configure services part of our program.cs file:

// Add services to the container.
builder.Services.AddRazorPages();

using var log = new LoggerConfiguration() //new
    .WriteTo.Console()
    .CreateLogger();

Log.Logger = log; //new 
log.Information("Done setting up serilog!"); //new

var app = builder.Build();
...

In the above we set up our LoggerConfiguration to write to the console and create a logger based on this. We assign this to the root (global) serilog Log.Logger which then can be accessed throughout the application. Maybe you would rather set up your Logger through dependency injection, we will cover that later. If we want to use our logger we can simply set it up in a class like the following:

public class IndexModel : PageModel
{
    private readonly ILogger log = Log.Logger.ForContext<IndexModel>();

    public void OnGet()
    {
        log.Information("Logging index model!");
        log.Error("This is an error");
    }
}

In the above we log something when the OnGet() method is called, which is when the Index page of our Razor application is hit. We can see the following the console:

serilog-logs-in-console

That is all there is to it, we can also configure Serilog to log to a file using the Serilog.Sinks.File package. This is done on the LoggerConfiguration using .WriteTo.File():

using var log = new LoggerConfiguration()
    .WriteTo.Console()
    .WriteTo.File("./logs.txt") //This
    .CreateLogger();

Using the above we now see a logs.txt file with the same logs in the file:

serilog-logs-in-file

NOTE: When logging to a file system, remember to configure how often files should be rolled and cleaned up!

Using dependency injection

As previously mentioned instead of assigning to a global static logger like the following:

using var log = new LoggerConfiguration()
    .WriteTo.Console()
    .WriteTo.File("./logs.txt")
    .CreateLogger();

Log.Logger = log;
log.Information("Done setting up serilog!");

If you would rather use dependency injection you can set this up as a Singleton:

using var log = new LoggerConfiguration()
    .WriteTo.Console()
    .WriteTo.File("./logs.txt")
    .CreateLogger();

builder.Services.AddSingleton<Serilog.ILogger>(log);
log.Information("Done setting up serilog!");

You can then use your usual constructor dependency injection:

public class IndexModel : PageModel
{
    private readonly ILogger _log;

    public IndexModel(ILogger log)
    {
        _log = log;
    }

    public void OnGet()
    {
        _log.Information("Logging index model!");
        _log.Error("This is an error");
    }
}

In the above we simply provide the interface used in the AddSingleton call and we automatically get our log object injected when the IndexModel is instantiated.

That is it

That is it, as always feel free to leave a comment down below if you found this helpful or if it was not! :)