feat(site-runtime): FlowTraceCollector
Per-run inline sink for ScriptTrace audit events (Flow Debugger design 2026-09-05 §5.4): cap 500, Truncated flag, lock-based; the audit rows stay complete beyond the cap. Claude-Session: https://claude.ai/code/session_01GSLkkHKz9JrAtmJjmQK3cP
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
using ZB.MOM.WW.Audit;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.SiteRuntime.Scripts;
|
||||
|
||||
/// <summary>
|
||||
/// Per-run sink for <c>ScriptTrace</c> audit events so a traced routed call can return them
|
||||
/// inline on <c>ScriptCallResult.TraceEvents</c> (Flow Debugger design 2026-09-05 §5.4).
|
||||
/// Lock-based: a run emits tens of events, not thousands. Beyond <see cref="DefaultMaxEvents"/>
|
||||
/// the collector keeps the first N, sets <see cref="Truncated"/>, and the audit rows written
|
||||
/// through <c>IAuditWriter</c> remain complete — Replay is never truncated, only the inline copy.
|
||||
/// An untraced run holds <c>null</c> rather than an inert collector; callers null-check.
|
||||
/// </summary>
|
||||
public sealed class FlowTraceCollector
|
||||
{
|
||||
/// <summary>Default inline cap per run (design §5.4 <c>MaxTraceEventsPerRun</c>).</summary>
|
||||
public const int DefaultMaxEvents = 500;
|
||||
|
||||
private readonly List<AuditEvent> _events = new();
|
||||
private readonly object _gate = new();
|
||||
private readonly int _max;
|
||||
|
||||
/// <summary>True once an <see cref="Add"/> was dropped because the cap was reached.</summary>
|
||||
public bool Truncated { get; private set; }
|
||||
|
||||
/// <summary>Creates a collector with the given inline cap (must be positive).</summary>
|
||||
public FlowTraceCollector(int maxEvents = DefaultMaxEvents)
|
||||
{
|
||||
if (maxEvents <= 0) throw new ArgumentOutOfRangeException(nameof(maxEvents), "must be positive");
|
||||
_max = maxEvents;
|
||||
}
|
||||
|
||||
/// <summary>Appends an event, or marks the collector truncated once the cap is reached.</summary>
|
||||
public void Add(AuditEvent evt)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(evt);
|
||||
lock (_gate)
|
||||
{
|
||||
if (_events.Count >= _max) { Truncated = true; return; }
|
||||
_events.Add(evt);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Current event count.</summary>
|
||||
public int Count { get { lock (_gate) return _events.Count; } }
|
||||
|
||||
/// <summary>Copies the collected events in emission order.</summary>
|
||||
public IReadOnlyList<AuditEvent> Snapshot()
|
||||
{
|
||||
lock (_gate) return _events.ToArray();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user