feat(flow-debugger): carry site-side trace truncation on the route reply (ScadaBridge#54)
ScriptCallResult and RouteToCallResponse gain trailing Truncated/LogsTruncated, filled from the site FlowTraceCollector after Complete(); RouteToCallResponseDto fields 8/9 carry them (sitecommand regen). FlowDebugRunner ORs them into FlowDebugRunResult on the script entry, and on the method entry through a new optional truncation sink on RouteHelper.WithTraceSink (carried through every With* clone). Previously a site run whose collector dropped rows came back as a silently short list. Older sites send false. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
@@ -86,6 +86,7 @@ public class RouteHelper
|
||||
private readonly INodeIdentityProvider? _nodeIdentity;
|
||||
private readonly bool _trace;
|
||||
private readonly Action<AuditEvent>? _traceSink;
|
||||
private readonly Action<bool, bool>? _siteTruncationSink;
|
||||
private readonly IReadOnlySet<string>? _permittedSiteIdentifiers;
|
||||
private readonly bool _captureBodies;
|
||||
private readonly ILogger? _logger;
|
||||
@@ -107,7 +108,7 @@ public class RouteHelper
|
||||
INodeIdentityProvider? nodeIdentity = null,
|
||||
ILogger<RouteHelper>? logger = null)
|
||||
: this(instanceLocator, instanceRouter, CancellationToken.None, CancellationToken.None, parentExecutionId: null,
|
||||
auditWriter, nodeIdentity, trace: false, traceSink: null, permittedSiteIdentifiers: null, captureBodies: true, logger)
|
||||
auditWriter, nodeIdentity, trace: false, traceSink: null, permittedSiteIdentifiers: null, captureBodies: true, logger, siteTruncationSink: null)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -123,7 +124,8 @@ public class RouteHelper
|
||||
Action<AuditEvent>? traceSink,
|
||||
IReadOnlySet<string>? permittedSiteIdentifiers,
|
||||
bool captureBodies,
|
||||
ILogger? logger)
|
||||
ILogger? logger,
|
||||
Action<bool, bool>? siteTruncationSink)
|
||||
{
|
||||
_permittedSiteIdentifiers = permittedSiteIdentifiers;
|
||||
_instanceLocator = instanceLocator;
|
||||
@@ -135,6 +137,7 @@ public class RouteHelper
|
||||
_nodeIdentity = nodeIdentity;
|
||||
_trace = trace;
|
||||
_traceSink = traceSink;
|
||||
_siteTruncationSink = siteTruncationSink;
|
||||
_captureBodies = captureBodies;
|
||||
_logger = logger;
|
||||
}
|
||||
@@ -150,7 +153,7 @@ public class RouteHelper
|
||||
/// <returns>A new <see cref="RouteHelper"/> inheriting the given deadline token.</returns>
|
||||
public RouteHelper WithDeadline(CancellationToken deadlineToken) =>
|
||||
new(_instanceLocator, _instanceRouter, deadlineToken, _requestAbortedToken, _parentExecutionId,
|
||||
_auditWriter, _nodeIdentity, _trace, _traceSink, _permittedSiteIdentifiers, _captureBodies, _logger);
|
||||
_auditWriter, _nodeIdentity, _trace, _traceSink, _permittedSiteIdentifiers, _captureBodies, _logger, _siteTruncationSink);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="RouteHelper"/> carrying the raw request-abort
|
||||
@@ -164,7 +167,7 @@ public class RouteHelper
|
||||
/// <returns>A new <see cref="RouteHelper"/> carrying the given request-abort token.</returns>
|
||||
public RouteHelper WithRequestAborted(CancellationToken requestAbortedToken) =>
|
||||
new(_instanceLocator, _instanceRouter, _deadlineToken, requestAbortedToken, _parentExecutionId,
|
||||
_auditWriter, _nodeIdentity, _trace, _traceSink, _permittedSiteIdentifiers, _captureBodies, _logger);
|
||||
_auditWriter, _nodeIdentity, _trace, _traceSink, _permittedSiteIdentifiers, _captureBodies, _logger, _siteTruncationSink);
|
||||
|
||||
/// <summary>
|
||||
/// Audit Log (ParentExecutionId): returns a <see cref="RouteHelper"/> whose
|
||||
@@ -179,7 +182,7 @@ public class RouteHelper
|
||||
/// <returns>A new <see cref="RouteHelper"/> carrying the given parent execution id.</returns>
|
||||
public RouteHelper WithParentExecutionId(Guid? parentExecutionId) =>
|
||||
new(_instanceLocator, _instanceRouter, _deadlineToken, _requestAbortedToken, parentExecutionId,
|
||||
_auditWriter, _nodeIdentity, _trace, _traceSink, _permittedSiteIdentifiers, _captureBodies, _logger);
|
||||
_auditWriter, _nodeIdentity, _trace, _traceSink, _permittedSiteIdentifiers, _captureBodies, _logger, _siteTruncationSink);
|
||||
|
||||
/// <summary>
|
||||
/// Flow Debugger (design 2026-09-05 §5.2): returns a <see cref="RouteHelper"/> whose routed
|
||||
@@ -190,7 +193,7 @@ public class RouteHelper
|
||||
/// <returns>A new <see cref="RouteHelper"/> carrying the flag.</returns>
|
||||
public RouteHelper WithTrace(bool trace) =>
|
||||
new(_instanceLocator, _instanceRouter, _deadlineToken, _requestAbortedToken, _parentExecutionId,
|
||||
_auditWriter, _nodeIdentity, trace, _traceSink, _permittedSiteIdentifiers, _captureBodies, _logger);
|
||||
_auditWriter, _nodeIdentity, trace, _traceSink, _permittedSiteIdentifiers, _captureBodies, _logger, _siteTruncationSink);
|
||||
|
||||
/// <summary>
|
||||
/// Flow Debugger (design 2026-09-05 §7.1): returns a <see cref="RouteHelper"/> that also
|
||||
@@ -199,10 +202,15 @@ public class RouteHelper
|
||||
/// audit store. Null clears the sink.
|
||||
/// </summary>
|
||||
/// <param name="traceSink">Receives the audit events in emission order; null = none.</param>
|
||||
/// <param name="siteTruncationSink">
|
||||
/// Gitea #54: receives <c>(truncated, logsTruncated)</c> from a routed reply whose SITE-side
|
||||
/// inline collector dropped rows, so the caller can label a short list as short. Called only
|
||||
/// when at least one flag is set; null = none.
|
||||
/// </param>
|
||||
/// <returns>A new <see cref="RouteHelper"/> carrying the sink.</returns>
|
||||
public RouteHelper WithTraceSink(Action<AuditEvent>? traceSink) =>
|
||||
public RouteHelper WithTraceSink(Action<AuditEvent>? traceSink, Action<bool, bool>? siteTruncationSink = null) =>
|
||||
new(_instanceLocator, _instanceRouter, _deadlineToken, _requestAbortedToken, _parentExecutionId,
|
||||
_auditWriter, _nodeIdentity, _trace, traceSink, _permittedSiteIdentifiers, _captureBodies, _logger);
|
||||
_auditWriter, _nodeIdentity, _trace, traceSink, _permittedSiteIdentifiers, _captureBodies, _logger, siteTruncationSink);
|
||||
|
||||
/// <summary>
|
||||
/// Flow Debugger outbound calls (plan 2026-09-06, D5/D7): the trace sink this helper carries,
|
||||
@@ -236,7 +244,7 @@ public class RouteHelper
|
||||
/// <returns>A new <see cref="RouteHelper"/> carrying the scope.</returns>
|
||||
public RouteHelper WithSiteScope(IReadOnlySet<string>? permittedSiteIdentifiers) =>
|
||||
new(_instanceLocator, _instanceRouter, _deadlineToken, _requestAbortedToken, _parentExecutionId,
|
||||
_auditWriter, _nodeIdentity, _trace, _traceSink, permittedSiteIdentifiers, _captureBodies, _logger);
|
||||
_auditWriter, _nodeIdentity, _trace, _traceSink, permittedSiteIdentifiers, _captureBodies, _logger, _siteTruncationSink);
|
||||
|
||||
/// <summary>
|
||||
/// Arch-review F11: returns a <see cref="RouteHelper"/> whose always-on <c>RouteCall</c> row
|
||||
@@ -251,7 +259,7 @@ public class RouteHelper
|
||||
/// <returns>A new <see cref="RouteHelper"/> carrying the flag.</returns>
|
||||
public RouteHelper WithBodyCapture(bool captureBodies) =>
|
||||
new(_instanceLocator, _instanceRouter, _deadlineToken, _requestAbortedToken, _parentExecutionId,
|
||||
_auditWriter, _nodeIdentity, _trace, _traceSink, _permittedSiteIdentifiers, captureBodies, _logger);
|
||||
_auditWriter, _nodeIdentity, _trace, _traceSink, _permittedSiteIdentifiers, captureBodies, _logger, _siteTruncationSink);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a route target for the specified instance.
|
||||
@@ -262,7 +270,7 @@ public class RouteHelper
|
||||
{
|
||||
return new RouteTarget(
|
||||
instanceCode, _instanceLocator, _instanceRouter, _deadlineToken, _requestAbortedToken, _parentExecutionId,
|
||||
_auditWriter, _nodeIdentity, _trace, _traceSink, _permittedSiteIdentifiers, _captureBodies, _logger);
|
||||
_auditWriter, _nodeIdentity, _trace, _traceSink, _permittedSiteIdentifiers, _captureBodies, _logger, _siteTruncationSink);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,6 +289,7 @@ public class RouteTarget
|
||||
private readonly INodeIdentityProvider? _nodeIdentity;
|
||||
private readonly bool _trace;
|
||||
private readonly Action<AuditEvent>? _traceSink;
|
||||
private readonly Action<bool, bool>? _siteTruncationSink;
|
||||
private readonly IReadOnlySet<string>? _permittedSiteIdentifiers;
|
||||
private readonly bool _captureBodies;
|
||||
private readonly ILogger? _logger;
|
||||
@@ -301,6 +310,7 @@ public class RouteTarget
|
||||
/// <param name="permittedSiteIdentifiers">Arch-review F2: site identifiers this call may reach; null = unscoped.</param>
|
||||
/// <param name="captureBodies">Arch-review F11: false omits the request/response summary on the <c>RouteCall</c> row.</param>
|
||||
/// <param name="logger">Arch-review F8: logs a Warning when the best-effort <c>RouteCall</c> audit write faults; null = silent.</param>
|
||||
/// <param name="siteTruncationSink">Gitea #54: receives a routed reply's site-side truncation flags; null = none.</param>
|
||||
internal RouteTarget(
|
||||
string instanceCode,
|
||||
IInstanceLocator instanceLocator,
|
||||
@@ -314,7 +324,8 @@ public class RouteTarget
|
||||
Action<AuditEvent>? traceSink = null,
|
||||
IReadOnlySet<string>? permittedSiteIdentifiers = null,
|
||||
bool captureBodies = true,
|
||||
ILogger? logger = null)
|
||||
ILogger? logger = null,
|
||||
Action<bool, bool>? siteTruncationSink = null)
|
||||
{
|
||||
_permittedSiteIdentifiers = permittedSiteIdentifiers;
|
||||
_instanceCode = instanceCode;
|
||||
@@ -327,6 +338,7 @@ public class RouteTarget
|
||||
_nodeIdentity = nodeIdentity;
|
||||
_trace = trace;
|
||||
_traceSink = traceSink;
|
||||
_siteTruncationSink = siteTruncationSink;
|
||||
_captureBodies = captureBodies;
|
||||
_logger = logger;
|
||||
}
|
||||
@@ -382,6 +394,7 @@ public class RouteTarget
|
||||
|
||||
EmitRouteCall(scriptName, normalized, startedUtc, sw.ElapsedMilliseconds, response.Success,
|
||||
response.ReturnValue, response.ErrorMessage, response.ChildExecutionId, response.TraceEvents);
|
||||
ReportSiteTruncation(response);
|
||||
|
||||
if (!response.Success)
|
||||
{
|
||||
@@ -463,6 +476,23 @@ public class RouteTarget
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gitea #54: forwards a routed reply's site-side truncation flags to the sink. Best-effort —
|
||||
/// a misbehaving sink never fails the routed call.
|
||||
/// </summary>
|
||||
private void ReportSiteTruncation(RouteToCallResponse response)
|
||||
{
|
||||
if (_siteTruncationSink is null || !(response.Truncated || response.LogsTruncated)) return;
|
||||
try
|
||||
{
|
||||
_siteTruncationSink(response.Truncated, response.LogsTruncated);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Best-effort, like the trace sink.
|
||||
}
|
||||
}
|
||||
|
||||
private static string? TraceJson(object? value)
|
||||
{
|
||||
if (value is null) return null;
|
||||
|
||||
Reference in New Issue
Block a user