иерархия log4net logger с похожими именами корней журналов

#.net #logging #log4net #hierarchy #appender

#.net #ведение журнала #log4net — вход в систему #иерархия #добавить #log4net #добавляющий

Вопрос:

Я нашел здесь ошибку или я делаю что-то не так с этой конфигурацией?

 <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%message%newline" />
  </layout>
</appender>
<logger name="MyApp.Common">
  <level value="WARN" />
  <appender-ref ref="ConsoleAppender" />
</logger>
<logger name="MyApp.Common.Namespace.SomeGenericClass`1">
  <level value="INFO" />
  <appender-ref ref="ConsoleAppender" />
</logger>
  

Предполагая, что я назвал свои регистраторы в соответствии со стандартом «…Схема именования типа объявления, я ожидаю, что все, что находится в пространстве имен MyApp.Common, будет регистрироваться на консоли, если оно имеет значение WARN или выше. Все, что находится в SomeGenericClass `1, будет зарегистрировано, если это INFO или выше (и дважды, если это WARN или выше из-за WARN logger).

Происходит то, что все, что в SomeGenericClass `1, которое получает ИНФОРМАЦИЮ о входе в систему, будет передано на консоль дважды вместо ожидаемого одного раза. Если я удалю более конкретный регистратор, ничего не будет зарегистрировано, а если я удалю менее конкретный регистратор, все будет регистрироваться только один раз, как и ожидалось. Кроме того, изменение порядка регистраторов в файле конфигурации на противоположный ничего не дает (как я и ожидал, поскольку я предполагаю, что порядок не имеет значения).

Я нашел здесь ошибку или я упускаю что-то важное в том, как работает иерархия?

Ответ №1:

Мой экспериментальный опыт использования нескольких узлов logger заключается в том, что log4net будет использовать самый низкий уровень, который вы устанавливаете в любом конкретном logger. Итак, даже если вы указали WARN в MyApp.Common, используя INFO в MyApp.Common.Из-за SomeGenericClass параметр глобального (?) уровня отключился, чтобы ПРЕДУПРЕДИТЬ. Итак, вы получили запись из MyApp.Common, а также MyApp.Common.SomeGenericClass, поскольку приложение ConsoleAppender дважды запускалось.

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

Я создал два разных регистратора, таких как у вас выше, хотя один из них root, он будет работать одинаково. Затем я создал два разных приложения, каждое из которых фильтровало разные уровни ошибок, и ссылался на них отдельно в двух регистраторах. Что я получаю, так это журналы из Othern Namespace в одном файле и все остальное в другом. Код для этого приведен ниже.

 <log4net>
<logger name="OtherNamespace">
  <appender-ref ref="OtherNamespaceAppender"/>
</logger>
<root>
  <level value="WARN"/>
  <appender-ref ref="MyRollingFileAppender"/>
</root>
<appender name="MyRollingFileAppender" type="log4net.Appender.RollingFileAppender">
  <file type="log4net.Util.PatternString" value="C:ComanchemylogfileFor_%property{User}.log" />
  <appendToFile value="true" />
  <rollingStyle value="Size" />
  <threshold value="ERROR" />
  <countDirection value="3" />
  <maxSizeRollBackups value="2" />
  <maximumFileSize value="200KB" />
  <staticLogFileName value="true" />
  <layout type="log4net.Layout.PatternLayout">
    <param name="ConversionPattern" value="%-6level%utcdate{ABSOLUTE} – %location :: %message%newline" />
  </layout>
</appender>
<appender name="OtherNamespaceAppender" type="log4net.Appender.RollingFileAppender">
  <file value="C:ComancheOtherNamespace.log" />
  <appendToFile value="true" />
  <rollingStyle value="Size" />
  <threshold value="WARN" />
  <countDirection value="3" />
  <maxSizeRollBackups value="2" />
  <maximumFileSize value="200KB" />
  <staticLogFileName value="true" />
  <filter type="log4net.Filter.LoggerMatchFilter">
    <acceptOnMatch value="true" />
    <LoggerToMatch value="OtherNamespace" />
    <!-- set your class name here -->
  </filter>

  <filter type="log4net.Filter.DenyAllFilter" />

  <layout type="log4net.Layout.PatternLayout">
    <param name="ConversionPattern" value="%-6level%utcdate{ABSOLUTE} :: %message%newline" />
  </layout>
</appender>
  

Имейте в виду, что «все остальное» находилось в другом пространстве имен, поэтому оно не было перенаправлено в регистратор OtherNamespace, что могло бы быть, если бы было какое-то совпадение имен. Также обратите внимание, что, поскольку все наследуется от root, если бы я изменил уровень ведения журнала в приложении, на которое ссылается root, на WARN, я бы также получил журналы OtherNamespace. Все, чего я хотел добиться, это чтобы более подробное ведение журнала велось в файл, отличный от всего остального.

Ответ №2:

Я думаю, это должно делать то, что вы хотите (не тестировал это):

 <logger name="MyApp.Common">
     <level value="WARN" />
     <appender-ref ref="ConsoleAppender" />
</logger>
<logger name="MyApp.Common.Namespace.SomeGenericClass`1">
     <level value="INFO" />  
</logger>
  

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