#.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.