diff --git a/src/Akka.Hosting.API.Tests/verify/CoreApiSpec.ApproveCore.verified.txt b/src/Akka.Hosting.API.Tests/verify/CoreApiSpec.ApproveCore.verified.txt index e54f8bb0..32820961 100644 --- a/src/Akka.Hosting.API.Tests/verify/CoreApiSpec.ApproveCore.verified.txt +++ b/src/Akka.Hosting.API.Tests/verify/CoreApiSpec.ApproveCore.verified.txt @@ -194,6 +194,8 @@ namespace Akka.Hosting public Akka.Hosting.LoggerConfigBuilder AddLogger() where T : Akka.Dispatch.IRequiresMessageQueue { } public Akka.Hosting.LoggerConfigBuilder ClearLoggers() { } + [System.Obsolete("SemanticLogMessageFormatter is now the default. Only use this method if you have " + + "a custom ILogMessageFormatter implementation.")] public Akka.Hosting.LoggerConfigBuilder WithDefaultLogMessageFormatter() where T : Akka.Event.ILogMessageFormatter { } public Akka.Hosting.LoggerConfigBuilder WithLogFilter(System.Action filterBuilder) { } @@ -273,4 +275,4 @@ namespace Akka.Hosting.Logging public LoggerFactorySetup(Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { } public Microsoft.Extensions.Logging.ILoggerFactory LoggerFactory { get; } } -} +} \ No newline at end of file diff --git a/src/Akka.Hosting.Tests/Logging/LogMessageFormatterSpec.cs b/src/Akka.Hosting.Tests/Logging/LogMessageFormatterSpec.cs index dfd2adb7..2884b0d0 100644 --- a/src/Akka.Hosting.Tests/Logging/LogMessageFormatterSpec.cs +++ b/src/Akka.Hosting.Tests/Logging/LogMessageFormatterSpec.cs @@ -63,6 +63,44 @@ await Awaiting(async () => await SetupHost()) .Should().ThrowAsync().WithMessage("*must have an empty constructor*"); } + [Fact(DisplayName = "SemanticLogMessageFormatter should be accepted (GitHub issue #703)")] + public async Task SemanticLogMessageFormatterShouldBeAcceptedTest() + { + // SemanticLogMessageFormatter has a private constructor - verify it doesn't throw +#pragma warning disable CS0618 + using var host = await SetupHost(); +#pragma warning restore CS0618 + + try + { + var sys = host.Services.GetRequiredService(); + sys.Settings.LogFormatter.Should().BeOfType(); + } + finally + { + await host.StopAsync(); + } + } + + [Fact(DisplayName = "DefaultLogMessageFormatter should be accepted")] + public async Task DefaultLogMessageFormatterShouldBeAcceptedTest() + { + // DefaultLogMessageFormatter has a private constructor - verify it doesn't throw +#pragma warning disable CS0618 + using var host = await SetupHost(); +#pragma warning restore CS0618 + + try + { + var sys = host.Services.GetRequiredService(); + sys.Settings.LogFormatter.Should().BeOfType(); + } + finally + { + await host.StopAsync(); + } + } + private async Task SetupHost() where TFormatter : ILogMessageFormatter { var host = new HostBuilder() @@ -79,7 +117,9 @@ private async Task SetupHost() where TFormatter : ILogMessage { setup.LogLevel = Event.LogLevel.DebugLevel; setup.AddLoggerFactory(); +#pragma warning disable CS0618 setup.WithDefaultLogMessageFormatter(); +#pragma warning restore CS0618 }); }); }).Build(); diff --git a/src/Akka.Hosting.Tests/Logging/SerilogLoggerEnd2EndSpecs.cs b/src/Akka.Hosting.Tests/Logging/SerilogLoggerEnd2EndSpecs.cs index 34908125..d01843bb 100644 --- a/src/Akka.Hosting.Tests/Logging/SerilogLoggerEnd2EndSpecs.cs +++ b/src/Akka.Hosting.Tests/Logging/SerilogLoggerEnd2EndSpecs.cs @@ -69,7 +69,9 @@ protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IService setup.ClearLoggers(); setup.AddLogger(); setup.LogLevel = Event.LogLevel.DebugLevel; +#pragma warning disable CS0618 setup.WithDefaultLogMessageFormatter(); +#pragma warning restore CS0618 }); } diff --git a/src/Akka.Hosting/LoggerConfigBuilder.cs b/src/Akka.Hosting/LoggerConfigBuilder.cs index 8fe36585..fd7a4434 100644 --- a/src/Akka.Hosting/LoggerConfigBuilder.cs +++ b/src/Akka.Hosting/LoggerConfigBuilder.cs @@ -57,10 +57,15 @@ public Type LogMessageFormatter if (!typeof(ILogMessageFormatter).IsAssignableFrom(value)) throw new ConfigurationException($"{nameof(LogMessageFormatter)} must implement {nameof(ILogMessageFormatter)}"); - var ctor = value.GetConstructor([]); - if (ctor is null) - throw new ConfigurationException($"{nameof(LogMessageFormatter)} Type must have an empty constructor"); - + // Built-in formatters use private constructors with singleton Instance properties; + // Akka.NET's Settings.cs handles these as special cases at runtime. + if (value != typeof(SemanticLogMessageFormatter) && value != typeof(DefaultLogMessageFormatter)) + { + var ctor = value.GetConstructor([]); + if (ctor is null) + throw new ConfigurationException($"{nameof(LogMessageFormatter)} Type must have an empty constructor"); + } + _logMessageFormatter = value; } } @@ -87,8 +92,14 @@ public LoggerConfigBuilder AddLogger() where T: IRequiresMessageQueue - /// Sets the formatter used by the logger + /// Sets the formatter used by the logger. /// + /// + /// As of Akka.NET 1.5.58, is the default formatter + /// and is configured automatically. This method is only needed if you have a custom + /// implementation. + /// + [Obsolete("SemanticLogMessageFormatter is now the default. Only use this method if you have a custom ILogMessageFormatter implementation.")] public LoggerConfigBuilder WithDefaultLogMessageFormatter() where T: ILogMessageFormatter { #pragma warning disable CS0618 // Type or member is obsolete