Skip to content

AOT (3/7): Props/actor-type trimming annotations, akka.loggers from a built-in table, Akka.DependencyInjection ripple - #8603

Merged
Aaronontheweb merged 4 commits into
devfrom
aot/m1-c-props-and-loggers
Sep 24, 2026
Merged

Aaronontheweb merged 4 commits into
devfrom
aot/m1-c-props-and-loggers

Conversation

@Aaronontheweb

Copy link
Copy Markdown
Member

Changes

Milestone-1 PR C of the Native AOT stack. Base: aot/m1-b-mailboxes (PR B). Three commits.

Why

Every actor Akka.NET creates is built through Props, and two pieces of metadata have to survive
trimming for that to work: the public constructor Activator.CreateInstance calls, and the
IRequiresMessageQueue<> interfaces Mailboxes.HasRequiredType reads off the actor type to pick a
mailbox. Neither was annotated, so the trimmer had no reason to keep either. The unrooted Native AOT
canary failed with MissingMethodException on Akka.Event.EventStreamUnsubscriber and on
Akka.Actor.CoordinatedShutdownExtension, and with No IProducesMessageQueue<TQueue> supplied for Akka.Event.LoggerMailboxType.

Separately, akka.loggers was the last type-name setting on the local boot path still going straight
through Type.GetType, so the trimmer could not tell that Akka.Event.DefaultLogger — the logger
core's own akka.conf names — was loaded, and dropped it.

What changed

1. Keep actor constructor and interface metadata through the Props type flow

One named contract, Props.ActorTypeMembers (PublicConstructors | Interfaces), on every Type and
generic parameter that reaches Activator.CreateInstance or the mailbox requirement check: the six
Type-taking Props constructors, Props.Type, Props.Create(Type, …), the three Create<TActor>
overloads, CreateBy<TProducer>, CreateProducer, ActivatorProducer, DefaultProducer,
FactoryConsumer<>, DynamicProps<>, IIndirectActorProducer.ActorType,
ActorRefFactoryExtensions.ActorOf<TActor>, and ExtendedActorSystem.SystemActorOf<TActor> with its
ActorSystemImpl override.

ActorSystemWithExtensions.WithExtension<T>(ActorSystem, Type) and WithExtension<T, TI> take the
narrower PublicParameterlessConstructor: those hand an IExtensionId — not an actor type — to the
parameterless Activator.CreateInstance, and that is exactly what the base branch's warnings asked
for.

2. Resolve akka.loggers from a built-in table

StartDefaultLoggers matches the configured name against the three loggers Akka.dll ships —
DefaultLogger, StandardOutLogger and TraceLogger, whose own XML doc tells users to name it here
— and returns the type from a constant typeof. Two spellings each — the bare name akka.conf ships
and the "Ns.T, Akka" form — with the configured value run through
TypeExtensions.StripAssemblyIdentity first, so a full AssemblyQualifiedName matches the second key
whatever version, culture or public key token it names. A literal
typeof(T).AssemblyQualifiedName key would only ever match the one build that compiled it: measured
here, that is Version=1.5.60.0, so Akka.Hosting HOCON written against any other Akka version would
miss the table and, with the switch off, throw. A miss falls back to Type.GetType in a
[RequiresUnreferencedCode] method that only runs while dynamic type loading is on; with the switch
off the site throws AkkaFeatures.NotBuiltIn.

The lookup is a string[] matched inside one method with an annotated return, not a
Dictionary<string, Type>. A dictionary cannot carry DynamicallyAccessedMembers on its values, so
the type coming out of TryGetValue arrives unannotated and the trimmer drops the constructor Props
needs. That is measured, not theoretical: the first draft of this commit used a dictionary, the
publish reported IL2067 at the AddLogger call, and the canary started DefaultLogger only to die
with MissingMethodException: No parameterless constructor defined for type 'Akka.Event.DefaultLogger'.

3. Carry the actor-type annotation through Akka.DependencyInjection

IIndirectActorProducer.ActorType is annotated in core now, so the three IDependencyResolver.Props
members, their implementations on DependencyResolver, ServiceProvider and
ServiceProviderDependencyResolver, and ServiceProviderActorProducer (both the Type-taking
constructor and the generic subclass) take Props.ActorTypeMembers, which the project can name
because core already grants it InternalsVisibleTo. ActivatorUtilities.CreateInstance asks for
PublicConstructors, which that constant covers.

Trim-warning count

Measured with the canary's README command on this base, deduped on code+file+line, counting only
warnings whose location is in src/core/Akka/:

base (aot/m1-b-mailboxes) this branch
unrooted (the canary's default) 21 12
-p:RootAkka=true (whole-library) 30 24

Nine warnings removed, three added, plus one that only moved.

Removed (8 from commit 1, 1 from commit 2):

IL2072 Actor/ActorCell.cs:243        IL2067 Actor/Extensions.cs:116
IL2072 Actor/ActorCell.cs:245        IL2087 Actor/Extensions.cs:136
IL2072 Dispatch/Mailboxes.cs:433     IL2067 Actor/Props.cs:588
IL2072 Dispatch/Mailboxes.cs:452     IL2072 Actor/Props.cs:691
IL2057 Event/LoggingBus.cs:117

Added — all three only appear under -p:RootAkka=true, because the unrooted canary never reaches
them, and none is on the local boot path:

IL2072 Actor/Props.cs:666            PropsSurrogate.FromSurrogate hands its deserialized Type field
                                     to the Props constructor. That type comes off the wire, so there
                                     is nothing for the trimmer to root; remote deployment already
                                     requires dynamic type loading. Expected and left alone.
IL2072 IO/Dns.cs:275                 IDnsProvider.ManagerClass / ActorClass reach Props.Create. The
IL2072 IO/SimpleDnsManager.cs:32     separate DNS PR later in this stack removes both by constructing
                                     those two actors directly instead of by name.

Moved, not added: IL2057 Actor/Props.cs:230 → :242, the Props.TypeName setter's Type.GetType,
shifted by the twelve lines this PR inserts above it.

Viral consequence for downstream generics

Nothing warns today, because no project in this repository sets EnableTrimAnalyzer, IsTrimmable
or IsAotCompatible. When one does, Akka.TestKit's generic forwarders will report IL2091 until
they carry the same constant: TestKitBase.ActorOf<TActor>,
ActorOfAsTestActorRef<TActor>, ActorOfAsTestFSMRef<TFsmActor, TState, TData> and
TestProbe.ChildActorOf<T> all pass their type parameter to Props.Create<TActor>. The same applies
to any external IIndirectActorProducer, IDependencyResolver implementation or
SystemActorOf<TActor> override — a DynamicallyAccessedMembers attribute is part of the contract,
so an implementor has to repeat it verbatim rather than inherit it, and gets IL2092/IL2093
(parameter/return mismatch) or IL2095 (type-parameter mismatch) until it does.

Canary status

The unrooted canary now boots fully with dynamic type loading switched off: both ActorSystems
come up, DefaultLogger really starts, both Ask round trips reply, and both terminate cleanly.

It still exits 1, at the startup watchdog, on the four serializer warnings PR D removes:

The type name for serializer 'json' did not resolve to an actual Type: 'Akka.Serialization.NewtonSoftJsonSerializer, Akka'
The type name for serializer 'bytes' did not resolve to an actual Type: 'Akka.Serialization.ByteArraySerializer, Akka'
Serialization binding to non existing serializer: 'bytes'
Serialization binding to non existing serializer: 'json'

With the watchdog and the two serializer assertions locally disabled (not committed), the canary
reaches [canary] OK with exit 0 and prints serializers, scheduler, log formatter and default mailbox all resolved, so A's scheduler and log-formatter tables and B's mailbox tables all pass
their positive assertions too. Serialization is the only thing left between this stack and a green
canary.

Public API

Attributes only — pre-approved by the repo owner. CoreAPISpec.ApproveCore.DotNet.verified.txt gains
[DynamicallyAccessedMembers(...)] on 16 members and nothing else: no signature, member or other
attribute changes. Akka.DependencyInjection has no approval file, so commit 3 produces no churn.

Tests

New src/core/Akka.Tests/Event/BuiltInLoggersSpec.cs, in the non-parallel
Akka.DynamicTypeLoading collection:

  • three theories, one per built-in logger, over four spellings each — the two table keys, the current
    AssemblyQualifiedName, and a Version=99.0.0.0, PublicKeyToken=… one that can only resolve
    because the lookup strips assembly identity — with the switch off —
    DefaultLogger and TraceLogger resolve the started logger actor by wildcard ActorSelection
    (the logger name carries a process-global counter, so it cannot be hard-coded); StandardOutLogger
    asserts the system comes up, since a MinimalLogger is never started as an actor and only
    suppresses the stdout logger's removal — which is what LoggerSpec relies on.
  • a custom logger with the switch on, asserting the logger actor answered InitializeLogger.
    This is the switch-on regression guard.
  • the same logger with the switch off, asserting ConfigurationException names the setting, the
    value and the switch.

Breaking changes

One row in BREAKING_CHANGES_V1.6.md. No runtime behavior change from the attributes; with the
switch on, akka.loggers resolves exactly as before for every input. With the switch off, only the
built-in loggers resolve.

Stack: PR 3 of 7 for AOT milestone 1. Base is PR #8602 (B); this PR shows only its own three commits. With A+B+C the canary boots, round-trips and terminates under Native AOT; it fails only on the four serializer startup warnings that PR D removes. Design and measurements: epic #7246.

Checklist

@Aaronontheweb Aaronontheweb added the AOT Ahead-of-Time (AOT) Compilation label Sep 23, 2026
@Aaronontheweb
Aaronontheweb added this pull request to stack #8607 September 23, 2026 02:07
@Aaronontheweb Aaronontheweb added this to the 1.6.0 milestone Sep 23, 2026
@Aaronontheweb
Aaronontheweb deleted the aot/m1-c-props-and-loggers branch September 23, 2026 02:24
@Aaronontheweb
Aaronontheweb restored the aot/m1-c-props-and-loggers branch September 23, 2026 02:41
@Aaronontheweb Aaronontheweb reopened this Sep 23, 2026
@Aaronontheweb
Aaronontheweb force-pushed the aot/m1-c-props-and-loggers branch from 761fad5 to 5bb17d7 Compare September 23, 2026 02:46
@Aaronontheweb
Aaronontheweb force-pushed the aot/m1-c-props-and-loggers branch from 5bb17d7 to 6523341 Compare September 23, 2026 14:19
@Aaronontheweb
Aaronontheweb force-pushed the aot/m1-c-props-and-loggers branch from 6523341 to 8c8730f Compare September 23, 2026 14:47
@Aaronontheweb
Aaronontheweb force-pushed the aot/m1-c-props-and-loggers branch from 8c8730f to 20e1ffe Compare September 23, 2026 18:09
@Aaronontheweb
Aaronontheweb force-pushed the aot/m1-c-props-and-loggers branch from 20e1ffe to 1023140 Compare September 23, 2026 21:13
@Aaronontheweb
Aaronontheweb force-pushed the aot/m1-c-props-and-loggers branch from 1023140 to 830d6a0 Compare September 24, 2026 02:05
Base automatically changed from feature/aot-m1-b-mailboxes to dev September 24, 2026 03:31
@Aaronontheweb
Aaronontheweb force-pushed the aot/m1-c-props-and-loggers branch from 830d6a0 to 8f01d80 Compare September 24, 2026 03:31
…flow

Every actor type Akka.NET instantiates arrives through Props, and two things have to survive
trimming once it does: the public constructor Activator.CreateInstance calls, and the
IRequiresMessageQueue<> interfaces Mailboxes.HasRequiredType reads off the type to pick a
mailbox. Neither was annotated, so the trimmer had no reason to keep either, and the unrooted
Native AOT canary failed with MissingMethodException on Akka.Event.EventStreamUnsubscriber and
on Akka.Actor.CoordinatedShutdownExtension, and with "No IProducesMessageQueue<TQueue> supplied
for Akka.Event.LoggerMailboxType".

Props now carries one named contract, Props.ActorTypeMembers (PublicConstructors | Interfaces),
along the whole flow: the Type-taking Props constructors, Props.Type, Props.Create(Type, ...),
the three Create<TActor> overloads, CreateBy<TProducer>, CreateProducer, ActivatorProducer,
DefaultProducer, FactoryConsumer<>, DynamicProps<>, IIndirectActorProducer.ActorType,
ActorRefFactoryExtensions.ActorOf<TActor>, and ExtendedActorSystem.SystemActorOf<TActor> with its
ActorSystemImpl override. Using the same constant everywhere means the annotation flows from a
caller's typeof(...) to the Activator call without a gap that trim analysis has to warn about.

ActorSystemWithExtensions.WithExtension<T>(ActorSystem, Type) and WithExtension<T, TI> take the
narrower PublicParameterlessConstructor instead. They are not on the Props path - the type they
hand to Activator is an IExtensionId, there is no mailbox requirement to read off it, and both
call the parameterless CreateInstance - so that is exactly what the trimmer needs to keep and
exactly what the warnings on the base branch asked for.

Measured against the base branch, this removes eight trim warnings, all of them an actor type or
Props.Type losing its annotation at a call site: IL2072 at ActorCell.cs:243 and :245, at
Mailboxes.cs:433 and :452 and at Props.cs:691, IL2067 at Extensions.cs:116 and Props.cs:588, and
IL2087 at Extensions.cs:136.

It adds three, all of them a Type that now flows into an annotated parameter from somewhere that
is not annotated, and none of them on the local boot path. They only appear with -p:RootAkka=true,
because the unrooted canary never reaches them: IL2072 at Props.cs:666, where PropsSurrogate hands
its deserialized Type field to the Props constructor - that type comes off the wire, so there is
nothing for the trimmer to root, and remote deployment already needs dynamic type loading - and
IL2072 at IO/Dns.cs:275 and IO/SimpleDnsManager.cs:32, where IDnsProvider.ManagerClass/ActorClass
reach Props.Create. The DNS pair goes away later in this stack, when those two actors are
constructed directly instead of by name.

Nothing about this changes at runtime. The attributes are only visible to trim analysis, so
the approved public API surface grows the attributes and nothing else.
akka.loggers is the last type-name setting on the local boot path that still went straight
through Type.GetType, so the trimmer could not tell that Akka.Event.DefaultLogger - the one
logger core's own akka.conf names - was loaded, and dropped it. The unrooted Native AOT canary
died in StartDefaultLoggers with ConfigurationException 'Logger specified in config cannot be
found: "Akka.Event.DefaultLogger"'.

StartDefaultLoggers now asks GetBuiltInLoggerType, which matches the configured name against the
names of the three loggers Akka.dll ships - DefaultLogger, StandardOutLogger and TraceLogger,
whose own documentation tells users to name it here - and returns the type from a constant typeof
expression. A value that misses those tables falls back to the old Type.GetType, moved into a
private static method marked [RequiresUnreferencedCode], and that fallback only runs while
dynamic type loading is on; with the switch off the site throws a ConfigurationException built by
AkkaFeatures.NotBuiltIn, so it names the setting, the value and the switch in the same words as
every other site.

Each logger carries two spellings - the bare name and the "Ns.T, Akka" form - and the lookup runs
the configured value through TypeExtensions.StripAssemblyIdentity first, so a full
AssemblyQualifiedName matches the second key whatever version, culture or public key token it
names. That matters for real input rather than in theory: Akka.Hosting writes the versioned form
into HOCON, and a literal typeof(T).AssemblyQualifiedName key would only ever match the one build
that compiled it, so HOCON written against any other Akka version would miss the table and, with
the switch off, throw. The spec covers that case with a Version=99.0.0.0 spelling per logger.

The lookup is a string[] matched inside one method with an annotated return, not the
Dictionary<string, Type> the sibling tables use, because a dictionary cannot carry
DynamicallyAccessedMembers on its values: the type coming back out of TryGetValue arrives
unannotated, and the trimmer then drops the constructor Props needs. That is not theoretical -
the first version of this commit used a dictionary, the publish reported IL2067 at the AddLogger
call, and the canary started DefaultLogger only to get MissingMethodException 'No parameterless
constructor defined for type Akka.Event.DefaultLogger'. The annotated return fixes both.

The table hands back a Type rather than a factory because StartDefaultLoggers goes on to call
Props.Create(loggerType) and start an actor; with the annotations from the previous commit that
Type carries its constructor and interface metadata through to the Activator call. This is the
ninth trim warning the branch removes: IL2057 at LoggingBus.cs:117 on the base branch.

Behavior with the switch on is unchanged for every input: Akka's HOCON already trims a value in
every syntax - quoted or bare, scalar or list element - so there is nothing left for this code to
normalize beyond assembly identity, and it does not touch config keys. Recorded in
BREAKING_CHANGES_V1.6.md.
IIndirectActorProducer.ActorType is annotated in core now, so every path that feeds it has to
carry the same contract or trim analysis reports the gap at the boundary. The three
IDependencyResolver.Props members, their implementations on DependencyResolver, ServiceProvider
and ServiceProviderDependencyResolver, and ServiceProviderActorProducer (both the Type-taking
constructor and the generic subclass) take Props.ActorTypeMembers, which Akka.DependencyInjection
can name directly because core already grants it InternalsVisibleTo.

ServiceProviderActorProducer.Produce builds the actor through
ActivatorUtilities.CreateInstance, whose Type parameter asks for PublicConstructors - the half
of Props.ActorTypeMembers that covers Activator - so the same constant satisfies it; the
Interfaces half is what the mailbox requirement check needs afterwards.

Annotation-only: no runtime behavior changes, and Akka.DependencyInjection has no API approval
file, so there is no approved-surface churn either.
…tInLoggersSpec

Should_accept_the_built_in_standard_out_logger_When_dynamic_type_loading_is_disabled
asserted system.Settings.StdoutLogger.Should().BeOfType<StandardOutLogger>(),
which is always true - StdoutLogger comes from akka.stdout-logger-class,
not from the akka.loggers spelling under test. Remove that assertion;
the test's real check is that ActorSystem.Create does not throw.

Replace the local SwitchName const with AkkaFeaturesSpec.SwitchName.
@Aaronontheweb
Aaronontheweb force-pushed the aot/m1-c-props-and-loggers branch from 8f01d80 to d626066 Compare September 24, 2026 13:58

@Aaronontheweb Aaronontheweb left a comment

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.

LGTM

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.

LGTM

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.

LGTM

[Fact(DisplayName = "LoggingBus should resolve a logger named in HOCON when dynamic type loading is on")]
public async Task Should_resolve_a_custom_logger_When_dynamic_type_loading_is_enabled()
{
await AkkaFeaturesSpec.WithDynamicTypeLoading(true, async () =>

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.

LGTM

}

[Fact(DisplayName = "LoggingBus should reject a logger named in HOCON that is not built in when dynamic type loading is off")]
public async Task Should_throw_ConfigurationException_When_the_logger_is_not_built_in_and_dynamic_type_loading_is_disabled()

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.

LGTM

/// </exception>
/// <returns>A reference to the underlying actor.</returns>
public override IActorRef SystemActorOf<TActor>(string name = null)
public override IActorRef SystemActorOf<[DynamicallyAccessedMembers(Props.ActorTypeMembers)] TActor>(string name = null)

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.

LGTM

/// <param name="name">The name of the actor to create. The default value is <see langword="null"/>.</param>
/// <returns>A reference to the newly created actor</returns>
public abstract IActorRef SystemActorOf<TActor>(string name = null) where TActor : ActorBase, new();
public abstract IActorRef SystemActorOf<[DynamicallyAccessedMembers(Props.ActorTypeMembers)] TActor>(string name = null) where TActor : ActorBase, new();

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.

LGTM

/// <returns>The extension retrieved from the given actor system.</returns>
public static T WithExtension<T>(this ActorSystem system, Type extensionId) where T : class, IExtension
public static T WithExtension<T>(this ActorSystem system,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type extensionId)

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.

LGTM

/// What the trimmer has to keep on an actor type reached through <see cref="Props"/>: the public
/// constructors <see cref="Activator"/> needs, and the interface list the mailbox requirement check walks.
/// </summary>
internal const DynamicallyAccessedMemberTypes ActorTypeMembers =

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.

LGTM

/// the actor type; an implementation returning a <see cref="Type" /> from elsewhere has to carry the
/// same annotation on whatever it reads that type from.
/// </remarks>
[DynamicallyAccessedMembers(Props.ActorTypeMembers)]

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.

LGTM

@Aaronontheweb
Aaronontheweb removed this pull request from stack #8607 September 24, 2026 16:42
@Aaronontheweb
Aaronontheweb added this pull request to stack #8632 September 24, 2026 16:43
@Aaronontheweb
Aaronontheweb merged commit a26b03e into dev Sep 24, 2026
16 checks passed
@Aaronontheweb
Aaronontheweb deleted the aot/m1-c-props-and-loggers branch September 24, 2026 16:44
Aaronontheweb added a commit to Aaronontheweb/akka.net that referenced this pull request Sep 28, 2026
…hat couldn't fail, add switch-on version-skew coverage

- Drop the LoadFirstPartyLoggerType try/catch helper: Type.GetType on a literal
  is a trimmer intrinsic, so the switch arm calls it directly (matches
  GetBuiltInLoggerType's style). throwOnError defaults to false, so a missing
  Akka.Hosting still returns null; a broken dll now surfaces its own exception
  instead of being swallowed.
- BuiltInLoggersSpec's "never probes" test only named built-in loggers, so
  GetFirstPartyLoggerType never ran. It now uses CustomLoggerConfig, which is
  not built in, so the lookup actually executes and asserts Akka.Hosting is
  never probed.
- Add a switch-on version-skew test to FirstPartyLoggerSpec: a Hosting logger
  name pinned to a different Akka.Hosting version now resolves with the
  DynamicTypeLoading switch on, not just off. Make the spec sealed and take
  ITestOutputHelper.
- Amend the akkadotnet#8603 ledger row: switch-off apps can also use Akka.Hosting's
  LoggerFactoryLogger, not just the three core loggers. Add a new row for
  this PR.
Aaronontheweb added a commit that referenced this pull request Sep 29, 2026
…esolves without reflection (#8654)

* AOT: resolve Akka.Hosting's LoggerFactoryLogger in akka.loggers without reflection

With Akka.DynamicTypeLoading off, LoggingBus only accepted the three loggers
in Akka.dll, so any Akka.Hosting app using AddLoggerFactory() failed at
startup with a NotBuiltIn ConfigurationException.

LoggingBus now checks a first-party table after the built-in one. A row
matches only on the exact type name plus assembly (identity stripped, per
#8613), then loads its own literal through a DynamicallyAccessedMembers
string parameter so the trimmer keeps the type. An assembly that will not
load counts as absent and falls through to today's behavior. Configs that
name only core loggers never probe.

Tests: switch-off startup and switch-on parity in Akka.Hosting.Tests;
no-probe and absent-assembly checks in Akka.Tests.

* Review fixes: simplify GetFirstPartyLoggerType, fix a no-probe test that couldn't fail, add switch-on version-skew coverage

- Drop the LoadFirstPartyLoggerType try/catch helper: Type.GetType on a literal
  is a trimmer intrinsic, so the switch arm calls it directly (matches
  GetBuiltInLoggerType's style). throwOnError defaults to false, so a missing
  Akka.Hosting still returns null; a broken dll now surfaces its own exception
  instead of being swallowed.
- BuiltInLoggersSpec's "never probes" test only named built-in loggers, so
  GetFirstPartyLoggerType never ran. It now uses CustomLoggerConfig, which is
  not built in, so the lookup actually executes and asserts Akka.Hosting is
  never probed.
- Add a switch-on version-skew test to FirstPartyLoggerSpec: a Hosting logger
  name pinned to a different Akka.Hosting version now resolves with the
  DynamicTypeLoading switch on, not just off. Make the spec sealed and take
  ITestOutputHelper.
- Amend the #8603 ledger row: switch-off apps can also use Akka.Hosting's
  LoggerFactoryLogger, not just the three core loggers. Add a new row for
  this PR.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AOT Ahead-of-Time (AOT) Compilation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant