Skip to content

Commit

Permalink
Add filtering by attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
evgenyfedorov2 committed Jan 16, 2025
1 parent fe00658 commit 5fc421c
Show file tree
Hide file tree
Showing 12 changed files with 120 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,24 @@ public bool TryEnqueue<TState>(
Exception? exception,
Func<TState, Exception?, string> formatter)
{
if (!IsEnabled(category, logLevel, eventId))
{
return false;
}

SerializedLogRecord serializedLogRecord = default;
if (attributes is ModernTagJoiner modernTagJoiner)
{
if (!IsEnabled(category, logLevel, eventId, modernTagJoiner))
{
return false;
}

serializedLogRecord = new SerializedLogRecord(logLevel, eventId, _timeProvider.GetUtcNow(), modernTagJoiner, exception,
((Func<ModernTagJoiner, Exception?, string>)(object)formatter)(modernTagJoiner, exception));
}
else if (attributes is LegacyTagJoiner legacyTagJoiner)
{
if (!IsEnabled(category, logLevel, eventId, legacyTagJoiner))
{
return false;
}

serializedLogRecord = new SerializedLogRecord(logLevel, eventId, _timeProvider.GetUtcNow(), legacyTagJoiner, exception,
((Func<LegacyTagJoiner, Exception?, string>)(object)formatter)(legacyTagJoiner, exception));
}
Expand Down Expand Up @@ -101,7 +106,7 @@ public void Flush()
_bufferedLogger.LogRecords(deserializedLogRecords);
}

public bool IsEnabled(string category, LogLevel logLevel, EventId eventId)
public bool IsEnabled(string category, LogLevel logLevel, EventId eventId, IReadOnlyList<KeyValuePair<string, object?>> attributes)
{
if (_timeProvider.GetUtcNow() < _lastFlushTimestamp + _globalOptions.CurrentValue.SuspendAfterFlushDuration)
{
Expand All @@ -110,7 +115,7 @@ public bool IsEnabled(string category, LogLevel logLevel, EventId eventId)

LoggerFilterRuleSelector.Select(_options.CurrentValue.Rules, category, logLevel, eventId, out BufferFilterRule? rule);

return rule is not null;
return rule is not null && rule.Filter(category, logLevel, eventId, attributes);
}

private void Trim()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public static ILoggingBuilder AddHttpRequestBuffering(this ILoggingBuilder build
_ = Throw.IfNull(builder);

_ = builder.Services
.Configure<HttpRequestBufferOptions>(options => options.Rules.Add(new BufferFilterRule(null, level, null)))
.Configure<HttpRequestBufferOptions>(options => options.Rules.Add(new BufferFilterRule(null, level, null, null)))
.Configure(configure ?? new Action<HttpRequestBufferOptions>(_ => { }));

return builder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

<PropertyGroup>
<TargetFrameworks>$(TargetFrameworks);netstandard2.0</TargetFrameworks>
<ForceLatestDotnetVersions>true</ForceLatestDotnetVersions>
<NoWarn>$(NoWarn);CA1063;CA2227;SA1316;S1067;S1121;S3358</NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DisableNETStandardCompatErrors>true</DisableNETStandardCompatErrors>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Logging;
using Microsoft.Shared.DiagnosticIds;
Expand All @@ -17,7 +19,7 @@ public class BufferFilterRule : ILoggerFilterRule
/// Initializes a new instance of the <see cref="BufferFilterRule"/> class.
/// </summary>
public BufferFilterRule()
: this(null, null, null)
: this(null, null, null, null)
{
}

Expand All @@ -27,11 +29,14 @@ public BufferFilterRule()
/// <param name="categoryName">The category name to use in this filter rule.</param>
/// <param name="logLevel">The <see cref="LogLevel"/> to use in this filter rule.</param>
/// <param name="eventId">The <see cref="EventId"/> to use in this filter rule.</param>
public BufferFilterRule(string? categoryName, LogLevel? logLevel, int? eventId)
/// <param name="filter">The optional filter delegate to use if a log message passes other filters.</param>
public BufferFilterRule(string? categoryName, LogLevel? logLevel, int? eventId,
Func<string?, LogLevel?, EventId?, IReadOnlyList<KeyValuePair<string, object?>>, bool>? filter = null)
{
Category = categoryName;
LogLevel = logLevel;
EventId = eventId;
Filter = filter ?? ((_, _, _, _) => true);
}

/// <inheritdoc/>
Expand All @@ -42,4 +47,7 @@ public BufferFilterRule(string? categoryName, LogLevel? logLevel, int? eventId)

/// <inheritdoc/>
public int? EventId { get; set; }

/// <inheritdoc/>
public Func<string?, LogLevel?, EventId?, IReadOnlyList<KeyValuePair<string, object?>>, bool> Filter { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,24 @@ public bool TryEnqueue<T>(
Exception? exception,
Func<T, Exception?, string> formatter)
{
if (!IsEnabled(category, logLevel, eventId))
{
return false;
}

SerializedLogRecord serializedLogRecord = default;
if (attributes is ModernTagJoiner modernTagJoiner)
{
if (!IsEnabled(category, logLevel, eventId, modernTagJoiner))
{
return false;
}

serializedLogRecord = new SerializedLogRecord(logLevel, eventId, _timeProvider.GetUtcNow(), modernTagJoiner, exception,
((Func<ModernTagJoiner, Exception?, string>)(object)formatter)(modernTagJoiner, exception));
}
else if (attributes is LegacyTagJoiner legacyTagJoiner)
{
if (!IsEnabled(category, logLevel, eventId, legacyTagJoiner))
{
return false;
}

serializedLogRecord = new SerializedLogRecord(logLevel, eventId, _timeProvider.GetUtcNow(), legacyTagJoiner, exception,
((Func<LegacyTagJoiner, Exception?, string>)(object)formatter)(legacyTagJoiner, exception));
}
Expand Down Expand Up @@ -118,7 +123,7 @@ private void Trim()
}
}

private bool IsEnabled(string category, LogLevel logLevel, EventId eventId)
private bool IsEnabled(string category, LogLevel logLevel, EventId eventId, IReadOnlyList<KeyValuePair<string, object?>> attributes)
{
if (_timeProvider.GetUtcNow() < _lastFlushTimestamp + _options.CurrentValue.SuspendAfterFlushDuration)
{
Expand All @@ -127,6 +132,6 @@ private bool IsEnabled(string category, LogLevel logLevel, EventId eventId)

LoggerFilterRuleSelector.Select(_options.CurrentValue.Rules, category, logLevel, eventId, out BufferFilterRule? rule);

return rule is not null;
return rule is not null && rule.Filter(category, logLevel, eventId, attributes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public static ILoggingBuilder AddGlobalBuffer(this ILoggingBuilder builder, LogL
_ = Throw.IfNull(builder);

_ = builder.Services
.Configure<GlobalBufferOptions>(options => options.Rules.Add(new BufferFilterRule(null, level, null)))
.Configure<GlobalBufferOptions>(options => options.Rules.Add(new BufferFilterRule(null, level, null, null)))
.Configure(configure ?? new Action<GlobalBufferOptions>(_ => { }));

return builder.AddGlobalBufferManager();
Expand Down
13 changes: 10 additions & 3 deletions src/Libraries/Microsoft.Extensions.Telemetry/ILoggerFilterRule.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Shared.DiagnosticIds;

Expand All @@ -15,15 +17,20 @@ public interface ILoggerFilterRule
/// <summary>
/// Gets the logger category this rule applies to.
/// </summary>
public string? Category { get; }
string? Category { get; }

/// <summary>
/// Gets the maximum <see cref="LogLevel"/> of messages.
/// </summary>
public LogLevel? LogLevel { get; }
LogLevel? LogLevel { get; }

/// <summary>
/// Gets the <see cref="EventId"/> of messages where this rule applies to.
/// </summary>
public int? EventId { get; }
int? EventId { get; }

/// <summary>
/// Gets the filter delegate that would be additionally applied to messages that passed the <see cref="LogLevel"/>, <see cref="Category"/>, and <see cref="EventId"/> filters.
/// </summary>
Func<string?, LogLevel?, EventId?, IReadOnlyList<KeyValuePair<string, object?>>, bool> Filter { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ public static class LoggerFilterRuleSelector
/// <param name="logLevel">The log level of the log event.</param>
/// <param name="eventId">The event id of the log event.</param>
/// <param name="bestRule">The best rule that matches the log event.</param>
public static void Select<T>(IList<T> rules, string category, LogLevel logLevel, EventId eventId,
out T? bestRule)
public static void Select<T>(IList<T> rules, string category, LogLevel logLevel, EventId eventId, out T? bestRule)
where T : class, ILoggerFilterRule
{
bestRule = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ public void AddHttpRequestBufferConfiguration_RegistersInDI()
{
List<BufferFilterRule> expectedData =
[
new BufferFilterRule("Program.MyLogger", LogLevel.Information, 1),
new BufferFilterRule(null, LogLevel.Information, null),
new BufferFilterRule("Program.MyLogger", LogLevel.Information, 1, null),
new BufferFilterRule(null, LogLevel.Information, null, null),
];
ConfigurationBuilder configBuilder = new ConfigurationBuilder();
configBuilder.AddJsonFile("appsettings.json");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,6 @@
<LibraryProjects Remove="$(RepoRoot)\src\Libraries\Microsoft.Extensions.Compliance.Redaction\Microsoft.Extensions.Compliance.Redaction.csproj" />
<!-- https://github.com/dotnet/extensions/issues/5625 -->
<LibraryProjects Remove="$(RepoRoot)\src\Libraries\Microsoft.Extensions.Http.Resilience\Microsoft.Extensions.Http.Resilience.csproj" />

<!-- TO DO: create a GitHub issue -->
<LibraryProjects Remove="$(RepoRoot)\src\Libraries\Microsoft.Extensions.Diagnostics.Testing\Microsoft.Extensions.Diagnostics.Testing.csproj" />
<LibraryProjects Remove="$(RepoRoot)\src\Libraries\Microsoft.Extensions.Http.Diagnostics\Microsoft.Extensions.Http.Diagnosticss.csproj" />
<LibraryProjects Remove="$(RepoRoot)\src\Libraries\Microsoft.Extensions.Telemetry\Microsoft.Extensions.Telemetry.csproj" />
<LibraryProjects Remove="$(RepoRoot)\src\Libraries\Microsoft.AspNetCore.Diagnostics.Middleware\Microsoft.AspNetCore.Diagnostics.Middleware.csproj" />
<!---->

<TrimmerRootAssembly Include="@(LibraryProjects->'%(FileName)')" />
<ProjectReference Include="@(LibraryProjects)" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ public void AddGlobalBufferConfiguration_RegistersInDI()
{
List<BufferFilterRule> expectedData =
[
new BufferFilterRule("Program.MyLogger", LogLevel.Information, 1),
new BufferFilterRule(null, LogLevel.Information, null),
new BufferFilterRule("Program.MyLogger", LogLevel.Information, 1, null),
new BufferFilterRule(null, LogLevel.Information, null, null),
];
ConfigurationBuilder configBuilder = new ConfigurationBuilder();
configBuilder.AddJsonFile("appsettings.json");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Diagnostics.Buffering;
using Xunit;

namespace Microsoft.Extensions.Logging.Test;
public class LoggerFilterRuleSelectorTests
{
[Fact]
public void SelectsRightRule()
{
// Arrange
var rules = new List<ILoggerFilterRule>
{
new BufferFilterRule(null, null, null, null),
new BufferFilterRule(null, null, 1, null),
new BufferFilterRule(null, LogLevel.Information, 1, null),
new BufferFilterRule(null, LogLevel.Information, 1, null),
new BufferFilterRule(null, LogLevel.Warning, null, null),
new BufferFilterRule(null, LogLevel.Warning, 2, null),
new BufferFilterRule(null, LogLevel.Warning, 1, null),
new BufferFilterRule("Program1.MyLogger", LogLevel.Warning, 1, null),
new BufferFilterRule("Program.*MyLogger1", LogLevel.Warning, 1, null),
new BufferFilterRule("Program.MyLogger", LogLevel.Warning, 1), // the best rule
new BufferFilterRule("Program.MyLogger", LogLevel.Warning, 2, null),
new BufferFilterRule("Program.MyLogger", null, 1, null),
new BufferFilterRule(null, LogLevel.Warning, 1, null),
new BufferFilterRule("Program", LogLevel.Warning, 1, null),
new BufferFilterRule("Program.MyLogger", LogLevel.Warning, null, null),
new BufferFilterRule("Program.MyLogger", LogLevel.Error, 1, null),
};

// Act
LoggerFilterRuleSelector.Select(
rules, "Program.MyLogger", LogLevel.Warning, 1, out var actualResult);

// Assert
Assert.Same(rules[9], actualResult);
}

[Fact]
public void WhenManyRuleApply_SelectsLast()
{
// Arrange
var rules = new List<ILoggerFilterRule>
{
new BufferFilterRule(null, LogLevel.Information, 1, null),
new BufferFilterRule(null, LogLevel.Information, 1, null),
new BufferFilterRule(null, LogLevel.Warning, null, null),
new BufferFilterRule(null, LogLevel.Warning, 2, null),
new BufferFilterRule(null, LogLevel.Warning, 1, null),
new BufferFilterRule("Program1.MyLogger", LogLevel.Warning, 1, null),
new BufferFilterRule("Program.*MyLogger1", LogLevel.Warning, 1, null),
new BufferFilterRule("Program.MyLogger", LogLevel.Warning, 1, null),
new BufferFilterRule("Program.MyLogger*", LogLevel.Warning, 1, null),
new BufferFilterRule("Program.MyLogger", LogLevel.Warning, 1), // the best rule
new BufferFilterRule("Program.MyLogger*", LogLevel.Warning, 1), // same as the best, but last and should be selected
};

// Act
LoggerFilterRuleSelector.Select(rules, "Program.MyLogger", LogLevel.Warning, 1, out var actualResult);

// Assert
Assert.Same(rules.Last(), actualResult);
}
}

0 comments on commit 5fc421c

Please sign in to comment.