Adding logging to .NET Core console application
October 09, 2021
There are many different logging frameworks available for .NET. Serilog and log4net being currently popular. .NET Core also ships with a default logging framework. While it is not as feature-rich as other frameworks, it is very extensible. It is also configured as a default when using WebHost setup or .NET Generic Host. Since the purpose of this post is to show how to add logging to a simple console application, we will configure logging ourselves. Let’s start by adding the required NuGet packages to our project file:
- Microsoft.Extensions.Logging
- Microsoft.Extensions.Logging.Console
In my sample application, I will be using version 5.0.0, and below is the code that configures it:
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole();
});
var logger = loggerFactory.CreateLogger("MyCategoryName");
logger.LogInformation("Hello, world!");
To create a logger instance, we first create and configure a logger factory.
This is done via the Create
method. In our sample, we configure the factory
with console output. Note that the AddConsole
method is an extension method
found in Microsoft.Extensions.Logging.Console NuGet package. Again, similar
to the .NET Core configuration, if you ever find yourself searching for a method
from online samples, it could be, that you are missing the appropriate NuGet
package. Also, note that the logger factory is a disposable resource, so make
sure it is disposed of after logging is not required anymore.
To create a logger, we need to provide a category name. It can be any string, but in practice, it is often fully qualified consuming class name. We will take a closer look at how to use categories later.
The final step is writing something to a log. This produces the following console output:
info: MyCategoryName[0]
Hello, world!
In the output, we can see the log level info and category name
MyCategoryName. The 0 is the event ID which I will mention later. Note that I
chose to use the LogInformation
extension method. Alternatively, we could have
used the following:
logger.Log(LogLevel.Information, "Hello World!");
In practice, it is more typical to use the extension methods for a given log
level. The only log level that doesn’t have an extension method is
LogLevel.None
. The reason being, that this log level is used to disable
logging output. Let’s take a look at how to filter logging output next, but
before we do that, let’s modify our example to facilitate the discussion. We
will first add Microsoft.Extensions.Logging.Debug NuGet package, so that we
can include
Debug
logging provider.
const string categoryName = "MyCategoryName";
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole();
builder.AddDebug();
});
var logger = loggerFactory.CreateLogger(categoryName);
logger.LogInformation("Some log information output");
logger.LogWarning("Some log warning output");
logger.LogError("Some log error output");
I have also added three different log outputs on which we will be filtering. Now let’s take a look at how to add filtering. We will start by adding filtering in code and later go to a more flexible variant of doing it via configuration. To add filtering rules in code, we need to modify how we create the logger factory:
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole();
builder.AddDebug();
builder.AddFilter(l => l >= LogLevel.Warning);
builder.AddFilter(categoryName, LogLevel.Information);
builder.AddFilter<ConsoleLoggerProvider>(
categoryName, LogLevel.Error);
});
In the code above, we defined three different rules. In general, the more specific rule will override the less specific one. Here the order of definition doesn’t matter. The first rule is the default rule, which is applied if no other rule is specified. The second rule is for the category name and the third for the category name on the console logger. Since a single category is used, only error messages are shown with this configuration.
Configuring filtering in code has the downside of requiring recompilation. So let us now have a look at how to achieve the same result using a configuration file. First, add an appsettings.json file, which will contain our configuration. Remember to set it as a copy always in your project file.
{
"Logging": {
"LogLevel": {
"Default": "Warning",
"MyCategoryName": "Information"
},
"Console": {
"LogLevel": {
"MyCategoryName": "Error"
}
}
}
}
Then we need to add Microsoft.Extensions.Configuration.Binder and Microsoft.Extensions.Configuration.Json NuGet packages to our project. Now we can modify our code, so that configuration will be loaded:
var configBuilder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json",
optional: true, reloadOnChange: true);
var configurationRoot = configBuilder.Build();
using var configDispose = configurationRoot as IDisposable;
const string categoryName = "MyCategoryName";
using var loggerFactory = LoggerFactory.Create(builder =>
{
var loggingConfig = configurationRoot.GetSection("Logging");
builder.AddConfiguration(loggingConfig);
builder.AddConsole();
builder.AddDebug();
});
First, we create our configuration from appsettings.json file. To configure
logging take the "Logging"
section and pass it to the AddConfiguration
extension method. Note that we could have named our logging configuration
section differently, but in default WebHost configurations "Logging"
is used,
so I chose to stick with that.
Resources: