Updated January 2024
The team at Microsoft has added another way to do logging that touts that it’s faster than using the old new way (ILogger
). In this article, I will discuss the old way of logging and introduce the new LoggerMessage
class.
The Standard Way of Logging
For several years, the logging of errors and related information has been facilitated by the Microsoft.Extensions.Logging NuGet package. This package provides support for .NET, .NET Standard 2.0, and .NET Framework 4.62, making it versatile for various platforms. Logging messages with this package is a straightforward process through the use of ILogger.
this._logger.LogError(eventId: 100, exception: ex,
message: "Something really bad happened!");
Benchmark Results
Let’s examine the performance of these calls.
While the existing approach is efficient, .NET 6, combined with a source generator, elevates logging to an even higher level of performance!
The Enhanced Logging Approach in .NET 6
With the advent of .NET 6, a groundbreaking source generator has been introduced, harnessing the power of LoggerMessage
to outperform the previously showcased code. I have seamlessly integrated support for this state-of-the-art source generator in Spargine, and I’ll guide you through the process below. It’s remarkably straightforward.
To begin, I created a partial class named EasyLogger
within Spargine. Using the LoggerMessageAttribute
, I implemented methods for each of the available logging levels: LogLevel.Critical, LogLevel.Debug, LogLevel.Error, LogLevel.Information, LogLevel.Trace, and LogLevel.Warning. Here’s an illustrative example:
[LoggerMessage(EventId = 100, Level = LogLevel.Critical,
EventName = "CRITICAL", Message = "{message}")]
public static partial void LogCritical(ILogger logger, string message, Exception ex);
By leveraging this attribute, you gain the ability to log crucial details including event ID, logging level, event name, message, and, of course, the exception (relevant for Error and Critical scenarios). Furthermore, you can log the type ID and circumvent the IsEnabled
check. Logging your messages with an ILogger object is now as straightforward as demonstrated in the following example:
EasyLogger.LogCritical(logger: this._logger, message: "Something bad happened!",
ex: ex);
Upon the application of the LoggerMessageAttribute
, it automatically generates a behind-the-scenes partial method resembling the following for LogCritical()
:
public static partial void LogCritical(global::Microsoft.Extensions.Logging.ILogger logger,global::System.String message, global::System.Exception ex)
{
if (logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Critical))
{
__LogCriticalCallback(logger, message, ex);
}
}
For each method, it generates a corresponding private method— in this instance, __LogCriticalCallback
— which manages the actual logging using LoggerMessage
.
Benchmark Results
Now, let’s examine the performance when utilizing the LoggerMessageAttribute
.
Indeed, the impact of utilizing the LoggerMessageAttribute
is striking, with performance improvements of up to 11 times!
Enhancing code efficiency is paramount, especially in cloud environments, and incorporating the LoggerMessageAttribute
in .NET 6 and beyond can substantially contribute to achieving this objective. It’s essential to log extensively and regularly; a fundamental strategy for pinpointing issues in your code. Additionally, don’t overlook the importance of logging events.
For a more in-depth exploration of Spargine and to discover additional performance enhancement tips, please refer to the Faster Performance with DotNetTips.Spargine chapter in my code performance book.
Summary
We all recognize the importance of optimizing our code, particularly in cloud environments, and incorporating LoggerMessage
can significantly contribute to this effort. I understand the reluctance to adopt new practices if they entail additional coding. However, in Spargine, I’ve streamlined the implementation process to minimize any hurdles. Logging extensively and frequently is crucial for effective issue tracking in your code. Therefore, make it a priority to log as much information as possible. This approach will prove invaluable in identifying and resolving potential issues.
Spargine is also available as NuGet packages. To learn more, please go here: https://dotnettips.wordpress.com/spargine/. Do you have any questions or comments? Please make them below.
Pick up any books by David McCarter by going to Amazon.com: http://bit.ly/RockYourCodeBooks
Make a one-time donation
Make a monthly donation
Make a yearly donation
Choose an amount
Or enter a custom amount
Your contribution is appreciated.
Your contribution is appreciated.
Your contribution is appreciated.
DonateDonate monthlyDonate yearlyIf you liked this article, please buy David a cup of Coffee by going here: https://www.buymeacoffee.com/dotnetdave
© The information in this article is copywritten and cannot be preproduced in any way without express permission from David McCarter.
Also the old (and still quite valid) way was to inject ILogger into the constructor.
Calling new LoggerFactory().CreateLogger() is wasteful (and wrong).
I think everyone should take a look at the source code -> https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs
The static class supports creating logging delegates for logging scopes for example, to enable structured logging, which I believe is more useful nowadays especially in the cloud, than just exception message logging.
I’ll be honest, I stopped reading at “new LoggerFactory().CreateLogger();”