Akka ILoggingAdapter output bypasses Serilog (DefaultLogger → stdout only) #120
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found while implementing #56 (ExecutionId log enrichment).
What: actors that log through Akka's
ILoggingAdapter(Context.GetLogger()) — e.g.DataConnectionActor,DataConnectionManagerActor,CertStoreActor,CentralCommunicationActor— never reach the Serilog pipeline. The host builds itsActorSystemfrom hand-assembled HOCON (AkkaHostedService.BuildHocon→ActorSystem.Create("scadabridge", config)) with noakka.loggerssetting and noAkka.Logger.Serilog/ Akka.HostingLoggerFactorybridge, so those lines go to Akka'sDefaultLogger(stdout) only:LoggerConfigurationFactoryconfigures,ExecutionId(#56): Akka logs asynchronously through its logger actor, so even a bridged adapter would not see the handler's SerilogLogContext.The arch review recorded the dual logging stacks as LOG-01 (
docs/plans/2026-09-09-arch-review.md:806).Options:
Microsoft.Extensions.Logging(Akka.Hosting'sLoggerFactoryLogger, orAkka.Logger.Serilog) so the lines at least land in the file sinks with the node enrichers. Fits the family baseline (one Serilog pipeline). Does not by itself giveExecutionId.ILoggingAdapterto an injectedILogger, asInstanceActor/ScriptActoralready do — then #56's per-messageAroundReceivepush tags them too. More churn; only worth it for actors that serve executions.Note: the protocol adapters the DCL calls (
RealOpcUaClient, the MxGateway adapter) already log throughILogger, and since #56 their lines on a script's write path DO carry theExecutionId. Only the DCL actors' own_loglines (mostly Debug on the write path) are affected.Fixed on main in
c810bd0f(pushed together with the #56 remainder).What changed
AkkaHostedService.BuildHoconnow setsakka.loggers = [ZB.MOM.WW.ScadaBridge.Host.Actors.SerilogAkkaLogger]. This replaces Akka's stdout-onlyDefaultLogger.Akka.Logger.Serilogdependency, because ScadaBridge keeps its own Serilog factory. It writes everyILoggingAdapterline to the static Serilog logger thatProgrambuilds throughLoggerConfigurationFactory, so the lines now reach the file sinks and carry theSiteId/NodeHostname/NodeRole/trace enrichers.SourceContextis the logging class's full name, so namespaceMinimumLevel.Overrides apply to Akka lines too.AkkaSourceis the actor path. Akka's own timestamp is kept, and the format arguments become structured properties.akka.loglevelstaysINFO, so Akka's Debug lifecycle chatter is still filtered at the adapter. The INFO+ lines that used to go to stdout now go through Serilog instead. Nothing new is produced.ExecutionLogContextgainedCurrent, anAsyncLocalmirror of the pushed id.ScriptRunLogScopenow pushes through it.DataConnectionActorandDataConnectionManagerActorlog through the newDataConnectionLayer.Logging.ExecutionAwareLogging.GetLogger(Context). Akka'sBusLoggingis sealed, so this is aLoggingAdapterBasewith the same publishing behaviour.ExecutionTaggedLogMessagewhile an id is current.ToString()is unchanged, so the stdout logger and TestKit filters see the same text.AroundReceivepush is visible on those lines.Evidence
dotnet build ZB.MOM.WW.ScadaBridge.slnx: 0 warnings, 0 errors.Host.Tests/SerilogAkkaLoggerTests: the line reaches Serilog with SourceContext, AkkaSource, node enricher and structured args; the ExecutionId crosses the logger hop; the timestamp is kept and every level maps; the Serilog minimum level applies; HOCON names the logger with loglevel INFO; an ActorSystem boots with the logger configured.DCL.Tests/Actors/ExecutionAwareLoggingTests: a line inside a push is tagged and its text is unchanged; a line outside a push is published unwrapped; a disabled level publishes nothing;DataConnectionActor's own write-path Debug lineWriting to tagis tagged with the request's id, with an uncorrelated write as the control.Commons.TestsExecutionLogContext.Currenttracks pushes and nesting and is restored on dispose.akka.loggersremoved from the HOCONDataConnectionActorreverted toContext.GetLogger()Docs: Component-Host REQ-HOST-8, Component-SiteRuntime, step-logs design §12, CLAUDE.md.