Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion .github/workflows/pr_validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:

- name: "dotnet test"
shell: bash
run: dotnet test -c Release
run: dotnet test -c Release

- name: "dotnet pack"
run: dotnet pack -c Release -o ./bin/nuget
14 changes: 14 additions & 0 deletions Akka.Hosting.sln
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Remote.Hosting.Tests",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Hosting.LoggingDemo", "src\Examples\Akka.Hosting.LoggingDemo\Akka.Hosting.LoggingDemo.csproj", "{298D7727-FDC6-49B2-9030-CC7F0E09B0B8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Hosting.OpenTelemetry.Demo", "src\Examples\Akka.Hosting.OpenTelemetry.Demo\Akka.Hosting.OpenTelemetry.Demo.csproj", "{6BCB9636-DAD2-4364-8A83-3D2888FE5AB2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Hosting.OpenTelemetry.AppHost", "src\Examples\Akka.Hosting.OpenTelemetry.AppHost\Akka.Hosting.OpenTelemetry.AppHost.csproj", "{CC22F505-B648-420E-BC8A-0FCE46082986}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -110,6 +114,14 @@ Global
{298D7727-FDC6-49B2-9030-CC7F0E09B0B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{298D7727-FDC6-49B2-9030-CC7F0E09B0B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{298D7727-FDC6-49B2-9030-CC7F0E09B0B8}.Release|Any CPU.Build.0 = Release|Any CPU
{6BCB9636-DAD2-4364-8A83-3D2888FE5AB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6BCB9636-DAD2-4364-8A83-3D2888FE5AB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6BCB9636-DAD2-4364-8A83-3D2888FE5AB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6BCB9636-DAD2-4364-8A83-3D2888FE5AB2}.Release|Any CPU.Build.0 = Release|Any CPU
{CC22F505-B648-420E-BC8A-0FCE46082986}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CC22F505-B648-420E-BC8A-0FCE46082986}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CC22F505-B648-420E-BC8A-0FCE46082986}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CC22F505-B648-420E-BC8A-0FCE46082986}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -118,6 +130,8 @@ Global
{5F6A7BE8-6906-46CE-BA1C-72EA11EFA33B} = {EFA970FF-6BCC-4C38-84D8-324D40F2BF03}
{4F79325B-9EE7-4501-800F-7A1F8DFBCC80} = {EFA970FF-6BCC-4C38-84D8-324D40F2BF03}
{298D7727-FDC6-49B2-9030-CC7F0E09B0B8} = {EFA970FF-6BCC-4C38-84D8-324D40F2BF03}
{6BCB9636-DAD2-4364-8A83-3D2888FE5AB2} = {EFA970FF-6BCC-4C38-84D8-324D40F2BF03}
{CC22F505-B648-420E-BC8A-0FCE46082986} = {EFA970FF-6BCC-4C38-84D8-324D40F2BF03}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B99E6BB8-642A-4A68-86DF-69567CBA700A}
Expand Down
14 changes: 9 additions & 5 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
<Copyright>Copyright © 2013-$([System.DateTime]::Now.Year) Akka.NET Team</Copyright>
<Authors>Akka.NET Team</Authors>
<VersionPrefix>1.5.59</VersionPrefix>
<PackageReleaseNotes>**Bug Fixes**
<PackageReleaseNotes>**New Features**
* [Add OpenTelemetry trace correlation support for LoggerFactoryLogger](https://github.com/akkadotnet/Akka.Hosting/issues/700) - enables proper trace correlation for logs emitted from actor code. When using Akka.NET 1.5.59+, `LogEvent.ActivityContext` captures trace context at log creation time and flows it through to OpenTelemetry `LogRecord`s via the new `AkkaTraceContextProcessor`.

**Bug Fixes**
* [Fix semantic logging not capturing named placeholders as structured properties](https://github.com/akkadotnet/Akka.Hosting/pull/702) - resolved [issue #701](https://github.com/akkadotnet/Akka.Hosting/issues/701) where named placeholders like `{Event}` in log messages were not captured as searchable structured properties. Made all `LoggerConfigBuilder` properties optional and refactored message formatting code.
* [Fix TestKit startup timeout race condition](https://github.com/akkadotnet/Akka.Hosting/pull/705) - resolved race condition in `TestKit.InitializeAsync()` where `CancellationTokenSource.Register()` threw exceptions on the timer thread, causing unhandled exceptions that crashed the test host process. Also increased default startup timeout from 10s to 30s for CI environments.

**Updates**
* [Bump Akka version from 1.5.58 to 1.5.59](https://github.com/akkadotnet/akka.net/releases/tag/1.5.59)</PackageReleaseNotes>
* [Bump Akka version from 1.5.58 to 1.5.59](https://github.com/akkadotnet/akka.net/releases/tag/1.5.59)
* Added OpenTelemetry dependency (1.9.0+) for trace correlation support</PackageReleaseNotes>
<PackageIcon>akkalogo.png</PackageIcon>
<PackageProjectUrl>
https://github.com/akkadotnet/Akka.Hosting
Expand All @@ -31,8 +35,8 @@
<CoverletVersion>6.0.3</CoverletVersion>
<XunitRunneVisualstudio>3.1.5</XunitRunneVisualstudio>
<AkkaVersion>1.5.59</AkkaVersion>
<MicrosoftExtensionsVersion>[6.0.0,)</MicrosoftExtensionsVersion>
<SystemTextJsonVersion>[6.0.10,)</SystemTextJsonVersion>
<MicrosoftExtensionsVersion>[8.0.0,)</MicrosoftExtensionsVersion>
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The OTEL packages require these

<SystemTextJsonVersion>[8.0.5,)</SystemTextJsonVersion>
</PropertyGroup>
<!-- SourceLink support for all Akka.NET projects -->
<ItemGroup>
Expand All @@ -50,4 +54,4 @@
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
</Project>
</Project>
41 changes: 41 additions & 0 deletions README.md
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added documentation

Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ See the ["Introduction to Akka.Hosting - HOCON-less, "Pit of Success" Akka.NET R
* [Microsoft.Extensions.Logging.ILoggerFactory Logging Support](#microsoftextensionsloggingiloggerfactory-logging-support)
* [Serilog Support](#serilog-support)
* [Microsoft.Extensions.Logging Log Event Filtering](#microsoftextensionslogging-log-event-filtering)
- [OpenTelemetry Trace Correlation](#opentelemetry-trace-correlation)
- [Microsoft.Extensions.Diagnostics.HealthChecks Integration](#healthchecks)
* [Dependency Injected Health Checks](#healthcheck-di)
* [Built-in HealthChecks](#healthcheck-builtin)
Expand Down Expand Up @@ -629,6 +630,46 @@ To set up the `Microsoft.Extensions.Logging` log filtering, you will need to edi

[Back to top](#akkahosting)

<a id="opentelemetry-trace-correlation"></a>
# OpenTelemetry Trace Correlation

Akka.NET processes log events asynchronously, which means `Activity.Current` does not flow across actor mailbox boundaries. To preserve trace correlation, Akka.Hosting captures the `ActivityContext` at log creation time and includes it in the log state. The `AkkaTraceContextProcessor` then applies that context to OpenTelemetry `LogRecord`s so exporters can correlate logs with traces.

Minimal setup:

```csharp
using Akka.Hosting;
using Akka.Hosting.Logging;
using OpenTelemetry.Logs;
using OpenTelemetry.Resources;

builder.Logging.AddOpenTelemetry(options =>
{
options.SetResourceBuilder(ResourceBuilder.CreateDefault()
.AddService("my-service"));

// Register before exporters
options.AddAkkaTraceCorrelation();

// Add OTLP exporter if you have not configured it elsewhere.
// Your mileage may vary; use the OpenTelemetry configuration that fits your app.
options.AddOtlpExporter();
});

builder.Services.AddAkka("MySystem", configBuilder =>
{
configBuilder.ConfigureLoggers(setup =>
{
setup.ClearLoggers();
setup.AddLoggerFactory();
});
});
```

See the demo in `src/Examples/Akka.Hosting.OpenTelemetry.AppHost` and `src/Examples/Akka.Hosting.OpenTelemetry.Demo` for a working Aspire setup.

[Back to top](#akkahosting)

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

Expand Down
8 changes: 6 additions & 2 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
#### 1.5.59 January 26th 2026 ####
#### 1.5.59 January 2026 ####

**New Features**
* [Add OpenTelemetry trace correlation support for LoggerFactoryLogger](https://github.com/akkadotnet/Akka.Hosting/issues/700) - enables proper trace correlation for logs emitted from actor code. Solves the problem that `Activity.Current` doesn't flow across actor mailbox boundaries because it uses `AsyncLocal<T>`. When using Akka.NET 1.5.59+, `LogEvent.ActivityContext` captures trace context at log creation time and flows it through to OpenTelemetry `LogRecord`s via the new `AkkaTraceContextProcessor`. Register with `options.AddAkkaTraceCorrelation()` in your OpenTelemetry logging configuration.

**Bug Fixes**
* [Fix semantic logging not capturing named placeholders as structured properties](https://github.com/akkadotnet/Akka.Hosting/pull/702) - resolved [issue #701](https://github.com/akkadotnet/Akka.Hosting/issues/701) where named placeholders like `{Event}` in log messages were not captured as searchable structured properties. Made all `LoggerConfigBuilder` properties optional and refactored message formatting code.
* [Fix TestKit startup timeout race condition](https://github.com/akkadotnet/Akka.Hosting/pull/705) - resolved race condition in `TestKit.InitializeAsync()` where `CancellationTokenSource.Register()` threw exceptions on the timer thread, causing unhandled exceptions that crashed the test host process. Also increased default startup timeout from 10s to 30s for CI environments.

**Updates**
* [Bump Akka version from 1.5.58 to 1.5.59](https://github.com/akkadotnet/akka.net/releases/tag/1.5.59)
* Added `OpenTelemetry` package dependency (1.9.0+) for trace correlation support

#### 1.5.58 January 9th 2026 ####

Expand Down Expand Up @@ -79,4 +83,4 @@
* [Added dependency-injected health checks](https://github.com/akkadotnet/Akka.Hosting/pull/659) - `WithHealthCheck<T>()` generic methods for DI-resolved health checks

**Updates**
* [Bump Akka version from 1.5.50 to 1.5.51](https://github.com/akkadotnet/akka.net/releases/tag/1.5.51)
* [Bump Akka version from 1.5.50 to 1.5.51](https://github.com/akkadotnet/akka.net/releases/tag/1.5.51)
2 changes: 1 addition & 1 deletion src/Akka.Cluster.Hosting.Tests/ClusterOptionsSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public void ClusterOptionsConfigurationTest()
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
var jsonConfig = new ConfigurationBuilder().AddJsonStream(stream).Build();

var clusterOptions = jsonConfig.GetSection("Akka:ClusterOptions").Get<ClusterOptions>();
var clusterOptions = jsonConfig.GetSection("Akka:ClusterOptions").Get<ClusterOptions>()!;
clusterOptions.SplitBrainResolver = jsonConfig.GetSection("Akka:KeepMajorityOption").Get<KeepMajorityOption>();

var builder = new AkkaConfigurationBuilder(new ServiceCollection(), "")
Expand Down
2 changes: 1 addition & 1 deletion src/Akka.Cluster.Hosting.Tests/XUnitLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public bool IsEnabled(LogLevel logLevel)
};
}

public IDisposable BeginScope<TState>(TState state)
public IDisposable? BeginScope<TState>(TState state) where TState : notnull
{
throw new NotImplementedException();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ namespace Akka.Hosting
public static Akka.Hosting.AkkaConfigurationBuilder WithHealthCheck(this Akka.Hosting.AkkaConfigurationBuilder builder, string name, Akka.Hosting.IAkkaHealthCheck healthCheck, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default, System.Collections.Generic.IEnumerable<string>? tags = null, System.TimeSpan? timeout = default) { }
public static Akka.Hosting.AkkaConfigurationBuilder WithHealthCheck(this Akka.Hosting.AkkaConfigurationBuilder builder, string name, System.Func<Akka.Actor.ActorSystem, Akka.Hosting.ActorRegistry, System.Threading.CancellationToken, System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>> healthCheck, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default, System.Collections.Generic.IEnumerable<string>? tags = null, System.TimeSpan? timeout = default) { }
}
public static class AkkaOpenTelemetryExtensions
{
public static OpenTelemetry.Logs.OpenTelemetryLoggerOptions AddAkkaTraceCorrelation(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions options) { }
}
public class DeadLetterOptions
{
public DeadLetterOptions() { }
Expand Down Expand Up @@ -251,6 +255,11 @@ namespace Akka.Hosting.HealthChecks
}
namespace Akka.Hosting.Logging
{
public sealed class AkkaTraceContextProcessor : OpenTelemetry.BaseProcessor<OpenTelemetry.Logs.LogRecord>
{
public AkkaTraceContextProcessor() { }
public override void OnEnd(OpenTelemetry.Logs.LogRecord data) { }
}
public class LoggerFactoryLogger : Akka.Actor.ActorBase, Akka.Dispatch.IRequiresMessageQueue<Akka.Event.ILoggerMessageQueueSemantics>
{
protected readonly Akka.Event.ILoggingAdapter InternalLogger;
Expand All @@ -264,4 +273,4 @@ namespace Akka.Hosting.Logging
public LoggerFactorySetup(Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { }
public Microsoft.Extensions.Logging.ILoggerFactory LoggerFactory { get; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ namespace Akka.Hosting.TestKit.Internals
public class XUnitLogger : Microsoft.Extensions.Logging.ILogger
{
public XUnitLogger(string category, Xunit.Abstractions.ITestOutputHelper helper, Microsoft.Extensions.Logging.LogLevel logLevel) { }
public System.IDisposable BeginScope<TState>(TState state) { }
public System.IDisposable? BeginScope<TState>(TState state)
where TState : notnull { }
public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel) { }
public void Log<TState>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, TState state, System.Exception? exception, System.Func<TState, System.Exception?, string> formatter) { }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="7.0.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
<PackageReference Include="xunit" Version="$(XunitVersion)" />
<PackageReference Include="FluentAssertions" Version="6.12.2" />
Expand Down
2 changes: 1 addition & 1 deletion src/Akka.Hosting.TestKit/Akka.Hosting.TestKit.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<ItemGroup>
<PackageReference Include="Akka.TestKit.Xunit2" Version="$(AkkaVersion)" />
<PackageReference Include="Akka.Persistence.TestKit" Version="$(AkkaVersion)" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="7.0.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
<PackageReference Include="xunit" Version="$(XunitVersion)" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="$(MicrosoftExtensionsVersion)" />
<PackageReference Include="System.Text.Json" Version="$(SystemTextJsonVersion)" />
Expand Down
2 changes: 1 addition & 1 deletion src/Akka.Hosting.TestKit/Internals/XUnitLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public bool IsEnabled(LogLevel logLevel)
};
}

public IDisposable BeginScope<TState>(TState state)
public IDisposable? BeginScope<TState>(TState state) where TState : notnull
{
return NullScope.Instance;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Akka.Hosting.Tests/Akka.Hosting.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<PackageReference Include="Microsoft.Extensions.Hosting" Version="$(MicrosoftExtensionsVersion)" />
<PackageReference Include="System.Text.Json" Version="$(SystemTextJsonVersion)" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
<PackageReference Include="Akka.TestKit.Xunit2" Version="$(AkkaVersion)" />
<PackageReference Include="FluentAssertions" Version="6.12.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
Expand Down
Loading