Files
lmxopcua/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Scripting/DpsScriptLogPublisher.cs
T
Joseph Doherty 9cad9ed0fc
v2-ci / build (push) Failing after 41s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
docs(xmldoc): fill missing XML docs + strip tracking-ID comments across src
Adds <summary>/<param>/<returns>/<inheritdoc> where missing and removes
project bookkeeping IDs (task/tracking refs) from shipped code comments,
so the docs read cleanly and CommentChecker is quiet except for known
false positives (PLC/protocol terms, event/IEqualityComparer inheritdoc).
Doc/comment-only; no logic changed; solution builds clean.
2026-07-07 12:38:39 -04:00

56 lines
2.5 KiB
C#

using Akka.Actor;
using Akka.Cluster.Tools.PublishSubscribe;
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Logging;
using ZB.MOM.WW.OtOpcUa.Core.Scripting;
using ZB.MOM.WW.OtOpcUa.Runtime.VirtualTags;
namespace ZB.MOM.WW.OtOpcUa.Runtime.Scripting;
/// <summary>
/// Concrete <see cref="IScriptLogPublisher"/> that routes each <see cref="ScriptLogEntry"/>
/// onto the Akka DistributedPubSub <c>script-logs</c> topic
/// (<see cref="VirtualTagActor.ScriptLogsTopic"/>). <c>ScriptLogSignalRBridge</c> subscribes
/// to that topic so entries reach the live Script-log Admin UI page.
/// </summary>
/// <remarks>
/// <para>
/// The <see cref="ActorSystem"/> is resolved lazily through a <see cref="Func{ActorSystem}"/>
/// so constructing the publisher never races Akka startup — the system only needs to exist
/// by the time the first script logs, not at DI-registration time.
/// </para>
/// <para>
/// This type sits behind a Serilog sink (<see cref="ScriptLogTopicSink"/>); a sink must
/// never throw back into the logging pipeline, so every failure path here is swallowed
/// (best-effort logged at Debug to the main Serilog logger).
/// </para>
/// </remarks>
public sealed class DpsScriptLogPublisher : IScriptLogPublisher
{
private readonly Func<ActorSystem> _system;
/// <summary>Initializes a new instance of the <see cref="DpsScriptLogPublisher"/> class.</summary>
/// <param name="system">
/// Lazy accessor for the running <see cref="ActorSystem"/>. Invoked on each
/// <see cref="Publish"/> so registration does not depend on Akka having started yet.
/// </param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="system"/> is <c>null</c>.</exception>
public DpsScriptLogPublisher(Func<ActorSystem> system) =>
_system = system ?? throw new ArgumentNullException(nameof(system));
/// <inheritdoc />
public void Publish(ScriptLogEntry entry)
{
try
{
var mediator = DistributedPubSub.Get(_system()).Mediator;
mediator.Tell(new Publish(VirtualTagActor.ScriptLogsTopic, entry));
}
catch (Exception ex)
{
// A logging sink must never throw into the logging pipeline. Best-effort note to
// the main log at Debug; nothing actionable for the script author here.
Serilog.Log.Debug(ex, "DpsScriptLogPublisher could not route a script log entry to the cluster bus.");
}
}
}