Это стоит вам МНОГО производительности, если вы не используете ведение журнала правильно!

Пакет NuGet Microsoft.Extensions.Logging является наиболее загружаемым пакетом ведения журнала NuGet после Serilog. Как наиболее часто используемый, он также имеет ловушку, в которую вы попадаете очень легко и которая стоит вам много производительности.

1. Ненужные вызовы журналирования

Первая и, возможно, самая серьезная ошибка — слишком много вызовов ILogger, даже если они даже не обработаны, из-за слишком низкого LogLevel.

Обычно, когда вы реализуете ведение журнала, вы различаете разные уровни LogLevels, от Trace до Critical. Если вы настроили регистратор только для регистрации событий Error, все, что ниже этого уровня, будет игнорироваться. Или будет?

Как правило, чем ниже LogLevel, тем больше журналов у вас может быть, поскольку вы потенциально регистрируете все для целей отладки. В принципе, это не так, однако вы должны делать это правильно и помнить о побочных эффектах, когда это делается неправильно.

Взгляните на следующий код:

На первый взгляд выглядит как законный способ регистрации отладочных записей. Однако здесь есть скрытая ловушка: интерполяция строк.
Интерполяция строки всегда приводит к тому, что строка (строки) упаковываются и, таким образом, помещаются в кучу.

Теперь представьте, что вы вызываете эти методы журнала несколько тысяч раз и устанавливаете для LogLevel значение Error. Для каждого вызова здесь вы выделяете память в куче, которая в конечном итоге должна быть собрана сборщиком мусора.

Во время сборки мусора ваше приложение полностью перестает выполняться!

Это стоит вам очень много производительности, вы могли бы очень легко сэкономить. Либо полностью удалив ненужные журналы, ИЛИ предварительно проверив, нужно ли вообще здесь вести журнал:

Теперь это выглядит красиво, правда? Ну, не совсем, но мы увидим, что мы можем сделать с этим позже в этой статье. Тем не менее, мы только что избавили нас от большого количества сборки мусора, так как строки не интерполируются и, следовательно, не помещаются в кучу.

2. Не использовать функцию шаблонов

Возможно, вы заметили, что ваша IDE предупреждает вас, когда вы передаете простую строку в качестве параметра message функции ведения журнала:

Это результат того, что вы все время неправильно использовали функцию ведения журнала! Вместо того, чтобы просто указать простую строку в качестве первого аргумента метода журнала, вы передаете шаблон и аргументы для шаблона. Предполагаемый способ использования функции ведения журнала выглядит следующим образом:

ItemId и ItemName — это именованные параметры, для которых вы должны передавать аргументы после строки в том же порядке, в котором они встречаются в шаблоне.

Использование этого подхода имеет несколько неочевидных на первый взгляд преимуществ:

  • Внешний инструмент мониторинга (например, Azure AppInsights или AWS CloudWatch) также регистрирует имена и значения параметров в дополнение к сообщению журнала. Тогда вы сможете отфильтровать более подробно там.
  • В реализации по умолчанию нет преимуществ в производительности. Однако при использовании этого подхода в сочетании с методом расширения cutom эти параметры могут быть типизированы в общем виде и, таким образом, не упакованы, что позволяет экономить память в куче, когда регистратор не ведет журнал.

Давайте посмотрим, как мы можем использовать эти преимущества в производительности.

Выполнение

Теперь, когда мы определили две основные ошибки при логировании, давайте реализуем решение, которое позволит нам правильно их использовать:

Это специальные методы расширения, которые заменят исходный вызов LogInformation на метод ILogger. Пожалуйста, обратите особое внимание на аргументы универсального типа здесь. Благодаря тому, что они являются общими, это позволяет передавать переданные аргументы НЕ и тем самым экономить потенциально выделенную память кучи.

Теперь вам просто нужно создать все эти методы расширения для всех LogLevel, и все готово. Или есть более быстрый способ?

Вместо того, чтобы самостоятельно реализовывать эту логику и, возможно, многие другие существующие методы, вы можете просто использовать пакет NuGet Serilog. В этом пакете все это предварительно реализовано, он очень расширяемый и имеет множество функций, которые уже оптимизированы для производительности.

Ориентиры

Чтобы визуализировать, какое влияние на самом деле оказывают эти ошибки, позвольте мне протестировать это с помощью некоторых тестов в BenchmarkDotNet:

Здесь я сравниваю все упомянутые варианты ведения журнала, один раз с Microsoft.Extensions.Logging.ILogger (Microsoft-Suffix), включая пользовательский метод расширения Information(), и один раз с Serilog.ILogger по умолчанию (Serilog-Suffix).

На моей машине эти тесты приводят к следующим результатам:

Совершенно очевидно, что Serilog по умолчанию имеет некоторые преимущества в производительности ведения журнала. Тем не менее, с нашими пользовательскими методами расширения для Microsoft ILogger мы можем достичь примерно такой же производительности Иотсутствия выделения памяти в куче, когда мы ниже нашего LogLevel! Это сэкономит вам МНОГО производительности.

Теперь давайте займемся математикой, мы только что (не) зарегистрировали небольшую строку 1000 раз, и общий объем выделенной памяти уже составляет около 50 КБ. Когда у нас был вызов ILogger внутри цикла for, который повторяется в 10 000 раз больше, и мы используем строку большего размера, у нас уже был бы 1 ГБ общей выделенной памяти кучи, которую необходимо собрать сборщиком мусора. все время и, таким образом, приостанавливает все ваше приложение более чем на секунду в минуту!

Я надеюсь, что эти методы помогут вам определить некоторые проблемы с производительностью в вашем приложении. Для меня они точно были!

Спасибо, что нашли время прочитать эту статью. Надеюсь, вы нашли его информативным, познавательным и интересным.
Мы очень ценим вашу поддержку и участие.

Если вы заинтересованы в том, чтобы быть в курсе последних тенденций, советов и приемов для чистой архитектуры, чистого кодирования и новейших технологических стеков, особенно в контексте C #, .NET и Angular, я был бы признателен. если вы решили следовать за мной.

Удачного дня!

Если вы еще не используете Medium для ежедневного расширения своих знаний, сейчас самое время начать! С Medium вы можете легко получить больше знаний по высокопрофессиональным темам, публиковать качественный контент и охватить более широкую аудиторию. Чтобы начать, просто создайте учетную запись Medium, используя эту ссылку:

Присоединиться к Medium сейчас

Таким образом, вы получите доступ к мощной платформе, которая поможет вам общаться с новыми писателями и читателями и каждый день узнавать что-то новое.