Skip to content

Commit

Permalink
FilteringTargetWrapper - Add support for batch writing and use of whe…
Browse files Browse the repository at this point in the history
…nRepeated as Filter
  • Loading branch information
snakefoot committed May 18, 2019
1 parent 3d07bd9 commit 9324129
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 47 deletions.
29 changes: 29 additions & 0 deletions src/NLog/Config/LoggingConfigurationParser.cs
Expand Up @@ -1021,6 +1021,11 @@ private void SetPropertyFromElement(object o, ILoggingConfigurationElement eleme
return;
}

if (SetFilterFromElement(o, propInfo, element))
{
return;
}

SetItemFromElement(o, propInfo, element);
}

Expand Down Expand Up @@ -1095,6 +1100,30 @@ private Layout TryCreateLayoutInstance(ILoggingConfigurationElement element, Typ
return _configurationItemFactory.Layouts.CreateInstance(ExpandSimpleVariables(layoutTypeName));
}

private bool SetFilterFromElement(object o, PropertyInfo propInfo, ILoggingConfigurationElement layoutElement)
{
// Check if it is a Filter
if (!typeof(Filter).IsAssignableFrom(propInfo.PropertyType))
return false;

// Check if the 'type' attribute has been specified
string filterTypeName = GetConfigItemTypeAttribute(layoutElement);
if (filterTypeName == null)
return false;

Filter filter = _configurationItemFactory.Filters.CreateInstance(ExpandSimpleVariables(filterTypeName));
// and is a Filter and 'type' attribute has been specified
if (filter != null)
{
ConfigureObjectFromAttributes(filter, layoutElement, true);
ConfigureObjectFromElement(filter, layoutElement);
propInfo.SetValue(o, filter, null);
return true;
}

return false;
}

private void SetItemFromElement(object o, PropertyInfo propInfo, ILoggingConfigurationElement element)
{
object item = propInfo.GetValue(o, null);
Expand Down
4 changes: 3 additions & 1 deletion src/NLog/Filters/ConditionBasedFilter.cs
Expand Up @@ -55,6 +55,8 @@ public class ConditionBasedFilter : Filter
[RequiredParameter]
public ConditionExpression Condition { get; set; }

internal FilterResult DefaultFilterResult { get; set; } = FilterResult.Neutral;

/// <summary>
/// Checks whether log event should be logged or not.
/// </summary>
Expand All @@ -72,7 +74,7 @@ protected override FilterResult Check(LogEventInfo logEvent)
return Action;
}

return FilterResult.Neutral;
return DefaultFilterResult;
}
}
}
92 changes: 81 additions & 11 deletions src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs
Expand Up @@ -33,11 +33,11 @@

namespace NLog.Targets.Wrappers
{
using System;
using Common;
using Conditions;
using Config;
using Internal;
using System.Collections.Generic;
using NLog.Common;
using NLog.Conditions;
using NLog.Config;
using NLog.Filters;

/// <summary>
/// Filters log entries based on a condition.
Expand All @@ -59,8 +59,6 @@ namespace NLog.Targets.Wrappers
[Target("FilteringWrapper", IsWrapper = true)]
public class FilteringTargetWrapper : WrapperTargetBase
{
private static readonly object boxedBooleanTrue = true;

/// <summary>
/// Initializes a new instance of the <see cref="FilteringTargetWrapper" /> class.
/// </summary>
Expand Down Expand Up @@ -90,16 +88,32 @@ public FilteringTargetWrapper(Target wrappedTarget, ConditionExpression conditio
{
WrappedTarget = wrappedTarget;
Condition = condition;
OptimizeBufferReuse = GetType() == typeof(FilteringTargetWrapper); // Class not sealed, reduce breaking changes
}

/// <summary>
/// Gets or sets the condition expression. Log events who meet this condition will be forwarded
/// to the wrapped target.
/// </summary>
/// <docgen category='Filtering Options' order='10' />
public ConditionExpression Condition { get => (Filter as ConditionBasedFilter)?.Condition; set => Filter = value != null ? new ConditionBasedFilter() { Condition = value, DefaultFilterResult = FilterResult.Ignore } : null; }

/// <summary>
/// Gets or sets the filter. Log events who evaluates to <see cref="FilterResult.Ignore"/> will be discarded
/// </summary>
/// <docgen category='Filtering Options' order='10' />
[RequiredParameter]
public ConditionExpression Condition { get; set; }
public Filter Filter { get; set; }

/// <inheritdoc/>
protected override void InitializeTarget()
{
base.InitializeTarget();

if (!OptimizeBufferReuse && WrappedTarget != null && WrappedTarget.OptimizeBufferReuse)
{
OptimizeBufferReuse = GetType() == typeof(FilteringTargetWrapper); // Class not sealed, reduce breaking changes
}
}

/// <summary>
/// Checks the condition against the passed log event.
Expand All @@ -109,8 +123,7 @@ public FilteringTargetWrapper(Target wrappedTarget, ConditionExpression conditio
/// <param name="logEvent">Log event.</param>
protected override void Write(AsyncLogEventInfo logEvent)
{
object v = Condition.Evaluate(logEvent.LogEvent);
if (boxedBooleanTrue.Equals(v))
if (FilterIncludeLogEvent(logEvent.LogEvent))
{
WrappedTarget.WriteAsyncLogEvent(logEvent);
}
Expand All @@ -119,5 +132,62 @@ protected override void Write(AsyncLogEventInfo logEvent)
logEvent.Continuation(null);
}
}

/// <inheritdoc/>
protected override void Write(IList<AsyncLogEventInfo> logEvents)
{
bool hasIgnoredLogEvents = false;
IList<AsyncLogEventInfo> filterLogEvents = null;
AsyncLogEventInfo filterLogEvent = default(AsyncLogEventInfo);
for (int i = 0; i < logEvents.Count; ++i)
{
if (FilterIncludeLogEvent(logEvents[i].LogEvent))
{
if (hasIgnoredLogEvents && filterLogEvents == null)
{
if (filterLogEvent.LogEvent != null)
{
filterLogEvents = new List<AsyncLogEventInfo>();
filterLogEvents.Add(filterLogEvent);
filterLogEvent = default(AsyncLogEventInfo);
}
else
{
filterLogEvent = logEvents[i];
}
}

if (filterLogEvents != null)
filterLogEvents.Add(logEvents[i]);
}
else
{
if (!hasIgnoredLogEvents && i > 0)
{
filterLogEvents = new List<AsyncLogEventInfo>();
for (int j = 0; j < i; ++j)
filterLogEvents.Add(logEvents[j]);
}
hasIgnoredLogEvents = true;
logEvents[i].Continuation(null);
}
}

if (!hasIgnoredLogEvents)
WrappedTarget.WriteAsyncLogEvents(logEvents);
else if (filterLogEvents != null)
WrappedTarget.WriteAsyncLogEvents(filterLogEvents);
else if (filterLogEvent.LogEvent != null)
WrappedTarget.WriteAsyncLogEvent(filterLogEvent);
}

private bool FilterIncludeLogEvent(LogEventInfo logEvent)
{
var filterResult = Filter.GetFilterResult(logEvent);
if (filterResult == FilterResult.Ignore || filterResult == FilterResult.IgnoreFinal)
return false;
else
return true;
}
}
}
77 changes: 45 additions & 32 deletions src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs
Expand Up @@ -35,10 +35,10 @@ namespace NLog.Targets.Wrappers
{
using System;
using System.Collections.Generic;
using Common;
using Conditions;
using Config;
using Internal;
using NLog.Common;
using NLog.Conditions;
using NLog.Config;
using NLog.Internal;

/// <summary>
/// Filters buffered log entries based on a set of conditions that are evaluated on a group of events.
Expand Down Expand Up @@ -75,18 +75,17 @@ public class PostFilteringTargetWrapper : WrapperTargetBase
/// <summary>
/// Initializes a new instance of the <see cref="PostFilteringTargetWrapper" /> class.
/// </summary>
public PostFilteringTargetWrapper() : this(null)
public PostFilteringTargetWrapper()
: this(null)
{
Rules = new List<FilteringRule>();
}

/// <summary>
/// Initializes a new instance of the <see cref="PostFilteringTargetWrapper" /> class.
/// </summary>
public PostFilteringTargetWrapper(Target wrappedTarget)
: this(null, wrappedTarget)
{
Rules = new List<FilteringRule>();
WrappedTarget = wrappedTarget;
}

/// <summary>
Expand All @@ -95,9 +94,10 @@ public PostFilteringTargetWrapper(Target wrappedTarget)
/// <param name="name">Name of the target.</param>
/// <param name="wrappedTarget">The wrapped target.</param>
public PostFilteringTargetWrapper(string name, Target wrappedTarget)
: this(wrappedTarget)
{
Name = name;
WrappedTarget = wrappedTarget;
Rules = new List<FilteringRule>();
}

/// <summary>
Expand All @@ -115,6 +115,17 @@ public PostFilteringTargetWrapper(string name, Target wrappedTarget)
[ArrayParameter(typeof(FilteringRule), "when")]
public IList<FilteringRule> Rules { get; private set; }

/// <inheritdoc/>
protected override void InitializeTarget()
{
base.InitializeTarget();

if (!OptimizeBufferReuse && WrappedTarget != null && WrappedTarget.OptimizeBufferReuse)
{
OptimizeBufferReuse = GetType() == typeof(PostFilteringTargetWrapper); // Class not sealed, reduce breaking changes
}
}

/// <summary>
/// NOTE! Obsolete, instead override Write(IList{AsyncLogEventInfo} logEvents)
///
Expand All @@ -138,22 +149,17 @@ protected override void Write(AsyncLogEventInfo[] logEvents)
/// <param name="logEvents">Array of log events to be post-filtered.</param>
protected override void Write(IList<AsyncLogEventInfo> logEvents)
{


InternalLogger.Trace("PostFilteringWrapper(Name={0}): Running on {1} events", Name, logEvents.Count);

var resultFilter = EvaluateAllRules(logEvents) ?? DefaultFilter;

if (resultFilter == null)
{
WrappedTarget.WriteAsyncLogEvents(logEvents);
}
else
{
InternalLogger.Trace("PostFilteringWrapper(Name={0}): Filter to apply: {1}", Name, resultFilter);

var resultBuffer = ApplyFilter(logEvents, resultFilter);

InternalLogger.Trace("PostFilteringWrapper(Name={0}): After filtering: {1} events.", Name, resultBuffer.Count);
if (resultBuffer.Count > 0)
{
Expand All @@ -169,25 +175,38 @@ protected override void Write(IList<AsyncLogEventInfo> logEvents)
/// <param name="logEvents"></param>
/// <param name="resultFilter"></param>
/// <returns></returns>
private static List<AsyncLogEventInfo> ApplyFilter(IList<AsyncLogEventInfo> logEvents, ConditionExpression resultFilter)
private static IList<AsyncLogEventInfo> ApplyFilter(IList<AsyncLogEventInfo> logEvents, ConditionExpression resultFilter)
{
var resultBuffer = new List<AsyncLogEventInfo>();

bool hasIgnoredLogEvents = false;
IList<AsyncLogEventInfo> resultBuffer = null;
for (int i = 0; i < logEvents.Count; ++i)
{
object v = resultFilter.Evaluate(logEvents[i].LogEvent);
if (boxedTrue.Equals(v))
{
resultBuffer.Add(logEvents[i]);
if (hasIgnoredLogEvents && resultBuffer == null)
{
resultBuffer = new List<AsyncLogEventInfo>();
}

if (resultBuffer != null)
resultBuffer.Add(logEvents[i]);
}
else
{
if (!hasIgnoredLogEvents && i > 0)
{
resultBuffer = new List<AsyncLogEventInfo>();
for (int j = 0; j < i; ++j)
resultBuffer.Add(logEvents[j]);
}
hasIgnoredLogEvents = true;
// anything not passed down will be notified about successful completion
logEvents[i].Continuation(null);
}
}

return resultBuffer;
return resultBuffer ?? (hasIgnoredLogEvents ? ArrayHelper.Empty<AsyncLogEventInfo>() : logEvents);
}

/// <summary>
Expand All @@ -197,30 +216,24 @@ private static List<AsyncLogEventInfo> ApplyFilter(IList<AsyncLogEventInfo> logE
/// <returns></returns>
private ConditionExpression EvaluateAllRules(IList<AsyncLogEventInfo> logEvents)
{
ConditionExpression resultFilter = null;

if (Rules.Count == 0)
return null;

for (int i = 0; i < logEvents.Count; ++i)
{
foreach (FilteringRule rule in Rules)
for (int j = 0; j < Rules.Count; ++j)
{
var rule = Rules[j];
object v = rule.Exists.Evaluate(logEvents[i].LogEvent);

if (boxedTrue.Equals(v))
{
InternalLogger.Trace("PostFilteringWrapper(Name={0}): Rule matched: {1}", Name, rule.Exists);

resultFilter = rule.Filter;
break;
return rule.Filter;
}
}

if (resultFilter != null)
{
break;
}
}

return resultFilter;
return null;
}
}
}
4 changes: 1 addition & 3 deletions tests/NLog.UnitTests/Targets/TargetTests.cs
Expand Up @@ -84,8 +84,6 @@ public void TargetContructorWithNameTest()

var args = new List<object> { fileTarget };



//default ctor
var defaultConstructedTarget = (WrapperTargetBase)Activator.CreateInstance(targetType);
defaultConstructedTarget.Name = name;
Expand All @@ -94,7 +92,7 @@ public void TargetContructorWithNameTest()
//specials cases
if (targetType == typeof(FilteringTargetWrapper))
{
var cond = new ConditionLoggerNameExpression();
ConditionLoggerNameExpression cond = null;
args.Add(cond);
var target = (FilteringTargetWrapper) defaultConstructedTarget;
target.Condition = cond;
Expand Down

0 comments on commit 9324129

Please sign in to comment.