using Serilog.Core; using Serilog.Events; using ZB.MOM.WW.OtOpcUa.Commons.Messages.Logging; namespace ZB.MOM.WW.OtOpcUa.Core.Scripting; /// /// Serilog sink that converts each script into a /// and forwards it to an . /// The publisher implementation (supplied by a later task) routes the entry onto the /// Akka DPS script-logs topic so the live Script-log Admin UI page can display it. /// /// /// /// Expected to be registered in the root script-logger pipeline alongside the /// rolling scripts-*.log file sink and the /// . Events below the configured minimum level /// are silently discarded so Debug/Verbose noise does not saturate the cluster bus /// in production deployments. /// /// /// Identity properties (ScriptId, VirtualTagId, AlarmId, /// EquipmentId) are lifted from the event's structured-property bag. If /// ScriptId is absent the sink falls back to the legacy ScriptName /// property so pipelines that pre-date this sink continue to work correctly. If /// neither property is present ScriptId defaults to "unknown". /// /// public sealed class ScriptLogTopicSink : ILogEventSink { private readonly IScriptLogPublisher _publisher; private readonly LogEventLevel _min; /// Initializes a new instance of the class. /// The publisher that routes entries to the cluster bus. Must not be null. /// Minimum log level to forward. Events below this level are discarded (default: ). /// Thrown when is null. public ScriptLogTopicSink(IScriptLogPublisher publisher, LogEventLevel min = LogEventLevel.Information) { _publisher = publisher ?? throw new ArgumentNullException(nameof(publisher)); _min = min; } /// /// Converts the to a and /// publishes it, unless the event is below the configured minimum level. /// /// The Serilog event to process. Silently ignored when null. public void Emit(LogEvent logEvent) { if (logEvent is null || logEvent.Level < _min) return; // Extract a string scalar property by key, returning null when absent or non-string. string? P(string key) => logEvent.Properties.TryGetValue(key, out var v) && v is ScalarValue { Value: string s } ? s : null; _publisher.Publish(new ScriptLogEntry( ScriptId: P(ScriptLoggerFactory.ScriptIdProperty) ?? P(ScriptLoggerFactory.ScriptNameProperty) ?? "unknown", Level: logEvent.Level.ToString(), Message: logEvent.RenderMessage(), TimestampUtc: logEvent.Timestamp.UtcDateTime, VirtualTagId: P(ScriptLoggerFactory.VirtualTagIdProperty), AlarmId: P(ScriptLoggerFactory.AlarmIdProperty), EquipmentId: P(ScriptLoggerFactory.EquipmentIdProperty))); } }