Часть 2: Как оптимизировать логирование в .NET: советы и примеры
Сравниваем производительность стандартного логирования и LoggerMessage. Узнайте, как ускорить .NET-приложение.
868 открытий4К показов

Это продолжение нашей серии о логировании в .NET. В первой части мы разобрали, как стандартные методы могут замедлять приложение, и познакомились с LoggerMessage. Теперь пора перейти к практике: мы покажем реальные тесты производительности и расскажем, как автоматизировать переход на эффективное логирование.
Сравнение производительности: стандартное логирование против LoggerMessage
Чтобы сравнить производительность LoggerMessage и методов расширения интерфейса, мы решили написать программу, подсчитывающую сумму четных элементов массива. Предварительно добавили несколько новых методов для записи дополнительных событий. Вот объявления для новых событий:
Обновленный код приложения:
Здесь строки вызова методов расширения ILogger
закомментированы. А вызовы наших методов, построенных через механизм LoggerMessage, раскомментированы. Таким образом, можно прогнать программу, используя ILogger
, либо, наоборот, LoggerMessage
При использовании механизма LoggerMessage затраты на выполнение методов логирования, которые мы вызывали в цикле (LogIterationStart и LogIterationComplete), составили 0,58% и 0,52% процессорного времени от общих затрат.
При использовании методов расширения ILogger затраты на выполнение метода ILogger.LogInformation составили 35,12% от общих затрат.
Да, можно заметить, что в затраты попали вызовы метода LoggerMessage.Define
. Но заметим, что эти методы выполняются один раз на старте приложения. И если наше приложение серверное (долгоживущее), то затраты станут ничтожными по отношению к общим затратам за всё время жизни приложения.
При использовании же методов расширения ILogger
затраты на выполнение будут расти, создавая нагрузку как на процессор, так и на память.
AutoLoggerMessage: как легко перейти на высокопроизводительное логирование
Мы рассмотрели два способа типизированных методов для ILogger. LoggerMessageAttribute упрощает процесс, но перевод готового проекта всё равно сложный.
К счастью, есть nuget-пакет, который умеет преобразовывать методы расширения ILogger.Log* из пространства имен Microsoft.Extensions.Logging в строго типизированные методы LoggerMessage.
Чтобы воспользоваться этим пакетом, нужно выполнить два простых шага:
Добавить в проект nuget-пакет stbychkov.AutoLoggerMessage
Это можно сделать либо через консоль с помощью инструмента Nuget package Manager, либо добавив ссылку на пакет в файл проекта.
Команда консоли Nuget Package Manager:
Install-Package stbychkov.AutoLoggerMessage
Добавление пакета с помощью утилиты dotnet:
dotnet add package stbychkov.AutoLoggerMessage
Разрешить перехватчики для генерации исходного кода
Для тестирования этого пакета мы создали отдельный проект консольного приложения и подключили в него указанный nuget-пакет. Далее добавили в модуль Program.cs пару строк с вызовами ILogger.LogInformation:
Если попытаться перейти к реализации метода LogInformation
с помощью команды Go To Implementation
, мы увидим, что попали не в метод расширения класса LoggerExtensions, а в строго типизированный метод в классе GenericLoggerExtensions
:
Для первого вызова, где передана только строка сообщения, будет вызван этот метод:
Для второго вызова, где передан параметр типа TimeSpan, будет вызван следующий метод:
Как видите, второй метод имеет обобщенный параметр.
Можно заметить, что внутри есть вызовы LoggerExtensions
, которые не строго типизированы. Это нужно, поскольку во время компиляции вложенный вызов logger.Log
должен быть заменен сгенерированным кодом, который в итоге будет вызывать подходящий строго типизированный метод расширения класса LoggerMessageExtensions
.
Сборка проекта как в MS Visual Studio 2022, так и в JetBrains Rider, прошла без проблем. Так, если в IDE Rider запустить отладку проекта, предварительно поставив точку останова на вызове одного из методов logger.LogInformation(), то можно увидеть код, который будет вызван. Для этого выберите команду Step Into:
Мы можем видеть, что дальше управление передается сгенерированному методу с типизированными параметрами:
Этот код построен на LoggerMessage.Define
– следующим шагом мы попадаем в один из методов расширения класса LoggerMessage
:
Таким образом, пакет AutoLoggerMessage
позволяет перейти на высокопроизводительное логирование без ручного изменения исходного кода.
Правда, у AutoLoggerMessage
есть ограничения:
- Поддерживается с .NET 8.0.8 и выше;
- В
ILogger.Log*()
может быть не более 6 параметров, иначе параметры передаются как массив object, то есть вызывается упаковка значимых типов.
Какой подход к логированию выбрать
Подводя итоги, приведем плюсы и минусы каждого подхода.
ILogger и методы расширения Log*() класса LoggerExtensions
+ Если используете ILogger из коробки, вам не нужно добавлять статический класс с объявлениями методов расширения.
- Так как дополнительные параметры передаются как object, то выполняется упаковка значимых типов.
- Разбор шаблонов сообщений выполняется при каждом вызове, что может увеличить нагрузку.
- Код загрязняется текстом шаблонов сообщений.
- Поскольку сообщения добавляются по мере необходимости прямо в код классов, то возможно дублирование сообщений.
LoggerMessage и строго типизированные методы класса LoggerMessageExtensions
+ Не загрязняют код шаблонами сообщений.
+ Методы расширений имеют строго типизированные параметры — это исключает упаковку значимых типов.
+ Шаблоны сообщений разбираются один раз во время старта приложения.
+ Все сообщения хранятся в одном статическом классе, которым проще управлять.
- Придется добавить статический класс и статический метод для каждого вновь добавляемого сообщения.
В целом, подход с LoggerMessage имеет больше преимуществ. А с пакетом AutoLoggerMessage можно легко перевести проекты на более производительное логирование.
Исходный код можно посмотреть на bitbucket. А узнать больше информации про .NET — в нашем канале.
Полезные ссылки
Logging in C# - .NET | Microsoft Learn
High-performance logging - .NET | Microsoft Learn
Compile-time logging source generation - .NET | Microsoft Learn
868 открытий4К показов