Thursday, July 31, 2014

log4net: Custom Filter

I have been playing around with log4net and some of its advanced features.  I wanted to blog about a custom filter I wrote: CompoundFilter.  This filter allows me to combine multiple standard filters into one “if all these filters are true, then…”.  Specifically, what I wanted to do was define an Appender, but then filter messages based on BOTH logger (LoggerMatchFilter) AND Level (LevelMatchFilter or LevelRangeFilter).

In a project called Log4Net.CustomFilter, I added a (nuget) log4net reference, then created CompoundFilter.cs:

using System;
using System.Collections.Generic;
using log4net.Core;
using log4net.Filter;

namespace Log4Net.CustomFilters
{
public class CompoundFilter : FilterSkeleton
{
private bool _acceptOnMatch;
private readonly IList<IFilter> _filters = new List<IFilter>();

public override FilterDecision Decide(LoggingEvent loggingEvent)
{
if (loggingEvent == null)
throw new ArgumentNullException("loggingEvent");

foreach (IFilter filter in _filters)
{
if (filter.Decide(loggingEvent) != FilterDecision.Accept)
return FilterDecision.Neutral; // one of the filter has failed
}

// All conditions are true
return _acceptOnMatch ? FilterDecision.Accept : FilterDecision.Deny;
}

public IFilter Filter
{
set { _filters.Add(value); }
}

public bool AcceptOnMatch
{
get { return _acceptOnMatch; }
set { _acceptOnMatch = value; }
}
}
}


Then, in my main application, I add a reference to the project (better – add a reference to the DLL).  Then, in the log4net.config file, you can add this syntax:

<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n"/>
</layout>

<filter type="Log4Net.CustomFilters.CompoundFilter, Log4Net.CustomFilters">
<filter type="log4net.Filter.LoggerMatchFilter">
<loggerToMatch value="MultiConnectG2.MainService" />
</filter>
<filter type="log4net.Filter.LevelMatchFilter">
<levelToMatch value="DEBUG"/>
</filter>
<acceptOnMatch value="true" />
</filter>

<filter type="log4net.Filter.LoggerMatchFilter">
<loggerToMatch value="MultiConnectG2.MainService" />
<acceptOnMatch value="false" />
</filter>

</appender>


The first filter uses the new CompoundFilter and tells it what assembly to find it in (Log4Net.CustomFilters).  Inside, we specify 2 standard filters: LoggerMatchFilter and LevelMatchFilter.  What this means is that this Compound filter will match if the logger’s name is MultiConnectG2.MainService and the level of the message is DEBUG.  And, if both of these match, then we should accept this message.


The second filter is a simply LoggerMatchFilter that simply says, if the logger name is MultiConnectG2.MainService, then do not accept the message.


Filters quit trying to match once the first filter is matched.  So this appender will ONLY allow DEBUG messages from any logger who’s name starts with MultiConnectG2.MainService and reject any other message from those loggers.


Any other logger (with a name that does not start with MultiConnectG2.MainService) will be processed normally through this appender.

No comments:

Post a Comment