Подтвердить что ты не робот

Ошибка log4net BufferingForwardingAppender

РЕДАКТИРОВАТЬ 2: Я решил проблему (см. ответ ниже). Обратите внимание, что эта проблема потенциально влияет на все приложения, украшенные BufferingForwardingAppender, а также на все наследующие от BufferingAppenderSkeleton (соответственно: AdoNetAppender, RemotingAppender, SmtpAppender и SmtpPickupDirAppender) *

Я делал несколько базовых сканеров log4net, и я попытался украсить RollingFileAppender с помощью BufferingForwardingAppender.

Я испытываю ужасную производительность, проходящую через BufferingForwardingAppender, а не напрямую через RollingFileAppender, и я действительно не понимаю причину.

Вот моя конфигурация:

<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
  <file value="c:\" />
  <appendToFile value="false" />
  <rollingStyle value="Composite" />
  <datePattern value="'.'MMdd-HH'.log'" />
  <maxSizeRollBackups value="168" />
  <staticLogFileName value="false" />
  <layout type="log4net.Layout.PatternLayout">      
    <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
  </layout>
</appender>

<appender name="BufferingForwardingAppender" type="log4net.Appender.BufferingForwardingAppender">
  <bufferSize value="512" />
  <appender-ref ref="RollingLogFileAppender" />
</appender>

<root>
  <level value="DEBUG" />
  <appender-ref ref="BufferingForwardingAppender" />    
</root>

И вот тест (очень простой код):

var stopWatch = new Stopwatch();
stopWatch.Start();            
for (int i = 0; i < 100000; i++)            
{
   Log.Debug("Hello");
}
stopWatch.Stop();
Console.WriteLine("Done in {0} ms", stopWatch.ElapsedMilliseconds);

Переход непосредственно через RollingFileAppender:

Выполнено в 511 мс

В то время как через BufferingForwardingAppender украшает RollingFileAppender:

Выполнено в 14261 мс

Это примерно в 30 раз медленнее.

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

Мне кажется, что конфигурация в порядке, так что это действительно странно.

Кто-нибудь понял?

Спасибо!

РЕДАКТИРОВАТЬ 1:

Поведение строго одинаково, обертывая/украшая FileAppender или даже ConsoleAppender (все еще есть пример базового BufferingForwardingAppender, который обертывает/украшает ConsoleAppender в официальных конфигурационных файлах log4net.. и ничего конкретного не упоминается в отношении производительности).

После некоторого исследования/профилирования я вижу, что большая часть времени испорчена внутри BufferingForwardingAppender более конкретно в вызове WindowsIdentity.GetCurrent()... который называется КАЖДОЕ время, когда мы вызываем вызов Log.Debug(). в предыдущем примере (100 К раз в источнике выборки выше).

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

Стек частичного вызова:

  • AppenderSkeleton.DoAppend
  • BufferingAppenderSkeleton.Append
  • LoggingEvent.FixVolatileData​​li >
  • LoggingEvent.get_UserName()

Вызов get_LocationInformation() также выполняется в FixVolatileData, что также приводит к высокой первоначальной стоимости (каждый раз отслеживать трассировку стека).

Теперь я пытаюсь понять, почему этот чрезвычайно дорогостоящий вызов FixVolatileData (по крайней мере, для исправления) возникает для каждого события журнала в этом контексте, тогда как переход непосредственно через завернутый appender (непосредственно через ConsoleAppender/FileAppender..) не выполняется этот вид операции.

Предстоящее обновление, за которым следует, если кто-то не получил ответ на все это:)

Спасибо!

4b9b3361

Ответ 1

Я выяснил эту проблему.

BufferingForwardingAppender наследует от BufferingAppenderSkeleton (как и другие приложения, использующие буферизацию событий регистрации, например AdoNetAppender, RemotingAppender, SmtpAppender..).

BufferingAppenderSkeleton фактически выполняет буферизацию событий регистрации до фактического доставки их в целевое приложение после выполнения определенного условия (например, буфер заполнен).

В соответствии с документацией класса LoggingEvent (представляющим событие регистрации и содержащим все значения (message, threadid...) события):

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

Эти "изменчивые" свойства представлены перечислением FixFlags, содержащим такие флаги, как Message, ThreadName, UserName, Identity... так что все летучие свойства. Он также содержит флаг "Нет" (исправить никакие свойства), "Все" (исправить все свойства) и "Частично" (исправить только определенные предопределенные значения свойств).

Whem BufferingAppenderSkeleton определяется с помощью DEFAULT, он устанавливает фиксацию на "Все", что означает, что все "изменчивые" свойства должны быть исправлены.

В этом контексте для каждого LoggingEvent, добавленного в BufferingAppenderSkeleton, все "изменчивые" свойства будут исправлены до того, как событие будет вставлено в буфер. Сюда входят свойства Identity (имя пользователя) и LocationInformation (трассировка стека), даже если эти свойства не включены в макет (но, я думаю, это имеет какой-то смысл, если макет был изменен для включения этих свойств позднее, в то время как буфер уже заполнено LoggingEvents).

Однако в моем случае это действительно HURTS производительность. Я не включаю Identity и LocationInformation в свой макет и не планирую (в основном, для проблем с производительностью)

Теперь для решения...

В BufferingAppenderSkeleton есть два свойства, которые могут использоваться для управления значением флага FixFlags для BufferingAppenderSkeleton (по умолчанию он установлен на "ВСЕ", что не очень приятно!). Эти два свойства: Fix (тип FixFlags) и OnlyFixPartialEventData (тип bool).

Для тонкой настройки значения флага или для отключения всех исправлений следует использовать свойство Fix. Для определенной частичной предопределенной комбинации флагов (не включая Identity или LocationInfo) вместо OnlyFixPartialEventData можно установить значение "true".

Если я повторно использую образец конфигурации выше (в моем вопросе), единственное изменение, внесенное в конфигурацию для повышения производительности, указано ниже:

<appender name="BufferingForwardingAppender" type="log4net.Appender.BufferingForwardingAppender">
  <bufferSize value="512" />
  <appender-ref ref="RollingLogFileAppender" />
  <Fix value="0"/> <!-- Set Fix flag to NONE -->
</appender>

Используя эту измененную конфигурацию, выполнение тестового кода, представленное в моем вопросе выше, снижается с приблизительно 14000мс до 230 мс (быстрее на 60 раз)! И если я использую <OnlyFixPartialEventData value="true"/> вместо того, чтобы отключать все исправления, он принимает около 350 мс.

К сожалению, этот флаг не очень хорошо документирован (за исключением документации по SDK, немного). Поэтому мне пришлось глубоко вникать в источники log4net, чтобы найти проблему.

Это особенно проблематично, особенно в "образцовых" образцах конфигурации, этот флаг отсутствует нигде (http://logging.apache.org/log4net/release/config-examples.html). Таким образом, образцы, предоставленные для BufferingForwardingAppender, и AdoNetAppender (и другие наследователи от BufferingAppenderSkeleton), будут давать пользователям TERRIBLE производительность, даже если макет, который они используют, довольно минимален.

Ответ 2

Возможно ли это потому, что вы не указываете шаблон макета в BufferingForwardingAppender, но вы находитесь в RollingLogFileAppender, поэтому BufferingForwardingAppender включает в себя все в нем выход, включая имя пользователя (% username)

Ниже представлена ​​интересная статья в блоге, в которой есть список параметров в макетах шаблонов, и похоже, что некоторые из них отмечены как медленные.

http://www.beefycode.com/post/Log4Net-Tutorial-pt-4-Layouts-and-Patterns.aspx