Serilog vs. Microsoft Extensions Logging: Which to Use?

I have been pretty upfront about my appreciation for Microsoft Extensions Logging. I’ve written more on it than the Loupe API - but Serilog brings some interesting capabilities to the party.

So, how do they compare? Should I use one over the other? Well, it’s messier than that - It doesn’t have to be a question of “Microsoft Extensions Logging or Serilog” but instead a question of “Microsoft Extensions Logging and Serilog?”

I won’t totally throw out the premise of this being a “vs.” article: I’ll take some time to compare the two head to head. But then, I want to discuss the typical setup we recommend for our users interested in logging for their .NET 5 & .NET Core app which is to use both.

Serilog vs. Microsoft Extensions Logging: Abridged

Both are competent as loggers for any .NET project. If you are already using one or the other for an application, there is no real reason to rush and switch to the other team.

Microsoft Extensions Logging (MEL) is a clean logging framework that’s considered the default for .NET. That doesn’t mean you can’t make .NET projects without it, but doing so will require a conscious effort. Its most significant strength is its flexibility (more on what makes it flexible later).

Serilog is the reference example of a Structured Logging system - one that doesn’t just create messages, it easily captures key attributes and data about the context the message happened in. While this is possible with MEL via Logging Scopes (and desirable!), Serilog does it automatically as a natural outcome of composing log messages from format strings, a neat trick!

So in short,use Serilog if one of these applies to you:

  • If you’re writing in .NET Framework and can’t use .NET Standard.
  • You have to add logging to the entire startup process.
  • If you need to use an intricate feature of Serilog that MEL doesn’t support, go ahead and write that part directly to Serilog (but consider writing the rest to MEL).

Otherwise, Microsoft.Extensions.Logging should be your API of choice. It’s not that Serilog is complex or cumbersome, but MEL is likely a dependency in pretty much any .NET project you start anyways.

Note that I specified to use Serilog as the logging framework. I didn’t write that arbitrarily. To add logging to your .NET application, you actually need to add two things: a logging framework and a logging API.

Logging Framework vs. Logging API: How We Use Both

When someone asks, “What Logger should I use for my application?” they are asking two questions:

  1. What is the logging API you want your code to utilize to capture log data?
  2. What is the logging framework you want to have implement that API to construct the formatted output various logs you want to record?

The API is what your code actually calls. The framework is what creates the structured log messages with the data passed through the API. Finally, the log messages are pushed to a sink, the mechanism used to store (and sometimes transport) the log data to where you can view it..

For the rest of the article, the sinks don’t really matter (although if you want your logs to be helpful, the sink is a tool that really matters). But how you set up Serilog does.

Serilog has very distinctive styled logs (because of the syntax used for insertion strings). That means if someone is going to use Serilog, they should use the framework. The only decision to make when choosing to use Serilog is whether to use both the framework and API, or to use the framework with MEL as the API.

Using Serilog Exclusively

Below is an example logging flow using Serilog as exclusively.

Flow chart showing the Serilog API going to the Serilog framework into the sinks Loupe and text file

This flow is capable of making excellent structured logs for pretty much any .NET application. All the features of Serilog that are not available in MEL will work when using Serilog exclusively. But there are downsides to this setup when we consider the dependencies your application takes on.

Everything in your codebase takes a dependency on Serilog. If you want to update to the newest release of Serilog, you have to update every single part of the project, including libraries you may not want to touch. Or maybe you want to add a new library to use in a simple utility? You’re setting up Serilog too. Or, if you’re going to move away from Serilog, you’ll need to update every part of the project.

This setup forces the entire organization to be all in on Serilog. Every team, every piece of the application. This is not a disaster scenario, but it is a real problem that can be avoided by using MEL as the API.

MEL as API, Serilog as Framework

One of MELs goals is to help .NET users apply whatever logging framework they want without requiring it as a dependency throughout the whole project.

So if we use MEL as the API and Serilog as a Framework, we pretty much get all the advantages of Serilog while keeping the flexibility of MEL. Take a look at the example flow below:

Flow chart showing the MEL API going to both the MEL and Serilog Frameworks, MEL going into the Debug Sink and Serilog going into the Loupe Sink

This functionally works the same as the Serilog example. We can use the Serilog formatting to make structured logs and access the same sinks. So, what’s the difference?

The key distinction is not visible in chart. These charts only account for the main process for your application. Consider any libraries you have. I mentioned earlier that if you use Serilog as the API, you will have to add Serilog as a dependency for each of those libraries. The same is true for MEL, the libraries will take it on as a dependency. But the difference is you do not have to use Serilog as the framework for each of these libraries as well.

This setup allows for greater flexibility across the board. That means you can use Serilog for where it makes sense, like the application executable, and avoid using it for items like individual libraries where it may be best to leave the output in debug, a console, or use the NullLogger to not capture anything at all. Ultimately, using MEL as the API allows you to use the best framework for your main process, while letting MEL take care of logging for the other parts you may want to keep simple.

So What Should I Use?

For almost every current .NET project, we recommend using MEL as the logging API and letting preference guide which frameworks to use behind it. That way, you’re only taking on one dependency as an organization, and you are free to use whichever logging framework makes sense for other parts of the project. If you want one of those frameworks to be Serilog, that’ll work just fine. It can create some great looking, data rich logs.

There’s one more piece to the logging pipeline that I haven’t covered in depth here: the sink. You may be using the console, a text file, or even something like the EventViewer if you are on windows. But it’s worth giving Loupe Desktop a try.

Screenshot of Loupe Desktop session with serilog logs.

Loupe Desktop is a free log viewer for .NET Framework, .NET Core, and .NET 5. The above screenshot shows me running Serilog into Loupe in an ASP.NET application. Along with an advanced log viewer, Loupe Desktop can also provide performance information on your application, including memory usage, average time per unique query, and much more. You can get it for yourself using the link below.

Get Loupe Desktop

Rock solid centralized logging

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