-
Notifications
You must be signed in to change notification settings - Fork 849
Description
Background and motivation
FakeLogger is useful not just in a unit, but also in an end-to-end/UI testing use case. We also started to use it in our Lombiq UI Testing Toolbox for Orchard Core project.
In this use case, one is looking to run assertions on the log entries. However, when testing a complete app, the log can include entries that we can safely ignore, but these make assertions more complicated. E.g. instead of "the log should be empty" we need to assert "the log should be empty except for entries with the ignored message message".
The best would be to not log these messages in the first place, but in our experience, this is frequently not possible even after applying the existing log filter rules (e.g. filtering out complete classes). We'd need to target specific category-logger-log level-message combinations.
I'd like to propose the ability to optionally configure a custom filter delegate for this. It would work together with the existing filters, AND-ing with them.
API Proposal
public class FakeLogCollectorOptions
{
/// <summary>
/// Gets or sets custom filter for which records are collected.
/// </summary>
/// <value>The default is <see langword="null" />.</value>
/// <remarks>
/// Defaults to <see langword="null" /> which doesn't apply any additional filter to the records.
/// If not empty, only records for which the filter function returns <see langword="true" /> will be collected by the fake logger.
/// </remarks>
[Experimental(DiagnosticIds.Experiments.Telemetry)]
public Func<FakeLogRecord, bool>? CustomFilter { get; set; }
}API Usage
ConfigureLogging(loggingBuilder => loggingBuilder.AddFakeLogging(options =>
{
options.CustomFilter = record => record.Message != "ignored message"
}))Alternative Designs
Non-nullable type with default 'pass-through' filter
public Func<FakeLogRecord, bool> CustomFilter { get; set; } = _ => true;
This is more consistent with other existing properties, but may result in NRE when supplied null by the user.
Inversed semantics of the boolean value returned by the filter function
true returned by the predicate function would change meaning from "let the record pass" to "catch the record by the filter and don't let it pass". However, the current semantics is in line with other filtering properties and is familiar filtering approach in e.g. other programming languages such as javascripts .filter().
Workarounds
While not an alternative design, a workaround is to adjust the log levels of entries, so they can be filtered on that. However, in our case, due to the log entries coming from external projects that we can't all feasibly change, this is not a suitable solution.
Risks
This is a non-breaking change.
A custom filter predicate, if implemented inefficiently, can potentially slow logging down a lot. Since we're talking only about specific testing scenarios where other filtering is not suitable, I'd consider this acceptable.