Update: for ASP.NET Core 3, check out the updated instructions in this more recent post.

You don’t need anything special to use Serilog with .NET Core: the Serilog package works the same way across the .NET Framework and .NET Core on Windows, macOS and Linux.

If you are writing .NET Core web apps with ASP.NET, you’ll want to plug into the Microsoft.Extensions.Logging subsystem to receive events from the framework: unhandled errors, diagnostic info from the request processing pipeline, events from EF, etc.

In ASP.NET Core 2.0, the default logging provider has gained some features, like its own level control and filtering. This is good thing for the out-of-the-box experience, but if you use Serilog, you won’t want two sets of configuration to keep in sync or two logging pipelines running with subtle differences between them.

To keep things sane and streamlined, Serilog is integrated deeper into ASP.NET Core 2.0. It’s much simpler to explain and configure:

  1. Install Serilog.AspNetCore from NuGet
  2. Set up Serilog with LoggerConfiguration in Program.cs, as you would in any other app
  3. Call UseSerilog() on the web host builder to replace the default logging provider

You can then go ahead and delete any other logger configuration that’s hanging around: there’s no need for a "Logging" section in appsettings.json, no AddLogging() anywhere, and no configuration through ILoggerFactory in Startup.cs.

// Install-Package Serilog.AspNetCore -Pre

public class Program
{
    public static int Main(string[] args)
    {
        Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Debug()
            .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
            .Enrich.FromLogContext()
            .WriteTo.Console()
            .CreateLogger();

        try
        {
            Log.Information("Starting web host");

            var host = WebHostBuilder.CreateDefault()
                .UseStartup<Startup>()
                .UseSerilog()  // <- The magic
                .Build();

            host.Run();

            return 0;
        }
        catch (Exception ex)
        {
            Log.Fatal(ex, "Host terminated unexpectedly");
            return 1;
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }
}

UseSerilog() replaces the built-in ILoggerFactory so that every logger in your application, whether it’s Serilog’s Log class, Serilog’s ILogger, or Microsoft.Extensions.Logging.ILogger<T>, will be backed with the same Serilog implementation, and controlled through the same configuration.

This does come with some caveats:

  • If you want Application Insights to receive log messages, you’ll need to add Serilog.Sinks.ApplicationInsights to your Serilog configuration
  • Likewise, you’ll need to add Serilog.Sinks.Console for terminal output, Serilog.Sinks.RollingFile for file logging, etc
  • If you want to configure Serilog from appsettings.json, the configuration needs to be loaded ahead of setting up Serilog

Of course, these are also the benefits: getting only what you ask for makes this a lean, tightly-controlled, and easily-understood logging setup.

Decoupling logger configuration from the rest of ASP.NET Core bootstrapping means you can count on your logger reporting exceptions thrown from DI registration and other start-up activities, too. I’m a strong believer in setting up logging immediately at application startup - the later in the process you leave it, the more chance there is you’ll be scrabbling around trying to find the reason for a failure that logging can’t report.

Serilog.AspNetCore in .NET Core 2.0 finally gets it right: no bloat is imposed by the integration layer, and everything works together seamlessly. If you need a hand to get up-and-running, please jump in on the comments below, or drop us a line via the Serilog Gitter channel.