Setting up Serilog in .NET 6

TL;DR: check out the complete, full-featured sample on GitHub.

.NET 6 is officially here! A lot has changed under the hood, and if you're kicking off a new project, a lot has changed on the surface, too.

For starters, a minimal ASP.NET Core application created using dotnet new web:

  • Doesn't have an explicit Program class, Startup class, or even a Main() method, and
  • Uses the new WebApplicationBuilder configuration API.

Everything we know and love is still tucked away in there, but even simple tasks like configuring Serilog have changed enough to warrant some updated instructions.

Why use Serilog with .NET 6?

Is Serilog still relevant in a .NET 6 application? If all you want to do with your logs is view some text in the terminal, maybe not.

If you want to treat your logs as structured data — a stream of first-class events that instrument your application and drive observability — absolutely, Serilog is still where it's at. Serilog has an unbeatable selection of output destinations (sinks), and its ability to enrich, route, filter, and format structured log events make Serilog indispensable in real-world applications.

.NET 6's logging APIs and frameworks can produce rich structured data that's great for production diagnostics and analysis. Serilog is how all of this magic is unlocked.

The sample

This post will show the bare minimum Serilog + .NET 6 configuration that you can start using right away. We've expanded it into a complete sample with everything you're likely to need for a production deployment in the datalust/dotnet6-serilog-example GitHub repository.

.NET 6 web application logs in Seq

Getting started

You'll need the .NET 6 SDK to get any further. Run dotnet --version at the terminal to confirm which version you have, if you're unsure.

Projects produced by the dotnet new web command have a CSPROJ, a configuration file, and a very simple Program.cs that looks like this:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();

Well that's certainly different!

Adding the Serilog package

To get started you'll need Serilog.AspNetCore:

dotnet add package Serilog.AspNetCore

This includes the core Serilog package, integration into the ASP.NET Core configuration and hosting infrastructure, basic sinks (outputs), and middleware for improved request logging.

You'll also need packages for your preferred sinks. For Seq it's:

dotnet add package Serilog.Sinks.Seq

Calling UseSerilog()

If you're familiar with Serilog configuration in .NET 5, the first thing you'll try to do is call UseSerilog() on WebApplicationBuilder:

using Serilog;

var builder = WebApplication.CreateBuilder(args)
    .UseSerilog(...);

// Error CS1929 : 'WebApplicationBuilder' does not contain a definition for 'UseSerilog' and
// the best extension method overload 'SerilogWebHostBuilderExtensions.UseSerilog(
// IWebHostBuilder, ILogger, bool, LoggerProviderCollection)' requires a receiver of type
// 'IWebHostBuilder'

Nope! Cheese moved 🐁.

The IHostBuilder we need is in builder.Host:

var builder = WebApplication.CreateBuilder(args);

builder.Host.UseSerilog((ctx, lc) => lc
    .WriteTo.Console()
    .WriteTo.Seq("http://localhost:5341"));

var app = builder.Build();

And that's enough to get nice Serilog output to the terminal, and structured logs into Seq.

The lc argument opens up the rest of the Serilog configuration API, so you can easily enrich your log events with additional properties and send events to more sinks.

Look out! The  builder.WebHost property looks very similar to builder.Host , and also exposes a UseSerilog() method, but this is a legacy integration point and it won't behave as nicely as the newer API.

Completing the picture

You can find a full-featured example showing how to handle startup errors, flushing on exit, streamlined request logging, JSON configuration, centralized logging with Seq, rolling file logging, filtering, and more in the sample GitHub repository.

If you're interested in learning more about the startup and hosting changes in .NET 6, check out this fantastic post by Andrew Lock.