Log recording plays a vital role in monitoring and debugging dotnet applications. However, improper use of logs can negatively impact application performance.
It is common to spread log write calls throughout the code, often using string interpolation to facilitate their creation. However, we rarely evaluate whether this approach is the most efficient.
Analyzing the LogInformation method benchmark
To illustrate the impact of memory allocation, we will examine the benchmark of the LogInformation method, existing in the common Logger that uses extension methods of the ILogger interface. The test code is simple and commonly found in production applications.
[Benchmark(Baseline = true)]
public void LogInformationComStringInterpolation()
{
_logger.LogInformation($"Pedido {_pedido} gerado com sucesso!");
}
Benchmark with string interpolation
When running the benchmark with string interpolation, we observe an allocation of 80 bytes. It seems like an insignificant amount, but when we repeat this call in a loop of 100,000 iterations, as in a file import, the allocation totals 7.63MB. It is a considerable amount of memory used just for writing logs.
Memory allocation even with LogLevel set to Warning
Surprisingly, even when setting the LogLevel to Warning, the memory allocation remains the same (7.63MB). This can be alarming, especially considering the presence of other log calls, such as LogDebug, LogTrace, and LogInformation, spread throughout the code.
Reducing memory allocation with "String Template"
One approach to reducing memory allocation is to use the "String Template" format instead of string interpolation. When applying this change, memory allocation is reduced to 56 bytes in a single test. Performing the same test with 100,000 iterations, memory allocation remains lower compared to string interpolation.
Avoiding memory allocation with LogLevel check
We can avoid memory allocation by checking if the LogLevel is enabled before making the log call. This approach solves the
problem when we want to increase the LogLevel in production. However, memory allocation still occurs when we need to execute LogInformation in production. Although the use of "String Template" can reduce consumption, it is not possible to eliminate it completely.
[Benchmark(Baseline = true)]
public void LogInformationComStringInterpolation()
{
for (int i = 0; i < 100_000; i++)
{
if (_logger.IsEnabled(LogLevel.Information))
{
_logger.LogInformation($"Pedido {_pedido} gerado com sucesso!");
}
}
}
[Benchmark]
public void LogInformationComStringTemplate()
{
for (int i = 0; i < 100_000; i++)
{
if (_logger.IsEnabled(LogLevel.Information))
{
_logger.LogInformation("Pedido {Pedido} gerado com sucesso!", _pedido);
}
}
}
Conclusion and next steps
It is essential to understand the reasons for memory allocations in the Logger and explore approaches to avoid them. In the next article, we will explain the reasons for the allocations and the method to reduce memory allocations when using logs.
The following test presents an optimization, in which no memory allocation is required during log writing.
Stay updated and do not miss the continuation of this content!
Top comments (0)