Defining Custom Logging Messages Using LoggerMessage.Define In ASP.NET Core

To mention,one of the nicest attributes introduced in ASP.NET Core is the universal logging facilities. In this post we shall discuss one of the helper methods in logging library, and how to make use of it in efficiently logging messages in the libraries.

Logging overview

The logging facility is shown as ILogger<T> and ILoggerFactory interfaces, which you can apply into your services using dependency injection to log messages in a various ways. For instance, in the following ProductController, a message is logged when the View action is cited.

public class ProductController : Controller

{

private readonly ILogger _logger;

public ProductController(ILoggerFactory loggerFactory)

{

_logger = loggerFactory.CreateLogger<ProductController>();

}

public IActionResult View(int id)

{

_logger.LogDebug(“View Product called with id {Id}”, id);

return View();

}

}

The ILogger could log message at several levels given by the LogLevel:

public enum LogLevel

{

Trace = 0,

Debug = 1,

Information = 2,

Warning = 3,

Error = 4,

Critical = 5,

}

The final reason of the logging infrastructure are logging givers. These are the “sinks” where the logs are written.You could plug in several providers, and write logs on a variety of different locations, for instance the console, to a file, to Serilog etc.

One good thing about the logging infrastructure, and the universal use of DI in the ASP.NET Core libraries is that same interfaces and classes are used all over the libraries and also in your application.

How to control logs produced by different categories

When deevlopin a logger with CreateLogger<T>, the type name you write is used to develop a category for the logs. At the application level, you could choose which LogLevels are output for a given category.

For instance, you can mention that by default, Debug or higher level logs are written to the providers, but for logs written by services in the MS namespace, only logs of Warning level or higher are written.

With this method you can control the amount of logging produced by several libraries in your application, enhancing logging levels for only areas which need them.

Logs without filtering

If you notice, you’ll see that most of the logs come from internal components, from classes in MS namespace. It’s nothing but noise. You could filter out Warning logs in the MS namespace, but retain other logs at Debug level:

Logs with filtering

With a default ASP.NET Core 1.X template, you need to change the appsettings.json file, and set loglevels to Warning as appropriate:

{

“Logging”: {

“IncludeScopes”: false,

“LogLevel”: {

“Default”: “Debug”,

“System”: “Warning”,

“Microsoft”: “Warning”

}

}

}

Note:In ASP.NET Core 1.X, filtering is a second thought. Some logging givers, like the Console provider let you mention how to filter. Otherwise, you can apply filters to all the providers at a same time using the WithFilter method.

Developing logging delegates with the LoggerMessage Helper

The LoggerMessage class is present in MS.Extensions.Logging.Abstractions package, and has a number of fixed, generic Define methods that return an Action<> which in turn could be used to create strong-type logging extensions.

The strong-type logging extension methods

In this instance, we are going to log the time that the HomeController.Index action method produces:

public class HomeController : Controller

{

public IActionResult Index()

{

_logger.HomeControllerIndexExecuting(DateTimeOffset.Now);

return View();

}

}

The HomeControllerIndexExecuting approach is a custom extension approach that takes a DateTimeOffset parameter. We can define it as:

internal static class LoggerExtensions

{

private static Action<ILogger, DateTimeOffset, Exception> _homeControllerIndexExecuting;

static LoggerExtensions()

{

_homeControllerIndexExecuting = LoggerMessage.Define<DateTimeOffset>(

logLevel: LogLevel.Debug,

eventId: 1,

formatString: “Executing ‘Index’ action at ‘{StartTime}’”);

}

public static void HomeControllerIndexExecuting(

this ILogger logger, DateTimeOffset executeTime)

{

_homeControllerIndexExecuting(logger, executeTime, null);

}

}

The HomeControllerIndexExecuting approach is an ILogger extension method which cites a fixed Action field on our fixed LoggerExtensions method. The _homeControllerIndexExecuting field is initiated by using the ASP.NET Core LoggerMessage.Define method, by giving a logLevel, an eventId and the formatString to create the log.

That seems like lots of effort. You could only call _logger.LogDebug() directly in the HomeControllerIndexExecuting extension method.

The goal is to improve performance by logging messages for unfiltered categories, without having to elaborately write: if(_logger.IsEnabled(LogLevel.Debug). The answer remains in the LoggerMessage.Define<T> approach.

The LoggerHelper.Define approach

The purpose this method is 3-fold:

Envelop the if statement to permit performant logging

Apply the correct strong-type parameters are passed when message is logged

Make sure that log message has the correct number of placeholders for parameters

Let’s summarise how the method appears:

public static class LoggerMessage

{

public static Action<ILogger, T1, Exception> Define<T1>(

LogLevel logLevel, EventId eventId, string formatString)

{

var formatter = CreateLogValuesFormatter(

formatString, expectedNamedParameterCount: 1);

return (logger, arg1, exception) =>

{

if (logger.IsEnabled(logLevel))

{

logger.Log(logLevel, eventId, new LogValues<T1>(formatter, arg1), exception, LogValues<T1>.Callback);

}

};

}

}

First, this does a check that the given format string, (“Executing ‘Index’ action at ‘{StartTime}’”) has the correct number of parameters named. Next, it gives back an action method with the required number of generic parameters. There are several overloads of the Define method, that takes 0–6 generic parameters, which depends on the number you would require for your custom message log.

We conclude now. Keep coding!

If you want to enhance yourself in Dot Net Course and improve yourself through Dot NET training program; our institute, CRB Tech Solutions would be of great help and support. We offer well structured program for the best Dot Net Course.

Among many reputed institutes of dot net training and placement in Pune, CRB Tech has created a niche for itself.

Stay connected to CRB Tech for your technical up-gradation and to remain updated with all the happenings in the world of Dot Net.