Skip to content

Commit 9db47fe

Browse files
authored
Allow custom filtering logic for FakeLogger (#5848)
* Adds new nullable predicate property CustomFilter to FakeLogCollectorOptions. * FakeLogCollector uses the new CustomFilter property when not null to filter out records if they have satisfied the previous filtering options but not the defined predicate.
1 parent d5aa457 commit 9db47fe

File tree

3 files changed

+77
-3
lines changed

3 files changed

+77
-3
lines changed

src/Libraries/Microsoft.Extensions.Diagnostics.Testing/Logging/FakeLogCollector.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,13 @@ internal void AddRecord(FakeLogRecord record)
129129
return;
130130
}
131131

132+
var customFilter = _options.CustomFilter;
133+
if (customFilter is not null && !customFilter(record))
134+
{
135+
// record was filtered out by a custom filter
136+
return;
137+
}
138+
132139
lock (_records)
133140
{
134141
_records.Add(record);

src/Libraries/Microsoft.Extensions.Diagnostics.Testing/Logging/FakeLogCollectorOptions.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
using System;
55
using System.Collections.Generic;
6-
using Microsoft.Extensions.Logging;
6+
using System.Diagnostics.CodeAnalysis;
7+
using Microsoft.Shared.DiagnosticIds;
78

89
#pragma warning disable CA2227 // Collection properties should be read only
910

@@ -34,6 +35,17 @@ public class FakeLogCollectorOptions
3435
/// </remarks>
3536
public ISet<LogLevel> FilteredLevels { get; set; } = new HashSet<LogLevel>();
3637

38+
/// <summary>
39+
/// Gets or sets custom filter for which records are collected.
40+
/// </summary>
41+
/// <value>The default is <see langword="null" />.</value>
42+
/// <remarks>
43+
/// Defaults to <see langword="null" /> which doesn't apply any additional filter to the records.
44+
/// If not empty, only records for which the filter function returns <see langword="true" /> will be collected by the fake logger.
45+
/// </remarks>
46+
[Experimental(DiagnosticIds.Experiments.Telemetry)]
47+
public Func<FakeLogRecord, bool>? CustomFilter { get; set; }
48+
3749
/// <summary>
3850
/// Gets or sets a value indicating whether to collect records that are logged when the associated log level is currently disabled.
3951
/// </summary>

test/Libraries/Microsoft.Extensions.Diagnostics.Testing.Tests/Logging/FakeLoggerTests.cs

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
using System.Globalization;
77
using System.Linq;
88
using System.Threading;
9-
using Microsoft.Extensions.Logging;
10-
using Microsoft.Extensions.Logging.Testing;
119
using Microsoft.Extensions.Time.Testing;
1210
using Xunit;
1311

@@ -283,4 +281,61 @@ public void Scopes()
283281
Assert.Equal(42, (int)logger.LatestRecord.Scopes[0]!);
284282
Assert.Equal("Hello World", (string)logger.LatestRecord.Scopes[1]!);
285283
}
284+
285+
[Theory]
286+
[InlineData(false, 2)]
287+
[InlineData(true, 1)]
288+
public void FilterByCustomFilter(bool useErrorLevelFilter, int expectedRecordCount)
289+
{
290+
const string NotIgnoredMessage1 = "Not ignored message 1";
291+
const string NotIgnoredMessage2 = "Not ignored message 2";
292+
const string IgnoredMessage = "Ignored message";
293+
294+
// Given
295+
var options = new FakeLogCollectorOptions
296+
{
297+
CustomFilter = r => r.Message != IgnoredMessage,
298+
FilteredLevels = useErrorLevelFilter ? [LogLevel.Error] : new HashSet<LogLevel>(),
299+
};
300+
301+
var collector = FakeLogCollector.Create(options);
302+
var logger = new FakeLogger(collector);
303+
304+
// When
305+
logger.LogInformation(NotIgnoredMessage1);
306+
logger.LogInformation(IgnoredMessage);
307+
logger.LogError(IgnoredMessage);
308+
logger.LogError(NotIgnoredMessage2);
309+
logger.LogCritical(IgnoredMessage);
310+
311+
var records = logger.Collector.GetSnapshot();
312+
313+
// Then
314+
Assert.Equal(expectedRecordCount, records.Count);
315+
Assert.Equal(expectedRecordCount, logger.Collector.Count);
316+
317+
IList<(string message, LogLevel level, string prefix)> expectationsInOrder = useErrorLevelFilter
318+
? [(NotIgnoredMessage2, LogLevel.Error, "error] ")]
319+
: [(NotIgnoredMessage1, LogLevel.Information, "info] "), (NotIgnoredMessage2, LogLevel.Error, "error] ")];
320+
321+
for (var i = 0; i < expectedRecordCount; i++)
322+
{
323+
var (expectedMessage, expectedLevel, expectedPrefix) = expectationsInOrder[i];
324+
var record = records[i];
325+
326+
Assert.Equal(expectedMessage, record.Message);
327+
Assert.Equal(expectedLevel, record.Level);
328+
Assert.Null(record.Exception);
329+
Assert.Null(record.Category);
330+
Assert.True(record.LevelEnabled);
331+
Assert.Empty(record.Scopes);
332+
Assert.Equal(0, record.Id.Id);
333+
Assert.EndsWith($"{expectedPrefix}{expectedMessage}", record.ToString());
334+
335+
if (i == expectedRecordCount - 1)
336+
{
337+
Assert.Equivalent(record, logger.LatestRecord);
338+
}
339+
}
340+
}
286341
}

0 commit comments

Comments
 (0)