Skip to content

Commit

Permalink
Akka.Event: add log filtering system to prevent Akka.NET logs from be…
Browse files Browse the repository at this point in the history
…ing emitted in first place (#7179)

* create `LogFilter` infrastructure

* Create evaluator from setup

close #7097

* added sanity checks for `Regex`-based rules

* integrated `LogFilterSetup` into `Settings` and `StandardOutLogger`

* simplifying

* added accurate end2end unit test

* added API approvals

* removed unnecessary API from `LogFilterBase`

* small perf optimization for default cases

* fixed bug with blended filter types

* added API approvals

* fixed race condition with tests

* updated APIs to be more expansive

* added docs

* fixed reference to samples

* fixed markdown linting rule

---------

Co-authored-by: Gregorius Soedharmo <arkatufus@yahoo.com>
  • Loading branch information
Aaronontheweb and Arkatufus committed May 22, 2024
1 parent 3f0be58 commit d1ed226
Show file tree
Hide file tree
Showing 10 changed files with 767 additions and 8 deletions.
39 changes: 39 additions & 0 deletions docs/articles/utilities/logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,42 @@ In your log, expect to see a line such as:
`[DEBUG]... received handled message hello from akka://test/deadLetters`

This logging can be toggled by configuring `akka.actor.debug.receive`.

## Filtering Log Messages

Since v1.5.21, Akka.NET supports for filtering log messages based on the `LogSource` or the content of a log message.

The goal of this feature is to allow users to run Akka.NET at more verbose logging settings (i.e. `LogLevel.Debug`) while not getting completely flooded with unhelpful noise from the Akka.NET logging system. You can use the [`LogFilterBuilder`](xref:Akka.Event.LogFilterBuilder) to exclude messages don't need while still keeping ones that you do.

### Configuring Log Filtering

[!code-csharp[Create LoggerSetup](../../../src/core/Akka.Tests/Loggers/LogFilterEvaluatorSpecs.cs?name=CreateLoggerSetup)]

We create a [`LogFilterBuilder`](xref:Akka.Event.LogFilterBuilder) prior to starting the `ActorSystem` and provide it with rules for which logs _should be excluded_ from any of Akka.NET's logged output - this uses the [`ActorSystemSetup`](xref:Akka.Actor.Setup.ActorSystemSetup) class functionality that Akka.NET supports for programmatic `ActorSystem` configuration:

[!code-csharp[Create ActorSystemSetup](../../../src/core/Akka.Tests/Loggers/LogFilterEvaluatorSpecs.cs?name=ActorSystemSetup)]

From there, we can create our `ActorSystem` with these rules enabled:

```csharp
ActorSystemSetup completeSetup = CustomLoggerSetup();

// start the ActorSystem with the LogFilterBuilder rules enabled
ActorSystem mySystem = ActorSystem.Create("MySys", completeSetup);
```

### Log Filtering Rules

There are two built-in types of log filtering rules:

* `ExcludeSource___` - filters logs based on the `LogSource`; this type of filtering is _very_ resource efficient because it doesn't require the log message to be expanded in order for filtering to work.
* `ExcludeMessage___` - filters logs based on the content of the message. More resource-intensive as it does require log messages to be fully expanded prior to filtering.

> [!NOTE]
> For an Akka.NET log to be excluded from the output logs, only one filter rule has to return a `LogFilterDecision.Drop`.
However, if that's not sufficient for your purposes we also support defining custom rules via the `LogFilterBase` class:

[!code-csharp[LogFilterBase](../../../src/core/Akka/Event/LogFilter.cs?name=LogFilterBase)]

You can filter log messages based on any of the accessibly properties, and for performance reasons any `LogFilterBase` that looks at `LogFilterType.Content` will be passed in the fully expanded log message as a `string?` via the optional `expandedMessage` property. This is done in order to avoid allocating the log message every time for each possible rule that might be evaluated.
Original file line number Diff line number Diff line change
Expand Up @@ -1725,6 +1725,7 @@ namespace Akka.Actor
public int LogDeadLetters { get; }
public bool LogDeadLettersDuringShutdown { get; }
public System.TimeSpan LogDeadLettersSuspendDuration { get; }
public Akka.Event.LogFilterEvaluator LogFilter { get; }
public Akka.Event.ILogMessageFormatter LogFormatter { get; }
public string LogLevel { get; }
public bool LogSerializerOverrideOnStart { get; }
Expand Down Expand Up @@ -3373,6 +3374,13 @@ namespace Akka.Event
public static bool Subscribe<TChannel>(this Akka.Event.EventStream eventStream, Akka.Actor.IActorRef subscriber) { }
public static bool Unsubscribe<TChannel>(this Akka.Event.EventStream eventStream, Akka.Actor.IActorRef subscriber) { }
}
[System.Runtime.CompilerServices.NullableAttribute(0)]
public sealed class ExactMatchLogSourceFilter : Akka.Event.LogFilterBase
{
public ExactMatchLogSourceFilter(string source, System.StringComparison comparison = 5) { }
public override Akka.Event.LogFilterType FilterType { get; }
public override Akka.Event.LogFilterDecision ShouldKeepMessage(Akka.Event.LogEvent content, [System.Runtime.CompilerServices.NullableAttribute(2)] string expandedMessage = null) { }
}
public interface IDeadLetterSuppression { }
public interface ILogMessageFormatter
{
Expand Down Expand Up @@ -3414,6 +3422,53 @@ namespace Akka.Event
public abstract Akka.Event.LogLevel LogLevel();
public override string ToString() { }
}
public abstract class LogFilterBase : Akka.Actor.INoSerializationVerificationNeeded, Akka.Event.IDeadLetterSuppression
{
protected LogFilterBase() { }
public abstract Akka.Event.LogFilterType FilterType { get; }
public abstract Akka.Event.LogFilterDecision ShouldKeepMessage(Akka.Event.LogEvent content, [System.Runtime.CompilerServices.NullableAttribute(2)] string expandedMessage = null);
}
[System.Runtime.CompilerServices.NullableAttribute(0)]
public sealed class LogFilterBuilder
{
public LogFilterBuilder() { }
public Akka.Event.LogFilterBuilder Add(Akka.Event.LogFilterBase filter) { }
public Akka.Event.LogFilterBuilder AddRange(System.Collections.Generic.IEnumerable<Akka.Event.LogFilterBase> filters) { }
public Akka.Event.LogFilterSetup Build() { }
public Akka.Event.LogFilterBuilder ExcludeMessageContaining(string messagePart) { }
public Akka.Event.LogFilterBuilder ExcludeMessageRegex(System.Text.RegularExpressions.Regex regex) { }
public Akka.Event.LogFilterBuilder ExcludeSourceContaining(string sourcePart) { }
public Akka.Event.LogFilterBuilder ExcludeSourceEndingWith(string sourceEnd) { }
public Akka.Event.LogFilterBuilder ExcludeSourceExactly(string source, System.StringComparison comparison = 5) { }
public Akka.Event.LogFilterBuilder ExcludeSourceRegex(System.Text.RegularExpressions.Regex regex) { }
public Akka.Event.LogFilterBuilder ExcludeSourceStartingWith(string sourceStart) { }
}
public enum LogFilterDecision
{
Keep = 0,
Drop = 1,
NoDecision = 2,
}
[System.Runtime.CompilerServices.NullableAttribute(0)]
public class LogFilterEvaluator
{
public static readonly Akka.Event.LogFilterEvaluator NoFilters;
public LogFilterEvaluator(Akka.Event.LogFilterBase[] filters) { }
public bool EvaluatesLogSourcesOnly { get; }
public virtual bool ShouldTryKeepMessage(Akka.Event.LogEvent evt, out string expandedLogMessage) { }
}
[System.Runtime.CompilerServices.NullableAttribute(0)]
public sealed class LogFilterSetup : Akka.Actor.Setup.Setup
{
public LogFilterSetup(Akka.Event.LogFilterBase[] filters) { }
public Akka.Event.LogFilterBase[] Filters { get; }
public Akka.Event.LogFilterEvaluator CreateEvaluator() { }
}
public enum LogFilterType
{
Source = 0,
Content = 1,
}
public enum LogLevel
{
DebugLevel = 0,
Expand Down Expand Up @@ -3571,6 +3626,7 @@ namespace Akka.Event
public abstract class MinimalLogger : Akka.Actor.MinimalActorRef
{
protected MinimalLogger() { }
public Akka.Event.LogFilterEvaluator Filter { get; }
public virtual Akka.Actor.ActorPath Path { get; }
public virtual Akka.Actor.IActorRefProvider Provider { get; }
protected abstract void Log(object message);
Expand All @@ -3588,6 +3644,20 @@ namespace Akka.Event
public void Log(Akka.Event.LogLevel logLevel, System.Exception cause, string format) { }
public void Log(Akka.Event.LogLevel logLevel, System.Exception cause, Akka.Event.LogMessage message) { }
}
[System.Runtime.CompilerServices.NullableAttribute(0)]
public sealed class RegexLogMessageFilter : Akka.Event.LogFilterBase
{
public RegexLogMessageFilter(System.Text.RegularExpressions.Regex messageRegex) { }
public override Akka.Event.LogFilterType FilterType { get; }
public override Akka.Event.LogFilterDecision ShouldKeepMessage(Akka.Event.LogEvent content, [System.Runtime.CompilerServices.NullableAttribute(2)] string expandedMessage = null) { }
}
[System.Runtime.CompilerServices.NullableAttribute(0)]
public sealed class RegexLogSourceFilter : Akka.Event.LogFilterBase
{
public RegexLogSourceFilter(System.Text.RegularExpressions.Regex sourceRegex) { }
public override Akka.Event.LogFilterType FilterType { get; }
public override Akka.Event.LogFilterDecision ShouldKeepMessage(Akka.Event.LogEvent content, [System.Runtime.CompilerServices.NullableAttribute(2)] string expandedMessage = null) { }
}
public class StandardOutLogger : Akka.Event.MinimalLogger
{
public StandardOutLogger() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1723,6 +1723,7 @@ namespace Akka.Actor
public int LogDeadLetters { get; }
public bool LogDeadLettersDuringShutdown { get; }
public System.TimeSpan LogDeadLettersSuspendDuration { get; }
public Akka.Event.LogFilterEvaluator LogFilter { get; }
public Akka.Event.ILogMessageFormatter LogFormatter { get; }
public string LogLevel { get; }
public bool LogSerializerOverrideOnStart { get; }
Expand Down Expand Up @@ -3365,6 +3366,13 @@ namespace Akka.Event
public static bool Subscribe<TChannel>(this Akka.Event.EventStream eventStream, Akka.Actor.IActorRef subscriber) { }
public static bool Unsubscribe<TChannel>(this Akka.Event.EventStream eventStream, Akka.Actor.IActorRef subscriber) { }
}
[System.Runtime.CompilerServices.NullableAttribute(0)]
public sealed class ExactMatchLogSourceFilter : Akka.Event.LogFilterBase
{
public ExactMatchLogSourceFilter(string source, System.StringComparison comparison = 5) { }
public override Akka.Event.LogFilterType FilterType { get; }
public override Akka.Event.LogFilterDecision ShouldKeepMessage(Akka.Event.LogEvent content, [System.Runtime.CompilerServices.NullableAttribute(2)] string expandedMessage = null) { }
}
public interface IDeadLetterSuppression { }
public interface ILogMessageFormatter
{
Expand Down Expand Up @@ -3406,6 +3414,53 @@ namespace Akka.Event
public abstract Akka.Event.LogLevel LogLevel();
public override string ToString() { }
}
public abstract class LogFilterBase : Akka.Actor.INoSerializationVerificationNeeded, Akka.Event.IDeadLetterSuppression
{
protected LogFilterBase() { }
public abstract Akka.Event.LogFilterType FilterType { get; }
public abstract Akka.Event.LogFilterDecision ShouldKeepMessage(Akka.Event.LogEvent content, [System.Runtime.CompilerServices.NullableAttribute(2)] string expandedMessage = null);
}
[System.Runtime.CompilerServices.NullableAttribute(0)]
public sealed class LogFilterBuilder
{
public LogFilterBuilder() { }
public Akka.Event.LogFilterBuilder Add(Akka.Event.LogFilterBase filter) { }
public Akka.Event.LogFilterBuilder AddRange(System.Collections.Generic.IEnumerable<Akka.Event.LogFilterBase> filters) { }
public Akka.Event.LogFilterSetup Build() { }
public Akka.Event.LogFilterBuilder ExcludeMessageContaining(string messagePart) { }
public Akka.Event.LogFilterBuilder ExcludeMessageRegex(System.Text.RegularExpressions.Regex regex) { }
public Akka.Event.LogFilterBuilder ExcludeSourceContaining(string sourcePart) { }
public Akka.Event.LogFilterBuilder ExcludeSourceEndingWith(string sourceEnd) { }
public Akka.Event.LogFilterBuilder ExcludeSourceExactly(string source, System.StringComparison comparison = 5) { }
public Akka.Event.LogFilterBuilder ExcludeSourceRegex(System.Text.RegularExpressions.Regex regex) { }
public Akka.Event.LogFilterBuilder ExcludeSourceStartingWith(string sourceStart) { }
}
public enum LogFilterDecision
{
Keep = 0,
Drop = 1,
NoDecision = 2,
}
[System.Runtime.CompilerServices.NullableAttribute(0)]
public class LogFilterEvaluator
{
public static readonly Akka.Event.LogFilterEvaluator NoFilters;
public LogFilterEvaluator(Akka.Event.LogFilterBase[] filters) { }
public bool EvaluatesLogSourcesOnly { get; }
public virtual bool ShouldTryKeepMessage(Akka.Event.LogEvent evt, out string expandedLogMessage) { }
}
[System.Runtime.CompilerServices.NullableAttribute(0)]
public sealed class LogFilterSetup : Akka.Actor.Setup.Setup
{
public LogFilterSetup(Akka.Event.LogFilterBase[] filters) { }
public Akka.Event.LogFilterBase[] Filters { get; }
public Akka.Event.LogFilterEvaluator CreateEvaluator() { }
}
public enum LogFilterType
{
Source = 0,
Content = 1,
}
public enum LogLevel
{
DebugLevel = 0,
Expand Down Expand Up @@ -3561,6 +3616,7 @@ namespace Akka.Event
public abstract class MinimalLogger : Akka.Actor.MinimalActorRef
{
protected MinimalLogger() { }
public Akka.Event.LogFilterEvaluator Filter { get; }
public virtual Akka.Actor.ActorPath Path { get; }
public virtual Akka.Actor.IActorRefProvider Provider { get; }
protected abstract void Log(object message);
Expand All @@ -3578,6 +3634,20 @@ namespace Akka.Event
public void Log(Akka.Event.LogLevel logLevel, System.Exception cause, string format) { }
public void Log(Akka.Event.LogLevel logLevel, System.Exception cause, Akka.Event.LogMessage message) { }
}
[System.Runtime.CompilerServices.NullableAttribute(0)]
public sealed class RegexLogMessageFilter : Akka.Event.LogFilterBase
{
public RegexLogMessageFilter(System.Text.RegularExpressions.Regex messageRegex) { }
public override Akka.Event.LogFilterType FilterType { get; }
public override Akka.Event.LogFilterDecision ShouldKeepMessage(Akka.Event.LogEvent content, [System.Runtime.CompilerServices.NullableAttribute(2)] string expandedMessage = null) { }
}
[System.Runtime.CompilerServices.NullableAttribute(0)]
public sealed class RegexLogSourceFilter : Akka.Event.LogFilterBase
{
public RegexLogSourceFilter(System.Text.RegularExpressions.Regex sourceRegex) { }
public override Akka.Event.LogFilterType FilterType { get; }
public override Akka.Event.LogFilterDecision ShouldKeepMessage(Akka.Event.LogEvent content, [System.Runtime.CompilerServices.NullableAttribute(2)] string expandedMessage = null) { }
}
public class StandardOutLogger : Akka.Event.MinimalLogger
{
public StandardOutLogger() { }
Expand Down

0 comments on commit d1ed226

Please sign in to comment.