In this article let us find out how Logging in EF Core works. We use logging to Log SQL queries. Find out more about errors. Improve Performance of the SQL Queries etc. In the article, we will create an example application and show you how to log queries to console and also to the windows event log. We also show you how to filter the logs using the log levels & DbLogger
Category.
Table of Contents
Why Logging
There are a lot of scenarios, where you would see the queries the EF Core generates. For Example, the Query may fail to bring the desired result or when the query itself is slow. Looking and analyzing the query that is sent to the back end server help us to write better queries and fine-tune performance bottlenecks.
EF Core has come with a lot of logging capabilities out of the box. All we need to capture it and direct it to a text file or console or another database.
Logging in .NET Core
.NET Core apps have built-in mechanisms for creating & managing various loggers. At the heart of the Logging API is three players LoggerProvider
, Logger
& LoggerFactory
.
Logger
is what writes the log entities to output. The output can be a console, database a text file etc. It must implement the interface ILogger
The LoggerProvider
must implement the interface ILoggerProvider
. The responsibility of the LoggerProvider
to create the Logger
. The re are many built-in LoggerProviders
available for us to use. The following is the list of some of them.
Logger Providers | Description |
---|---|
Microsoft.Extensions.Logging.Console | A simple console logger |
Microsoft.Extensions.Logging.AzureAppServices | Supports Azure App Services 'Diagnostics logs' and 'Log stream' features. |
Microsoft.Extensions.Logging.Debug | Logs to a debugger monitor using System.Diagnostics.Debug.WriteLine() |
Microsoft.Extensions.Logging.EventLog | Logs to Windows Event Log |
Microsoft.Extensions.Logging.EventSource | upports EventSource/EventListener |
Microsoft.Extensions.Logging.TraceSource | Logs to a trace listener using System.Diagnostics.TraceSource.TraceEvent() |
The LoggerFactory
contains the collection of LoggerProvider
. We add the LoggerProvider
to the LoggerFactory
. Since it is a collection, it gives us the ability to log to multiple locations using different providers. It is the responsibility of the LoggerFactory
to create the instance of the ILoggerProvider
. The LoggerProvider
then creates the instance of the Logger
.
Logging To Console
The DBContext generates a lot of log information. All we need to do is tie up the LoggerProvider
to it.
The way it is done in a console application is a little different from an ASP.NET Core application. In ASP.NET Core apps the LoggerFactory is already set up by the framework. It is also automatically tied up to the DBContext class.
EF Core Console App
First, create an EF Core Console Application as shown in the tutorial. The tutorial uses Visual Studio 2017. But you can also use the Visual Studio 2019 and Dot net core 3.x.
Console Logger Provider
The next step is to install the Logger Provider. The list above contains some of the built in loggers. Let us use the Console Logger Provider. Install it using the following command.
1 2 3 | Install-Package Microsoft.Extensions.Logging.Console |
Logger Factory & Logger Provider
First, we need to create the Logger Factory
Open the EFContext class and add the following at top
1 2 3 4 5 6 7 8 9 | public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create( builder => { builder.AddConsole(); } ); |
We LoggerFactory.Create
is a static method, which creates the LoggerFactory
. It gets the builder as its argument. The purpose of it is to configure the LoggerProvider
.
We declare the MyLoggerFactory
as static. Because the Context must use the same instance of the LoggerFactory
for the entire duration of the application lifetime. Otherwise, it will have performance side effects
The AddConsole
is an extension method from the Microsoft.Extensions.Logging.Console
package. It configures the LoggerProvider
and adds it the LoggerFactory
.
With this our LoggerFactory
is ready. Now time to hook it up to the DBContext
. We do it while Configuring the DBContext
using the OnConfiguring
Method. Pass our LoggerFactory
using the UseLoggerFactory
method.
1 2 3 4 5 6 7 8 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder .UseLoggerFactory(MyLoggerFactory) .UseSqlServer(connectionString); } |
Now, run the app and you will see the select query in your console screen.
Configure Logger
The above code logs everything, which is going to fill up the console. The logger provides a lot of filters to keep the log entries to minimum
EnableDetailedErrors
This EnableDetailedErrors
option provides additional error details about the errors. Such errors most often about the misconfigured entity
properties like int
in place of string
etc. This option is set to false
by default. But when set to true
it will include details of the specific entity property that generated the error. Enabling this option will incur a small performance hit.
1 2 3 4 5 6 7 8 9 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder .UseLoggerFactory(MyLoggerFactory) .EnableDetailedErrors(true) .UseSqlServer(connectionString); } |
EnableSensitiveDataLogging
The EnableSensitiveDataLogging
enables application data to include sensitive information in the logs. The sensitive information include the values assigned to properties of your entity instances, parameter values for commands being sent to the database, etc
1 2 3 4 5 6 7 8 9 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder .UseLoggerFactory(MyLoggerFactory) .EnableSensitiveDataLogging(true) .UseSqlServer(connectionString); } |
Log Levels
The Logs are classified according to severity levels. The following table shows
Name | Value | Description |
---|---|---|
Trace | 0 | Logs that contain the most detailed messages. These messages may contain sensitive application data. These messages are disabled by default and should never be enabled in a production environment. |
Debug | 1 | Logs that are used for interactive investigation during development. These logs should primarily contain information useful for debugging and have no long-term value. |
Information | 2 | Logs that track the general flow of the application. These logs should have long-term value. |
Warning | 3 | Logs that highlight an abnormal or unexpected event in the application flow, but do not otherwise cause the application execution to stop. |
Error | 4 | Logs that highlight when the current flow of execution is stopped due to a failure. These should indicate a failure in the current activity, not an application-wide failure. |
Critical | 5 | Logs that describe an unrecoverable application or system crash, or a catastrophic failure that requires immediate attention. |
None | 6 | Not used for writing log messages. Specifies that a logging category should not write any messages |
Use the AddFilter
method to filter our the desired log level.
1 2 3 4 5 6 7 8 9 | public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create( builder => { builder.AddConsole() .AddFilter(level => level == LogLevel.Information); } ); |
DBLogger Category
Apart from the Log Levels, the logger API defines several DBLogger
categories. We can use them to filter out the log.
DBLogger Category | Description |
---|---|
DbLoggerCategory.ChangeTracking.Name | Logger category for messages from change detection and tracking. |
DbLoggerCategory.Database.Name | Logger categories for messages related to database interactions. |
DbLoggerCategory.Database.Connection.Name | Logger category for messages related to connection operations. |
DbLoggerCategory.Database.Transaction.Name | Logger category for messages related to transaction operations. |
DbLoggerCategory.Database.Command.Name | Logger category for command execution, including SQL sent to the database. |
DbLoggerCategory.Infrastructure.Name | Logger category for miscellaneous messages from the Entity Framework infrastructure. |
DbLoggerCategory.Migrations.Name | Logger category messages from Migrations. |
DbLoggerCategory.Query.Name | Logger category for messages related to queries, excluding the generated SQL, which is in the DbLoggerCategory.Database.Command category. |
DbLoggerCategory.Scaffolding.Name | Logger category for messages from scaffolding/reverse engineering. |
DbLoggerCategory.Update.Name | Logger category for messages related to SaveChanges(), excluding messages specifically relating to database interactions which are covered by the DbLoggerCategory.Database categories. |
DbLoggerCategory.Model.Name | Logger categories for messages related to model building and metadata. |
DbLoggerCategory.Model.Validation.Name | Logger category for messages from model validation. |
1 2 3 4 5 6 7 8 9 10 | public static readonly ILoggerFactory MyLoggerFactory2 = LoggerFactory.Create( builder => { builder.AddConsole() .AddFilter((category, level) => category == DbLoggerCategory.Database.Command.Name && level == LogLevel.Information); }); |
Logging To EventLog
Now, you have understood how to implement the logging in EF Core Console Application, we can easily use any other provider. For Example install the following package, which is a LoggerProvider to EventLog
1 2 3 | install-package Microsoft.Extensions.Logging.EventLog |
Now, use the AddEventLog
extension method to send the logs to the windows event log along with the console. You can also use the addfilter
method to filter out the logs.
1 2 3 4 5 6 7 8 9 10 11 | public static readonly ILoggerFactory MyLoggerFactory1 = LoggerFactory.Create( builder => { builder.AddConsole(); builder.AddEventLog(); } ); |
Summary
Logging in EF core helps us find out more information about the errors. It helps to fine tune our queries, thus helping us in improving the performance.
What should I do to log to whatever logger factory is registered in the application services?
The above code is limited to capturing the logs from the EF Core (i.e DBContext only).
If you want to use the log other info then
public class MyLog
{
public static ILoggerFactory factory = LoggerFactory.Create(
builder =>
{
builder.AddConsole();
builder.AddEventLog();
}
);
public static ILogger logger = factory.CreateLogger("EF");
}
And then can use it as
MyLog.logger.LogInformation("Log Whatever you want");
If you want to implement Logging in Console Apps, then it is better to use the DI to inject into the factory and use it.