Henrik Olsson
Henrik Olsson

Reputation: 757

Log4net - modify logging levels for individual logger and appender combinations

I have two appenders, one (InfoAppender) with a levelMin info filter and one (DebugAppender) without filter. This works great. I can also set minimum logging levels for individual loggers using the element. But now I want to have most loggers logging info (and above) to InfoAppender and debug (and above) to DebugAppender, but a certain chatty logger (NHibernate) logging warn (and above) to InfoAppender and info (and above) to DebugAppender.

I looked at the solution provided in Log4Net Logging of two different levels to two different appenders for the same logger, but it doesn't work for me. I have the following configuration:

<log4net>
  <appender name="InfoAppender" type="log4net.Appender.RollingFileAppender">
    <file value="C:\Logs\" />
    <appendToFile value="true" />
    <rollingStyle value="Date" />
    <datePattern value="'Info.'yyyy-MM-dd'.log.txt'"/>
    <staticLogFileName value="false" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
    </layout>
    <filter type="log4net.Filter.LevelRangeFilter">
      <levelMin value="INFO" />
    </filter>
  </appender>
  <appender name="DebugAppender" type="log4net.Appender.RollingFileAppender">
    <file value="C:\Logs\" />
    <appendToFile value="true" />
    <rollingStyle value="Date" />
    <datePattern value="'Debug.'yyyy-MM-dd'.log.txt'"/>
    <staticLogFileName value="false" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
    </layout>
  </appender>
  <logger name="NHibernate" additivity="false">
    <appender-ref ref="InfoAppender">
      <threshold value="WARN" />
    </appender-ref>
    <appender-ref ref="DebugAppender">
      <threshold value="INFO" />
    </appender-ref>
  </logger>
  <root>
    <level value="DEBUG" />
    <appender-ref ref="InfoAppender" />
    <appender-ref ref="DebugAppender" />
  </root>
</log4net>

I have the following test code:

        XmlConfigurator.Configure();
        var logger1 = LogManager.GetLogger("Program");
        var logger2 = LogManager.GetLogger("NHibernate");
        logger1.Debug("Debug message");
        logger2.Debug("Debug message");
        logger1.Info("Info message");
        logger2.Info("Info message");
        logger1.Warn("Warn message");
        logger2.Warn("Warn message");

But both debug messages are logged to DebugAppender and both info messages are logged to InfoAppender.

Upvotes: 2

Views: 971

Answers (1)

samy
samy

Reputation: 14962

The configuration mechanism of log4net is quite lenient but doesn't tell you when some parameters are not taken into account. For example in your configuration the following references the appender InfoAppender but the threshold property doesn't do anything

 <appender-ref ref="InfoAppender">
    <threshold value="WARN" />
  </appender-ref>

No parsing of xml element below the appender-ref node occur, you could put anything it won't be taken into account:

protected void ParseChildrenOfLoggerElement(XmlElement catElement, Logger log, bool isRoot)
// some code ommited
        if (xmlElement.LocalName == "appender-ref")
        {
            IAppender appender = this.FindAppenderByReference(xmlElement);
            string attribute = xmlElement.GetAttribute("ref");
            if (appender != null)
            {
                LogLog.Debug(string.Concat(new string[]
                {
                    "XmlHierarchyConfigurator: Adding appender named [",
                    attribute,
                    "] to logger [",
                    log.Name,
                    "]."
                }));
                log.AddAppender(appender);
            }
            else
            {
                LogLog.Error("XmlHierarchyConfigurator: Appender named [" + attribute + "] not found.");
            }
        }

What you need to do is add a filter between your logger and your final appender. To do this I recommend using the ForwardingAppender which lets you pass log events between appenders while adding some behavior: change the NHibernate section of your config file to this:

<appender name="NHibernateDebugFilterAppender" type="log4net.Appender.ForwardingAppender">
  <appender-ref ref="DebugAppender"/>
  <filter type="log4net.Filter.LevelRangeFilter">
    <levelMin value="INFO" />
  </filter>
</appender>

<appender name="NHibernateInfoFilterAppender" type="log4net.Appender.ForwardingAppender">
  <appender-ref ref="InfoAppender"/>
  <filter type="log4net.Filter.LevelRangeFilter">
    <levelMin value="WARN" />
  </filter>
</appender>

<logger name="NHibernate" additivity="false">
  <appender-ref ref="NHibernateInfoFilterAppender" />
  <appender-ref ref="NHibernateDebugFilterAppender"/>
</logger>

Upvotes: 1

Related Questions