akka.net icon indicating copy to clipboard operation
akka.net copied to clipboard

Akka.Event: add log filtering system to prevent Akka.NET logs from being emitted in first place

Open Aaronontheweb opened this issue 1 year ago • 3 comments
trafficstars

Changes

Fixes #7097 and probably lots of other usability issues.

The goal behind this system is to prevent Akka.NET LogEvents from being emitted in the first place in the event that their data is not useful. This should allow developers to do things like akka.LOGLEVEL=DEBUG without getting absolutely flooded with noise from busy parts of the system like Akka.Remote, Akka.Streams, and so on.

The general idea is to pass in a new Setup class:

/// <summary>
/// Used to specify filters that can be used to curtail noise from sources in the Akka.NET log stream.
/// </summary>
public sealed class LogFilterSetup : Setup
{
    public LogFilterBase[] Filters { get; }
    
    public LogFilterEvaluator CreateEvaluator() => new(Filters);

    public LogFilterSetup(LogFilterBase[] filters)
    {
        Filters = filters;
    }
}

These LogFilterBase types are intended to be simple functions, but while taking into account some easy performance considerations:

public abstract class LogFilterBase : INoSerializationVerificationNeeded, IDeadLetterSuppression
{
    /// <summary>
    /// Which part of the log message this filter is evaluating?
    /// </summary>
    /// <remarks>
    /// This actually has a performance implication - if we're filtering on the source, which
    /// is already fully "expanded" into its final string representation, we can try to fail fast
    /// on that without any additional allocations.
    ///
    /// If we're filtering on the message, we have to fully expand the log message first which
    /// involves allocations. Users on really tight performance budgets should be aware of this.
    /// </remarks>
    public abstract LogFilterType FilterType { get; }

    /// <summary>
    /// Returns <c>true</c> if the message should be kept, <c>false</c> if it should be dropped.
    /// </summary>
    /// <param name="evt">The <see cref="LogEvent"/> being evaluated.</param>
    /// <remarks>
    /// If there are multiple filters, all of them must return <c>true</c> for the message to be kept.
    /// </remarks>
    public abstract bool ShouldKeepMessage(LogEvent evt);

    /// <summary>
    /// Fast path designed to avoid allocating strings if we're filtering on the message content.
    /// </summary>
    /// <param name="part">The part of the message to evaluate.</param>
    /// <param name="content">Usually the fully expanded message content.</param>
    public abstract LogFilterDecision ShouldKeepMessage(LogFilterType part, string content);
}

I'm even including a LogFilterBuilder that should make it easy to construct these using a simple builder pattern. For a log to be printed, all filters must evaluate to true. Will include some examples of this below.

Checklist

For significant changes, please ensure that the following have been completed (delete if not relevant):

Aaronontheweb avatar Apr 29 '24 16:04 Aaronontheweb

Going to leave this PR open for comments for a while before it gets merged in

Aaronontheweb avatar Apr 29 '24 20:04 Aaronontheweb

Also: documentation. Will add it once I get review comments.

Aaronontheweb avatar Apr 29 '24 20:04 Aaronontheweb

I still need to do API approvals here

Aaronontheweb avatar May 13 '24 16:05 Aaronontheweb