Where Does Microsoft Extensions Logging Log To?

If you are new to logging for .NET Core or .NET 5, chances are you have started dabbling with Microsoft.Extensions.Logging (MEL). It’s the default logging API for .NET, and with good reason. MEL works across the entirety of .NET Core, integrates with other logging frameworks like Loupe, Nlog, and Serilog, and can log to quite a few log sinks. MEL allows for customizable logging pipelines, and you’ll want to deviate from the default settings often. But before changing things around, it’s worth knowing the default behavior of MEL as a baseline. Let’s take a look!

I have created a brand new Razor Pages project in Visual Studio and added a simple log to the Configure method in the startup:

 public void Configure(IApplicationBuilder app, IWebHostEnvironment env,
                       ILogger<Startup> logger) //add logger to method
{
     //...
    logger.LogError("Hello World!"); //write error log
}

If I run my application with no other changes, MEL will log to the debug console. Here’s the output from the debug console in Visual Studio:

Screenshot of debug console with "Hello World" error log highlighted

Using debug as the default sink makes sense as it’s a console dedicated to, well, debugging. It shows up in Visual Studio and is disabled in release builds, making it both useful and safe out of the box. Most people troubleshooting problems early in a project’s life will start there.

But I don’t want to use the debug forever. Specific logs can be hard to find, as there’s no highlighting or visual identifier to help errors stand out (I highlighted the error for the screenshot). Plus, I can’t rely on it when the application’s in production. It’s at least worth comparing with the other built-in options for MEL to see if there’s a better fit.

The Built-In Sinks

There are four built-in sinks for MEL:

  1. The debug
  2. The console (aka, the terminal)
  3. The Windows Event Viewer
  4. The Event Source, for tracing (not actually a log sink)

You can easily change it in the host builder (found in the Program.cs file). Let’s remove the default sink and change it to the console in my project:

 public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureLogging(logging =>
        {
            logging.ClearProviders(); //remove default sink
            logging.AddConsole(); //set console as sink
            })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

And here’s the “Hello World!” error I created earlier, but logged to the console:

Screenshot showing "Hello World!" error in console with a red "fail" indicator next to it.

The error is more noticeable, although this method required me to start the application through the console to view the log.

For someone running the application from the console already, this is not a problem; it’s a feature. But I just like to press F5 and go. I’m not using the console often, so I shouldn’t use it as my log sink. Additionally, using the console as the log sink is pretty demanding on performance. It’s worth looking at other options.

I use Windows, so in theory the Event Viewer would work as my log sink. The Event Viewer can save logs even, unlike the console or debug. My example application sent warning, error, and critical logs to the Event Viewer automatically anyways. For example, the same log event I saw in the console was recorded in the Windows Event Viewer without ever specifying it as the target sink:

Event Viewer screenshot showing the "Hello World" error log that was sent to the console

The Event Viewer has a few problems though:

  • The Event Viewer is shared across multiple applications on a Windows system. You want to be careful not to flood it with logs and potentially hide other issues. Operations and system managers sometimes use the Event Viewer to find the problems across a system from various processes and apps. Their jobs become harder if the Event Viewer is spammed with logs from a single application.

  • With no changes, the Event Viewer doesn’t collect information leveled logs or lower from .NET apps. This is honestly how most folks will want to operate, as writing to the Event Viewer is pretty performance intensive. It’s probably best to let the Event Viewer capture errors naturally and avoid logging to it explicitly.

  • It’s exclusive to Windows. If your .NET Core or .NET 5 application is cross-platform, the Windows Event Viewer doesn’t help you log for macOS, Linux, or mobile platforms. Increasingly, this will be a problem for .NET applications as more and more developers start taking advantage of the cross-platform improvements made to .NET.

  • The Event Viewer is slow. It’s slow to write to and to use during troubleshooting. You have way more clicks to find and read the log you want to see than our other options, and loading the log data itself can take a while. If you value speed, the Event Viewer is not the best choice.

Considering the issues with the Event Viewer, you might want to know more about the last MEL native sink: the EventSource, which sends data to cross-platform tracing software provided by Microsoft (the dotnet-trace utility). But it’s not a logging sink. It’s a tracing tool, honestly a tool that deserves its own article. But not something you write your logs to.

Overall, we have four built-in sinks for MEL, three appropriate for logging, each with its advantages and flaws.

Where’s the log to file option?

None of the default sinks are ways to log directly to a file. That’s because Microsoft has not added and doesn’t intend to add a file logger to MEL. Personally, I think it’s a mistake, as the current selection of built-in sinks suffer in many situations where a log file would work well. When I need to reference an error from a closed session, the debug and console won’t work, the Event Viewer is slow, but a log file does the trick and can work on any operating system.

I find it a bit frustrating. But while I think MEL should have a built-in file logger, MEL doesn’t really need one. There are many 3rd party options available for developers to use instead. It just requires using a 3rd party logging framework, possibly a 3rd party log sink (like a dedicated log viewer), and MEL can still work as the API. But there are tons of sinks available, and deciding which specific one to use can be challenging.

So how do you decide what log sink will fit your needs?

Selecting the Best Log Sink for Your Project

Console Log to File Loupe (3rd Party Sink)
Does it Log in Real Time? Yes No Yes
Where Does it Log To? Terminal Text file in a specified directory Specified Loupe directory or online repository
Are Logs Stored Long Term? No, logs are gone when session is over Yes, as long as there is adequate disk space Yes, retention period depends on the version of Loupe and the specific data
Can I access it from another machine? No No Yes, as long as you have a Loupe Server Repository, you can access your logs on any desktop with a browser

With a different log sink often comes another logging framework. Here, I’ll focus on the log sink separate from the framework. If you need a refresher on the differences between a log sink, a logging framework, a logging API, and how they all work together, I cover that here.

When deciding what sink to use for a project, there are some questions you should ask yourself:

  • Do you need logs that update in real time? If you do, debug and the console will provide that, while writing to a standard log file will not. You could also consider a dedicated log viewer that supports real-time log viewing.

  • Do you need logs in a specific place? If I’m running an application in a docker container, I’m probably viewing the docker console. For some events, like critical failures and errors, it’s helpful to have application logs appear there as well. In many cases, you may want to use multiple sinks so everyone can get the log data where they need it.

  • Do you need logs with long-term storage? If you want to reference the logs after a session is over, after restarting your desktop, etc., debug and the console won’t cut it. That data is gone once you close those sinks. You need to use a sink that provides long-term storage like a file or a dedicated log viewer.

  • Do you need to access logs on another machine? If you want to access logs for a desktop application running across the country, you should find a sink that works with a centralized logging service. Centralized logging is the best way to access logs from multiple locations and the best log sink for applications in production.

All these questions and more will factor into what log sink you want to use for a project. You’ll likely reevaluate the log sink multiple times during initial development and after launch. The good news is Microsoft Extensions Logging is flexible enough to log where you need it to as long as you consider options beyond the built-in sinks.

What Are Some Dedicated Log Viewers that Work with MEL?

For local logging, we built a free .NET log viewer, Loupe Desktop. It works in real-time and provides long-term storage. Pretty much the upsides of logging to the console or file without the sacrifices. For folks getting started with a project or new to MEL, I recommend Loupe Desktop as it doesn’t take too long to set up, and you can use it at no cost forever.

If your application is in production, I would start to consider a centralized logging service. Ours is Loupe Server, and it works with MEL for any .NET Core or .NET 5 application:

Screenshot from Loupe Server showing a Microsoft Extensions Log

If you want to learn more about Loupe Server, take a gander at our feature pages for Loupe, or start our free trial in the link below for some hands-on education.

Try Out Loupe Server

Rock solid centralized logging

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