feat(cli): flow-debug logs column and --show-logs
This commit is contained in:
@@ -37,8 +37,14 @@ internal static class FlowDebugTableProjection
|
||||
/// renderer's own fallbacks still apply.
|
||||
/// </summary>
|
||||
/// <param name="json">The raw success JSON body from the management API.</param>
|
||||
/// <param name="showLogs">
|
||||
/// When true, prints each step's <c>ScriptLog</c> lines and (on a failed step) the first
|
||||
/// three lines of its exception detail as additional indented <c>note</c> rows immediately
|
||||
/// following that step's row (step-logs design 2026-09-09, CLI Task 14). The <c>logs</c>
|
||||
/// column (a count of attached log lines) is always present, independent of this flag.
|
||||
/// </param>
|
||||
/// <returns>Compact JSON suitable for table rendering.</returns>
|
||||
internal static string ProjectEvents(string json)
|
||||
internal static string ProjectEvents(string json, bool showLogs = false)
|
||||
{
|
||||
JsonDocument doc;
|
||||
try
|
||||
@@ -62,7 +68,7 @@ internal static class FlowDebugTableProjection
|
||||
|
||||
var truncated = TryGetPropertyCI(root, "truncated", out var t) && t.ValueKind == JsonValueKind.True;
|
||||
|
||||
var rows = ProjectSteps(events, truncated);
|
||||
var rows = ProjectSteps(events, truncated, showLogs);
|
||||
if (rows == null)
|
||||
{
|
||||
// The event array did not deserialize as canonical audit events (a future wire
|
||||
@@ -103,7 +109,7 @@ internal static class FlowDebugTableProjection
|
||||
/// Builds the flow graph from the canonical events and projects one row per step. Returns
|
||||
/// null when the array is not a list of canonical audit events.
|
||||
/// </summary>
|
||||
private static JsonArray? ProjectSteps(JsonElement events, bool truncated)
|
||||
private static JsonArray? ProjectSteps(JsonElement events, bool truncated, bool showLogs)
|
||||
{
|
||||
List<AuditEvent>? list;
|
||||
try
|
||||
@@ -148,12 +154,47 @@ internal static class FlowDebugTableProjection
|
||||
["lifecycle"] = lifecycle is { Count: > 0 } ? $"{lifecycle.Count} {(lifecycle.Count == 1 ? "row" : "rows")}" : null,
|
||||
["correlation"] = step.CorrelationId?.ToString("D"),
|
||||
["error"] = step.ErrorMessage,
|
||||
["logs"] = step.Logs?.Count ?? 0,
|
||||
});
|
||||
|
||||
if (!showLogs)
|
||||
continue;
|
||||
|
||||
if (step.Logs is { Count: > 0 } logs)
|
||||
{
|
||||
foreach (var line in logs)
|
||||
rows.Add(new JsonObject { ["note"] = JsonValue.Create(FormatLogLine(line)) });
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(step.ErrorDetail))
|
||||
{
|
||||
foreach (var line in FormatErrorDetailLines(step.ErrorDetail))
|
||||
rows.Add(new JsonObject { ["note"] = JsonValue.Create(line) });
|
||||
}
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
/// <summary>Formats one <see cref="FlowLogLine"/> as <c> HH:mm:ss.fff LEVEL source message</c> (UTC, level upper-cased and padded to 5 chars).</summary>
|
||||
private static string FormatLogLine(FlowLogLine line)
|
||||
{
|
||||
var time = line.OccurredAtUtc.UtcDateTime.ToString("HH:mm:ss.fff", CultureInfo.InvariantCulture);
|
||||
var level = (line.Level ?? "info").ToUpperInvariant().PadRight(5);
|
||||
return $" {time} {level} {line.Source} {line.Message}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats the first three lines of an exception's <c>ToString()</c> detail — the first
|
||||
/// prefixed <c> exception: </c>, the rest indented four spaces.
|
||||
/// </summary>
|
||||
private static IEnumerable<string> FormatErrorDetailLines(string errorDetail)
|
||||
{
|
||||
var lines = errorDetail.Replace("\r\n", "\n").Split('\n');
|
||||
for (var i = 0; i < Math.Min(3, lines.Length); i++)
|
||||
yield return i == 0 ? $" exception: {lines[i]}" : $" {lines[i]}";
|
||||
}
|
||||
|
||||
/// <summary>Projects one canonical audit event (camelCase JSON) to its compact row (fallback path).</summary>
|
||||
private static JsonNode ProjectEvent(JsonElement evt)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user