Skip to content

[API Proposal]: Allow custom filtering logic for FakeLogger #5615

@Piedone

Description

@Piedone

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.

Metadata

Metadata

Labels

api-approvedAPI was approved in API review, it can be implementedarea-telemetry

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions