Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -636,3 +636,42 @@ To set up the `Microsoft.Extensions.Logging` log filtering, you will need to edi
```

[Back to top](#akkahosting)

<a id="filtering-logs"></a>
## Filtering Logs In Akka.NET

In Akka.NET 1.5.21, we introduced [log filtering for log messages based on the LogSource or the content of a log message](https://getakka.net/articles/utilities/logging.html#filtering-log-messages). Depending on your coding style, you can use this feature in Akka.Hosting in several ways.

1. Using The `LoggerConfigBuilder.WithLogFilter()` method.

The `LoggerConfigBuilder.WithLogFilter()` method lets you set up the `LogFilterBuilder`

```csharp
builder.Services.AddAkka("MyActorSystem", configurationBuilder =>
{
configurationBuilder
.ConfigureLoggers(loggerConfigBuilder =>
{
loggerConfigBuilder.WithLogFilter(filterBuilder =>
{
filterBuilder.ExcludeMessageContaining("Test");
});
});
});
```

2. Setting the `loggerConfigBuilder.LogFilterBuilder` property directly.

```csharp
builder.Services.AddAkka("MyActorSystem", configurationBuilder =>
{
configurationBuilder
.ConfigureLoggers(loggerConfigBuilder =>
{
loggerConfigBuilder.LogFilterBuilder = new LogFilterBuilder();
loggerConfigBuilder.LogFilterBuilder.ExcludeMessageContaining("Test");
});
});
```

[Back to top](#akkahosting)
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ namespace Akka.Hosting
public Akka.Hosting.DeadLetterOptions? DeadLetterOptions { get; set; }
public Akka.Hosting.DebugOptions? DebugOptions { get; set; }
public bool LogConfigOnStart { get; set; }
public Akka.Event.LogFilterBuilder? LogFilterBuilder { get; set; }
public Akka.Event.LogLevel LogLevel { get; set; }
[System.Obsolete("Use the WithDefaultLogMessageFormatter<T> method instead")]
public System.Type LogMessageFormatter { get; set; }
Expand All @@ -163,6 +164,7 @@ namespace Akka.Hosting
public Akka.Hosting.LoggerConfigBuilder ClearLoggers() { }
public Akka.Hosting.LoggerConfigBuilder WithDefaultLogMessageFormatter<T>()
where T : Akka.Event.ILogMessageFormatter { }
public Akka.Hosting.LoggerConfigBuilder WithLogFilter(System.Action<Akka.Event.LogFilterBuilder> filterBuilder) { }
}
public static class LoggingExtensions
{
Expand Down
18 changes: 18 additions & 0 deletions src/Akka.Hosting.Tests/HostingExtensionsSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
// -----------------------------------------------------------------------

using System;
using System.Linq;
using Akka.Event;
using FluentAssertions;
using FluentAssertions.Extensions;
using Microsoft.Extensions.DependencyInjection;
Expand All @@ -32,4 +34,20 @@ public void WithActorAskTimeoutInfiniteTest()
builder.Configuration.HasValue.Should().BeTrue();
builder.Configuration.Value.GetString("akka.actor.ask-timeout").Should().Be("infinite");
}

[Fact(DisplayName = "ConfigureLogger WithLogFilter should inject LogFilterSetup")]
public void ConfigureLoggerWithLogFilterSetupTest()
{
var builder = new AkkaConfigurationBuilder(new ServiceCollection(), "fake")
.ConfigureLoggers(logger =>
{
logger.WithLogFilter(filterBuilder =>
{
filterBuilder.ExcludeMessageContaining("Test");
});
});
var filterSetup = builder.Setups.OfType<LogFilterSetup>().First();
filterSetup.Filters.Length.Should().Be(1);
filterSetup.Filters.Any(f => f is RegexLogMessageFilter).Should().BeTrue();
}
}
41 changes: 40 additions & 1 deletion src/Akka.Hosting.Tests/Logging/LoggerConfigBuilderSpecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
// -----------------------------------------------------------------------

using System;
using System.Linq;
using Akka.Configuration;
using Akka.Hosting.Logging;
using Akka.Event;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
Expand Down Expand Up @@ -106,4 +107,42 @@ public void DeadLetterOptionsTest()
}.ToString();
cfg.GetInt("akka.log-dead-letters").Should().Be(10);
}

[Fact(DisplayName = "WithLogFilter should populate the LogFilterBuilder property")]
public void WithLogFilterPropertyTest()
{
var akkaBuilder = new AkkaConfigurationBuilder(new ServiceCollection(), "test");
var loggerConfigBuilder = new LoggerConfigBuilder(akkaBuilder)
.WithLogFilter(filterBuilder =>
{
filterBuilder.ExcludeMessageContaining("Test");
});
loggerConfigBuilder.LogFilterBuilder.Should().NotBeNull();
var filterSetup = loggerConfigBuilder.LogFilterBuilder!.Build();
filterSetup.Filters.Length.Should().Be(1);
filterSetup.Filters.Any(f => f is RegexLogMessageFilter).Should().BeTrue();
}

[Fact(DisplayName = "WithLogFilter should append existing LogFilterBuilder property")]
public void WithLogFilterConcatTest()
{
var akkaBuilder = new AkkaConfigurationBuilder(new ServiceCollection(), "test");
var loggerConfigBuilder = new LoggerConfigBuilder(akkaBuilder)
{
LogFilterBuilder = new LogFilterBuilder()
.ExcludeSourceContaining("Test")
};
loggerConfigBuilder
.WithLogFilter(filterBuilder =>
{
filterBuilder.ExcludeMessageContaining("Test");
});

loggerConfigBuilder.LogFilterBuilder.Should().NotBeNull();
var filterSetup = loggerConfigBuilder.LogFilterBuilder.Build();
filterSetup.Filters.Length.Should().Be(2);
filterSetup.Filters.Any(f => f is RegexLogMessageFilter).Should().BeTrue();
filterSetup.Filters.Any(f => f is RegexLogSourceFilter).Should().BeTrue();
}

}
20 changes: 19 additions & 1 deletion src/Akka.Hosting/LoggerConfigBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ internal LoggerConfigBuilder(AkkaConfigurationBuilder builder)
public DeadLetterOptions? DeadLetterOptions { get; set; }

public DebugOptions? DebugOptions { get; set; }

public LogFilterBuilder? LogFilterBuilder { get; set; }

[Obsolete("Use the WithDefaultLogMessageFormatter<T> method instead")]
public Type LogMessageFormatter
Expand Down Expand Up @@ -96,6 +98,13 @@ public LoggerConfigBuilder WithDefaultLogMessageFormatter<T>() where T: ILogMess
return this;
}

public LoggerConfigBuilder WithLogFilter(Action<LogFilterBuilder> filterBuilder)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I'm not so sure about - what happens if someone calls this and ConfigureLogging (or whatever it's called?) - can't we end up with one config overriding the other?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the two implementation inside ConfigureLoggers(builder) and the extension method AkkaConfigurationBuilder.WithLogFilter() is exclusive to each other. The first being added would win because of how we retrieve Setup classes in ActorSystem.Settings

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - we decided to stick with just the LogConfigBuilder.

{
LogFilterBuilder ??= new LogFilterBuilder();
filterBuilder(LogFilterBuilder);
return this;
}

/// <summary>
/// INTERNAL API
///
Expand All @@ -108,7 +117,7 @@ internal void AddLogger(Type logger)
_loggers.Add(logger);
}

internal Config ToConfig()
private Config ToConfig()
{
var sb = new StringBuilder()
.Append("akka.loglevel=").AppendLine(ParseLogLevel(LogLevel))
Expand All @@ -124,6 +133,15 @@ internal Config ToConfig()
return ConfigurationFactory.ParseString(sb.ToString());
}

internal AkkaConfigurationBuilder Build(AkkaConfigurationBuilder builder)
{
builder.AddHoconConfiguration(ToConfig(), HoconAddMode.Prepend);
if (LogFilterBuilder is not null)
builder.AddSetup(LogFilterBuilder.Build());

return builder;
}

private static string ParseLogLevel(LogLevel logLevel)
=> logLevel switch
{
Expand Down
2 changes: 1 addition & 1 deletion src/Akka.Hosting/Logging/LoggerFactoryLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class LoggerFactoryLogger: ActorBase, IRequiresMessageQueue<ILoggerMessag
/// </summary>
protected readonly ILoggingAdapter InternalLogger = Akka.Event.Logging.GetLogger(Context.System.EventStream, nameof(LoggerFactoryLogger));
private readonly ILoggerFactory _loggerFactory;
private ILogger<ActorSystem> _akkaLogger;
private readonly ILogger<ActorSystem> _akkaLogger;

public LoggerFactoryLogger()
{
Expand Down
2 changes: 1 addition & 1 deletion src/Akka.Hosting/LoggingExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static AkkaConfigurationBuilder ConfigureLoggers(this AkkaConfigurationBu
{
var setup = new LoggerConfigBuilder(builder);
configurator(setup);
return builder.AddHoconConfiguration(setup.ToConfig(), HoconAddMode.Prepend);
return setup.Build(builder);
}

/// <summary>
Expand Down