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;
///
/// Concrete that routes each
/// onto the Akka DistributedPubSub script-logs topic
/// (). ScriptLogSignalRBridge subscribes
/// to that topic so entries reach the live Script-log Admin UI page.
///
///
///
/// The is resolved lazily through a
/// 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.
///
///
/// This type sits behind a Serilog sink (); 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).
///
///
public sealed class DpsScriptLogPublisher : IScriptLogPublisher
{
private readonly Func _system;
/// Initializes a new instance of the class.
///
/// Lazy accessor for the running . Invoked on each
/// so registration does not depend on Akka having started yet.
///
/// Thrown when is null.
public DpsScriptLogPublisher(Func system) =>
_system = system ?? throw new ArgumentNullException(nameof(system));
///
/// Publishes onto the DPS script-logs topic. Any failure
/// (system not yet ready, mediator unavailable) is swallowed so the logging pipeline is
/// never disrupted by a transient cluster condition.
///
/// The entry to publish.
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.");
}
}
}