Adding a Logger With the .NET 6 Minimal Hosting Model

With the release of .NET 6, Microsoft introduced a new hosting model for ASP.NET Core. In short, it takes the old .NET Core model, puts it all into one file, and reduces the amount of code considerably.

Now, it’s not required to use the new hosting model. I have already transferred older .NET 5 applications to .NET 6 without it (the transition only required changing the target framework). But the simplicity of the new model, and the fact that it’s considered the default by Microsoft, warrants attention. I could see teams wanting to switch to the new hosting model, as I wanted to do the same thing for some demo applications I use to test Loupe.

So I figured this would be an excellent opportunity to go over adding a logger to the new host. This article will cover a basic log-to-file setup with Serilog and logging directly to Loupe. Hopefully, that will help make testing out .NET 6 a little bit easier for you.

Let’s Start With What’s Already Included

If I just open up a template ASP.NET Core application on .NET 6, it uses the new hosting model, and Microsoft.Extensions.Logging (MEL) is still available as the default. MEL is the standard logging API across .NET Core. It captures exceptions and lets you log directly to it on .NET 6, the same way you have before.

So, to use a capable logger on .NET 6, you don’t need to add anything! It’s included. But MEL is limited in available log sinks out-of-the-box, so it’s worth the time to look at the other logging options available anyways. Let’s start with a popular choice across .NET for logging to file and a personal favorite of mine: Serilog.

Quickly Log to a File With the New Hosting Model

If you are looking to log to a text file on .NET 6, Serilog is where I would start.

The current documentation for Serilog shows you how to log to a file using the generic hosting model already, and we can quickly adapt this for the .NET 6 hosting model, as it largely works the same way. So what we need to do is just include the LoggerConfiguration() before Build(). Additionally, I want to add Serilog.Extensions.Logging to use Serilog as a MEL provider, so I’ll clear providers and set that up.

This is what the Program.cs file looks like through to the Build() method:

var builder = WebApplication.CreateBuilder(args);

Log.Logger = new LoggerConfiguration()
    .WriteTo.File("logs/myapp.txt", rollingInterval: RollingInterval.Day)

// Add services to the container.

var app = builder.Build();


Note there is no using statement in this sample. That’s because, for something as ubiquitous as a logger, it makes sense to use a global using statement. Global using statements were introduced in C#10, and several of them are generated automatically in the new .NET 6 projects (hence why there are none present in the new host model). You can’t throw your own statements into the auto-generated file, but you can make a file in the project to contain all your global statements (I made a file called GlobalUsings.cs). But of course, you can just stick with normal using statements and throw them at the top of the Program.cs file and be just fine.

Otherwise, the code sample is all you need to start. You can always get fancier with it, add additional formatting, enrichers, or eventually use a log sink more sophisticated than a file (like Loupe Desktop for live log viewing). But overall, it’s easy to get Serilog up and running as the setup doesn’t differ all that much.

Adding Loupe to the New Hosting Model

The current Loupe “getting started” documentation for .NET Core is written for the generic host, but it was easy to adapt the directions for the new hosting model. Using the migration code samples provided by Microsoft, I wrote the following Loupe configuration:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddHttpContextAccessor(); //Requierd Service for Loupe

builder.Host.AddLoupe(builder => builder.AddAspNetCoreDiagnostics()

var app = builder.Build();


The above sample is a pretty basic Loupe setup using the optional performance counters package, and adding Loupe as a MEL provider. No using statements for the same reasons as the Serilog sample: it makes sense to create global using statements for Loupe (but you don’t have to if you want to just write regular statements, that’s fine). For reference, these are the statements I would include in a separate file:


global using Loupe.Agent.Core.Services;
global using Loupe.Agent.AspNetCore;
global using Loupe.Agent.PerformanceCounters;
global using Loupe.Extensions.Logging;

Also, note the added HttpContext Accessor service in the sample, as that’s a requirement for the ASP.NET Core Loupe Agent. Your setup may have a few more optional Agents (like our client-side logging agent for JavaScript), but adapting it to the new host model will look something like the above sample. We will add more specific directions to our documentation soon.

.NET 6 is New, But Not Incompatible

The new hosting model was very easy to work with, and I could add the logging tools I used across .NET Core. I don’t think the hosting model is the right choice for everyone. But for those looking to write a .NET 6 application from scratch, or use a more minimal hosting model, the new one shouldn’t take too long to get used to.

If you would like to get started with a great log viewer for .NET 6, give Loupe a shot. You can use our desktop log viewer for detailed local log viewing. Our Loupe Server software goes a step further though and provides a centralized-logging system you can access on any desktop with a browser. You can learn more about Loupe Server and our 30-day free trial in the link below.

See The Trial

Rock solid centralized logging

Unlimited applications, unlimited errors, scalable from solo startup to enterprise.