Skip to main content
.NET

How To Achieve High-Performance Logging in .NET

Traditional .NET logging can slow your app. Switch to LoggerMessage delegates for better performance or leverage source-generated logging (in .NET 6+) for minimal runtime costs and cleaner code. Boost efficiency, reduce overhead, and log like a pro! 🚀

Christian Schou Køster

Ever wondered if logging in .NET could be made more efficient? Efficient logging is crucial for modern software development. It helps monitor applications and aids in debugging and performance tuning.

In .NET, while the traditional extension methods for logging are common, there are better, more performant ways to log messages. In this short .NET post, I will show you these options and teach you how they can elevate your logging practices. 🔥

Traditional Logging

Most .NET developers are familiar with the standard approach, like the one below.

logger.LogInformation("Processing request for {Endpoint}", endpoint);

While pretty straightforward, this method can lead to performance issues due to the repeated parsing of message templates and the overhead of handling variable parameters. Modern .NET versions may even warn you about this, which is cool! ✌️

CA1848 - Use LoggerMessage delegates for improved performance

An error/warning you might encounter in your IDE (if enabled) would be this one.

CA1848: Use the LoggerMessage delegates (code analysis) - .NET
Learn about code analysis rule CA1848: Use the LoggerMessage delegates

If you don't have it but would like to enable this warning, you should update your .csproj file and change the analysis level to latest-recommended. That will automatically give you this warning.

<PropertyGroup>
  <AnalysisLevel>latest-recommended</AnalysisLevel>
</PropertyGroup>

So how can we make this warning go away and improve our software? 🤓

Unlocking Performance with LoggerMessage Delegates

Wo what does that message mean? Let's break it apart a bit.

.NET offers LoggerMessage delegates to improve the performance. How? .NET is capable of pre-compiling message templates and strongly type parameters. Here’s how you can achieve just that.

Step 1 - Define a new delegate

The first thing we would have to do in our application would be to define a new delegate. In the example below I have created a new ProcessRequestLog template that I will be using whenever I log some information about processing an event in my application.

You can place it at any place in your application, but I would recommend you do it within the same project and inside a folder for all your delegates.

private static readonly Action<ILogger, string, Exception?> ProcessRequestLog =
    LoggerMessage.Define<string>(
        LogLevel.Information,
        new EventId(101, "ProcessRequest"),
        "Processing request for {Endpoint}"
    );
🧑‍💻
A delegate in .NET is essentially a type-safe function pointer. It allows you to encapsulate a method’s reference in an object, enabling the method to be passed as a parameter or assigned to a variable. In the context of LoggerMessage, delegates help pre-define logging methods to improve performance and enforce consistency.

Step 2 - Wrap it

Now that we have a new delegate in place, we should wrap it inside an extension method for our convenience. Here is the code for that:

public static void LogProcessRequest(this ILogger logger, string endpoint)
{
    ProcessRequestLog(logger, endpoint, null);
}

Step 3 - Use it

With the extension in place, we can now easily use it on the existing logger instance in our classes. Here how-to:

logger.LogProcessRequest("/api/users");

By following the above approach we can eliminate the need to parse the message template every time the log is invoked, improving performance in high-throughput applications. ⚡️

Taking It One Step Further

Beginning with .NET 6 we have the option to take advantage of source generators. What does that mean Christian and do I need it? Well if you want to make our logging even more efficient and developer-friendly then read along.

We are in such a lucky position that LoggerMessage is also an attribute 💪 In .NET we can use attributes to enable compile-time generation of our logging methods with little to minimal effort.

Here is how you can achieve just that.

Step 1 - Declare a partial method

The first thing we have to do is declare a partial method for our LogProcessRequest. The code you need:

[LoggerMessage(
    EventId = 101,
    Level = LogLevel.Information,
    Message = "Processing request for {Endpoint}"
)]
public static partial void LogProcessRequest(this ILogger logger, string endpoint);

With that in place, we can now continue in our business logic with the following as we did before:

logger.LogProcessRequest("/api/users");

Why do it this way compared to the first way I showed you? With this approach, the logging logic is generated at compile time, reducing runtime overhead and boilerplate code in your application.

Comparing The Two Approaches

Interested in a high-level comparison of the two approaches? Here is a quick comparison for you.

Approach Benefits Drawbacks
Traditional Logging Simple and familiar Performance overhead
LoggerMessage Delegates High performance, strong typing Requires additional boilerplate
Source-Generated Logging Best performance, minimal code Requires .NET 6 or later

Pro Tips for Better Logging

Here are my top three tips for improved logging in .NET applications.

  1. Set Analysis Level - Enabling latest-recommended analyzers help identify areas for improvement directly in your IDE.
  2. Use Event IDs - Event IDs will make it easier to filter and identify logs at runtime.
  3. Leverage Source Generation - It’s the future of logging in .NET, reducing runtime costs - what's not to like?!

Summary

Modern .NET frameworks provide several tools for high-performance logging, from LoggerMessage delegates to source-generated logging.

By using these built-in methods, you can write cleaner, faster, and more maintainable logging code. I am sure you and your colleagues will thank you. I hope you learned something new in this short post. Until next time - Happy coding! ✌️

Resources

Code analysis in .NET
Learn about source code analysis in the .NET SDK.
Introducing C# Source Generators - .NET Blog
We’re pleased to introduce the first preview of Source Generators, a new C# compiler feature that lets C# developers inspect user code and generate new C# source files that can be added to a compilation. This is done via a new kind of component that we’re calling a Source Generator. To get started with Source […]
LoggerMessage Class (Microsoft.Extensions.Logging)
Creates delegates that can be later cached to log messages in a performant way.
Christian Schou Køster