From 7e2aacd19511d4ae87391562aed053c6f06e475f Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Thu, 4 Dec 2025 02:37:56 +0700 Subject: [PATCH 1/2] Update RELEASE_NOTES.md for 1.5.57-beta2 release --- RELEASE_NOTES.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 89a0e63..5631635 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,17 @@ +#### 1.5.57-beta2 December 3rd 2025 #### + +* [Update Akka.NET to 1.5.57-beta2](https://github.com/akkadotnet/akka.net/releases/tag/1.5.57-beta2) +* [Add semantic logging support for Akka.NET 1.5.56+](https://github.com/akkadotnet/Akka.Logger.Serilog/pull/294) + +This release adds full semantic logging support, enabling Serilog to receive properly structured message templates and parameters instead of pre-formatted strings. This enhancement leverages Akka.NET's semantic logging APIs introduced in version 1.5.56, enabling richer structured logging capabilities. + +**New Features:** +- **Semantic Logging**: Serilog now receives message templates with named and positional parameters for true structured logging +- **Enhanced Template Support**: Full support for Serilog destructuring (`@`), stringification (`$`), and format specifiers (e.g., `:N2`) +- **ForContext Integration**: Semantic logging works seamlessly with `ForContext()` for enriched log contexts +- **Akka Metadata Preservation**: All Akka.NET metadata (timestamp, log level, thread, logger name) is preserved in structured logs +- **Backwards Compatible**: Fully compatible with older Akka.NET versions through `LogMessage` type checking + #### 1.5.25 June 17 2024 #### * [Update Akka.Hosting to 1.5.25](https://github.com/akkadotnet/akka.net/releases/tag/1.5.25) From c135350f493c7d6c5f9dde19dfd784e6d334850d Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Fri, 5 Dec 2025 01:06:00 +0700 Subject: [PATCH 2/2] Harden SerilogFormattingSpecs --- .../SerilogFormattingSpecs.cs | 43 ++++++++++++++----- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/src/Akka.Logger.Serilog.Tests/SerilogFormattingSpecs.cs b/src/Akka.Logger.Serilog.Tests/SerilogFormattingSpecs.cs index 0f8ee05..9a8e2a9 100644 --- a/src/Akka.Logger.Serilog.Tests/SerilogFormattingSpecs.cs +++ b/src/Akka.Logger.Serilog.Tests/SerilogFormattingSpecs.cs @@ -11,6 +11,7 @@ using Serilog; using Xunit; using Xunit.Abstractions; +using LogEvent = Serilog.Events.LogEvent; using SerilogLog = Serilog.Log; namespace Akka.Logger.Serilog.Tests @@ -53,10 +54,11 @@ public async Task RawLogOutputRegressionTest(string version, string expected, st await AwaitConditionAsync(() => _sink.Writes.Count == 0); _serilogLogger.Information(messageFormat, args); - await AwaitConditionAsync(() => _sink.Writes.Count == 1); - _sink.Writes.TryDequeue(out var logEvent).Should().BeTrue(); - logEvent!.RenderMessage().Should().Be(expected); + await AwaitConditionAsync(() => AssertCondition(logEvent => + { + logEvent.RenderMessage().Should().Be(expected); + })); } [Theory(DisplayName = "SerilogLoggingAdapter output must be compatible with previous version")] @@ -67,10 +69,11 @@ public async Task AdapterLogOutputRegressionTest(string version, string expected await AwaitConditionAsync(() => _sink.Writes.Count == 0); _loggingAdapter.Info(messageFormat, args); - await AwaitConditionAsync(() => _sink.Writes.Count == 1); - _sink.Writes.TryDequeue(out var logEvent).Should().BeTrue(); - logEvent!.RenderMessage().Should().Contain(expected); + await AwaitConditionAsync(() => AssertCondition(logEvent => + { + logEvent.RenderMessage().Should().Contain(expected); + })); } [Theory(DisplayName = "Default ILoggingAdapter output must be compatible with previous version")] @@ -81,10 +84,28 @@ public async Task LogOutputRegressionTest(string version, string expected, strin await AwaitConditionAsync(() => _sink.Writes.Count == 0); Sys.Log.Info(messageFormat, args); - await AwaitConditionAsync(() => _sink.Writes.Count == 1); - _sink.Writes.TryDequeue(out var logEvent).Should().BeTrue(); - logEvent!.RenderMessage().Should().Contain(expected); + await AwaitConditionAsync(() => AssertCondition(logEvent => + { + logEvent.RenderMessage().Should().Contain(expected); + })); + } + + private bool AssertCondition(Action assertion) + { + while (_sink.Writes.TryDequeue(out var logEvent)) + { + try + { + assertion(logEvent); + return true; + } + catch + { + // no-op + } + } + return false; } [Theory] @@ -93,13 +114,13 @@ public async Task LogOutputRegressionTest(string version, string expected, strin [InlineData(LogLevel.DebugLevel, "test case {myNum} {myStr}", new object[] { 1, "foo" })] public void ShouldHandleSerilogFormats(LogLevel level, string formatStr, object[] args) { - Sys.EventStream.Subscribe(TestActor, typeof(LogEvent)); + Sys.EventStream.Subscribe(TestActor, typeof(Akka.Event.LogEvent)); Action logWrite = () => { _loggingAdapter.Log(level, formatStr, args); - var logEvent = ExpectMsg(); + var logEvent = ExpectMsg(); logEvent.LogLevel().Should().Be(level); logEvent.ToString().Should().NotBeEmpty(); };