61 lines
2.8 KiB
C#
61 lines
2.8 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));
|
|
|
|
/// <summary>
|
|
/// Publishes <paramref name="entry"/> onto the DPS <c>script-logs</c> topic. Any failure
|
|
/// (system not yet ready, mediator unavailable) is swallowed so the logging pipeline is
|
|
/// never disrupted by a transient cluster condition.
|
|
/// </summary>
|
|
/// <param name="entry">The entry to publish.</param>
|
|
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.");
|
|
}
|
|
}
|
|
}
|