Skip to content

Add LoggerSetup for AOT-compatible custom logger registration (#7902) - #8218

Merged
Aaronontheweb merged 9 commits into
akkadotnet:devfrom
ArinF1:feature/logger-setup-aot-7902
Sep 30, 2026
Merged

Aaronontheweb merged 9 commits into
akkadotnet:devfrom
ArinF1:feature/logger-setup-aot-7902

Conversation

@ArinF1

@ArinF1 ArinF1 commented May 19, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Resolves #7902

Adds LoggerSetup to enable programmatic, AOT-compatible custom logger
registration — an alternative to HOCON-based logger config, which relies
on Type.GetType() and is not safe in AOT scenarios.

Changes

  • Akka.Event.LoggerSetup — a Setup subclass holding an ordered
    list of logger Props, built with LoggerSetup.Create(params Props[])
    or LoggerSetup.Create(IEnumerable<Props>). It mirrors the shape of
    ExtensionsSetup.
  • LoggerSetup.Formatter — an optional ILogMessageFormatter, set
    through LoggerSetup.Create(ILogMessageFormatter, params Props[]). The
    same AOT problem that applies to a custom logger type also applies to a
    third-party log message formatter — for example Akka.Logger.Serilog's
    SerilogLogMessageFormatter or an NLog equivalent — since
    akka.logger-formatter also resolves by Type.GetType().
  • LoggingBus.StartDefaultLoggers — starts the LoggerSetup loggers
    and the HOCON akka.loggers list together (see "Maintainer changes"
    below).

Usage

var setup = ActorSystemSetup.Create(
    LoggerSetup.Create(new SerilogLogMessageFormatter(), Props.Create<MyCustomLogger>()));

var system = ActorSystem.Create("MySystem", setup);

A LoggerSetup is additive, not a replacement: the default
akka.loggers = [DefaultLogger] keeps printing to standard output
alongside MyCustomLogger above, unless you also set akka.loggers = [].
Formatter works the same way for akka.logger-formatter — it wins when
set, otherwise HOCON resolves the formatter as it does today.

Maintainer changes

I made the following changes before merge, based on review feedback:

  • Additive, not replacing. LoggerSetup used to skip akka.loggers
    entirely. That silently dropped any HOCON-configured logger, including
    Akka.Hosting's LoggerFactoryLogger (written by AddLoggerFactory()).
    StartDefaultLoggers now starts the LoggerSetup loggers and the HOCON
    list together. A logger type named by both starts once — the
    LoggerSetup is checked first and wins, the same order
    ActorSystemImpl.LoadExtensions uses for ExtensionsSetup versus
    akka.extensions. One side effect: a type repeated more than once
    within akka.loggers itself now also starts once, not once per
    occurrence.
  • Shrank the API to the ExtensionsSetup shape. Dropped
    LoggerRegistration and LoggerSetupBuilder — LoggerSetup now just
    holds IReadOnlyList<Props>, built with Create(params Props[]) /
    Create(IEnumerable<Props>). Props.Create<T>() already carries the
    trimming annotations Prop-based construction needs, so the extra
    wrapper types and their own [DynamicallyAccessedMembers] plumbing
    added no value. LoggingBus now has a single AddLogger(system, Props, ...), and the HOCON path builds Props.Create(type) itself.
  • Fixed a generic-logger name bug this shrink surfaced: a generic
    logger's Type.Name carries a backtick and an arity digit (e.g.
    MyLogger`1) that isn't a legal actor-path character;
    CreateLoggerName now swaps it out.
  • Added LoggerSetup.Formatter, so a custom ILogMessageFormatter
    (Serilog, NLog, etc.) has the same code-based escape hatch a custom
    logger type already gets. Settings checks it before falling back to
    akka.logger-formatter, and the NotBuiltIn message for that setting
    now mentions it as the code alternative.
  • Merged dev into the branch to pick up the AOT and logger-resolution
    work that landed since this PR opened, and resolved the merge conflict
    in LoggingBus.cs using the additive design above.
  • Replaced the spec file with a single generic CapturingLogger<TMarker>
    (a static per-closed-type instance counter, no polling loops) and specs
    covering: a HOCON logger plus a LoggerSetup logger that also repeats
    the HOCON type (proves the additive behavior and the dedup); a
    LoggerSetup logger and formatter both working with
    Akka.DynamicTypeLoading switched off; and the LoggerSetup formatter
    winning over akka.logger-formatter when both are set.
  • Updated CoreAPISpec.ApproveCore.DotNet.verified.txt for the final
    surface; left CoreAPISpec.ApproveCore.Net.verified.txt matching dev,
    since this environment can't build or verify that target.

Verified: dotnet build -warnaserror on Akka.csproj; the Logger specs
3 times each (no flakes); the Logger, Event, and AkkaFeaturesSpec specs
in Akka.Tests (235 passed); Akka.API.Tests (24 passed); and the trim
analyzer, which reports the same warnings as dev with none new from
LoggerSetup.cs, LoggingBus.cs, or Settings.cs.

Follow-up

Not part of this PR, but worth tracking separately:

  • Akka.Hosting's LoggerConfigBuilder.AddLogger<T> writes a type name
    into HOCON today. It should route through LoggerSetup instead, now
    that one exists.
  • Only one LoggerSetup exists per system — like any Setup, a second
    one passed to ActorSystemSetup.And replaces the first rather than
    merging with it. That is consistent with ExtensionsSetup today, but
    it means two libraries that both want to register a logger through
    LoggerSetup will need to coordinate.

@ArinF1
ArinF1 force-pushed the feature/logger-setup-aot-7902 branch 2 times, most recently from 1c03a87 to 6b8eeb2 Compare May 20, 2026 19:31
@Aaronontheweb Aaronontheweb added this to the 1.5.70 milestone Jun 15, 2026
@Aaronontheweb Aaronontheweb modified the milestones: 1.5.70, 1.5.71 Jul 3, 2026
…tnet#7902)

- Add LoggerSetup, LoggerRegistration, LoggerSetupBuilder to Akka.Event
- Modify LoggingBus.StartDefaultLoggers to check LoggerSetup before HOCON
- Add unit tests in LoggerSetupSpec
- Update API approval files
@ArinF1
ArinF1 force-pushed the feature/logger-setup-aot-7902 branch from 74eda91 to 2c0b994 Compare July 21, 2026 20:00
@Aaronontheweb Aaronontheweb modified the milestones: 1.5.71, 1.5.72 Aug 27, 2026

@ArinF1 ArinF1 left a comment

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.

n

# Conflicts:
#	src/core/Akka/Event/LoggingBus.cs
…afety in tests and API approvals

Adds specs for a LoggerSetup logger running alongside a HOCON logger, the same
type appearing in both starting once, and a LoggerSetup logger working with
Akka.DynamicTypeLoading switched off. Updates the LoggerRegistration/LoggerSetupBuilder
API approval entries for the ExtendedActorSystem factory type and the
DynamicallyAccessedMembers annotations added during the dev merge.
@Aaronontheweb

Copy link
Copy Markdown
Member

Thanks for this, @ArinF1 — LoggerSetup is a nice addition for AOT users. I pushed a few follow-up commits to your branch to get it ready for merge:

  • Made LoggerSetup additive with akka.loggers instead of replacing it, so it doesn't silently drop a HOCON-configured logger (like Akka.Hosting's LoggerFactoryLogger). A logger type named in both now starts once.
  • Changed the factory delegate to take ExtendedActorSystem instead of the internal ActorSystemImpl.
  • Added [DynamicallyAccessedMembers] annotations so the trimmer keeps what Props.Create needs.
  • Merged dev in and added a few more specs covering the additive behavior and the dynamic-type-loading-off path.

Everything's green: build, the logger/event tests, the AOT feature specs, and the API approval tests. Details are in the PR description under "Maintainer changes." Nice work, and thanks for your patience while this sat in the queue.

…edup, generic-safe logger names

Address review feedback on the additive LoggerSetup design:

- LoggerSetup now mirrors ExtensionsSetup - a private constructor, an
  IReadOnlyList<Props>, and Create(params Props[]) / Create(IEnumerable<Props>)
  factories. Drops LoggerRegistration and LoggerSetupBuilder; Props.Create<T>()
  already carries the trimming annotations, so the extra wrapper types and
  DynamicallyAccessedMembers plumbing added no value.
- StartDefaultLoggers checks the LoggerSetup before akka.loggers, so a type
  named by both is the LoggerSetup's instance that survives, matching the
  order ActorSystemImpl.LoadExtensions uses for ExtensionsSetup versus
  akka.extensions. Merges the two AddLogger overloads into one that takes
  Props directly; the HOCON path now builds Props.Create(type) itself.
  Documents the loggers-dispatcher override that applies to every logger
  regardless of source.
- Fixes CreateLoggerName for a generic logger type: Type.Name carries a
  backtick-arity marker (e.g. MyLogger`1) that is not a legal actor-path
  character.
- Replaces LoggerSetupSpec with a single generic CapturingLogger<TMarker>
  (a static per-closed-type instance counter, no polling) and two specs:
  a HOCON logger plus a LoggerSetup logger that also repeats the HOCON
  type, and a LoggerSetup logger starting with Akka.DynamicTypeLoading
  switched off.
- Reverts CoreAPISpec.ApproveCore.Net.verified.txt to dev's version; updates
  only the DotNet variant for the new surface.
@Aaronontheweb Aaronontheweb added AOT Ahead-of-Time (AOT) Compilation logging labels Sep 30, 2026
Lets a LoggerSetup carry an ILogMessageFormatter, so a third-party
formatter (e.g. Akka.Logger.Serilog's SerilogLogMessageFormatter) works
with Akka.DynamicTypeLoading off, the same way LoggerSetup already lets a
custom logger sidestep HOCON's Type.GetType resolution.

- LoggerSetup.Formatter is null by default; Settings now checks it before
  falling back to akka.logger-formatter, so a LoggerSetup formatter wins
  when present and today's HOCON path is unchanged otherwise.
- Adds LoggerSetup.Create(ILogMessageFormatter, params Props[]) alongside
  the existing Create(params Props[]) / Create(IEnumerable<Props>); all
  three share one internal validation path.
- Updates the akka.logger-formatter NotBuiltIn message to mention a
  LoggerSetup formatter as the code alternative.
- Extends the dynamic-type-loading-off spec to also register a formatter
  and check both the Settings.LogFormatter instance and the formatted log
  output, and adds a precedence spec confirming the LoggerSetup formatter
  wins over akka.logger-formatter.
- Updates the DotNet API approval file for the new member and overload.
@Aaronontheweb Aaronontheweb modified the milestones: 1.5.72, 1.6.0 Sep 30, 2026

@Aaronontheweb Aaronontheweb left a comment

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 - thank you!

@Aaronontheweb
Aaronontheweb merged commit cf25db8 into akkadotnet:dev Sep 30, 2026
16 checks passed
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 logging

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AOT: Add LoggerSetup for AOT-compatible custom logger registration

2 participants