fix(logs): address PR review — redact workflowInput, improve fallback heuristic, add isPending guard

This commit is contained in:
Waleed Latif
2026-04-15 10:59:01 -07:00
parent 06aebaa6e1
commit ccf975146e
5 changed files with 24 additions and 5 deletions

View File

@@ -266,6 +266,8 @@ interface LogDetailsProps {
hasPrev?: boolean
/** Callback to retry a failed execution */
onRetryExecution?: () => void
/** Whether a retry is currently in progress */
isRetryPending?: boolean
}
/**
@@ -283,6 +285,7 @@ export const LogDetails = memo(function LogDetails({
hasNext = false,
hasPrev = false,
onRetryExecution,
isRetryPending = false,
}: LogDetailsProps) {
const [isExecutionSnapshotOpen, setIsExecutionSnapshotOpen] = useState(false)
const scrollAreaRef = useRef<HTMLDivElement>(null)
@@ -399,6 +402,7 @@ export const LogDetails = memo(function LogDetails({
variant='ghost'
className='!p-1'
onClick={() => onRetryExecution?.()}
disabled={isRetryPending}
aria-label='Retry execution'
>
<Redo className='h-[14px] w-[14px]' />

View File

@@ -24,6 +24,7 @@ interface LogRowContextMenuProps {
onClearAllFilters: () => void
onCancelExecution: () => void
onRetryExecution: () => void
isRetryPending?: boolean
isFilteredByThisWorkflow: boolean
hasActiveFilters: boolean
}
@@ -45,6 +46,7 @@ export const LogRowContextMenu = memo(function LogRowContextMenu({
onClearAllFilters,
onCancelExecution,
onRetryExecution,
isRetryPending = false,
isFilteredByThisWorkflow,
hasActiveFilters,
}: LogRowContextMenuProps) {
@@ -78,9 +80,9 @@ export const LogRowContextMenu = memo(function LogRowContextMenu({
>
{isRetryable && (
<>
<DropdownMenuItem onSelect={onRetryExecution}>
<DropdownMenuItem onSelect={onRetryExecution} disabled={isRetryPending}>
<Redo />
Retry
{isRetryPending ? 'Retrying...' : 'Retry'}
</DropdownMenuItem>
<DropdownMenuSeparator />
</>

View File

@@ -821,6 +821,7 @@ export default function Logs() {
hasNext={selectedLogIndex < sortedLogs.length - 1}
hasPrev={selectedLogIndex > 0}
onRetryExecution={handleRetrySidebarExecution}
isRetryPending={retryExecution.isPending}
/>
),
[
@@ -830,6 +831,7 @@ export default function Logs() {
handleNavigateNext,
handleNavigatePrev,
handleRetrySidebarExecution,
retryExecution.isPending,
selectedLogIndex,
sortedLogs.length,
]
@@ -1231,6 +1233,7 @@ export default function Logs() {
onOpenPreview={handleOpenPreview}
onCancelExecution={handleCancelExecution}
onRetryExecution={handleRetryExecution}
isRetryPending={retryExecution.isPending}
onToggleWorkflowFilter={handleToggleWorkflowFilter}
onClearAllFilters={handleClearAllFilters}
isFilteredByThisWorkflow={isFilteredByThisWorkflow}

View File

@@ -438,12 +438,19 @@ export function extractRetryInput(log: WorkflowLog): unknown | undefined {
}
const executionState = execData.executionState as
| { blockStates?: Record<string, { output?: unknown }> }
| {
blockStates?: Record<
string,
{ output?: unknown; executed?: boolean; executionTime?: number }
>
}
| undefined
if (!executionState?.blockStates) return undefined
// Starter/trigger blocks are pre-populated with executed: false and executionTime: 0,
// which distinguishes them from blocks that actually ran during execution.
for (const state of Object.values(executionState.blockStates)) {
if (state.output && typeof state.output === 'object' && 'input' in state.output) {
if (state.executed === false && state.executionTime === 0 && state.output != null) {
return state.output
}
}

View File

@@ -372,6 +372,9 @@ export class ExecutionLogger implements IExecutionLoggerService {
? Math.max(0, Math.round(rawDurationMs))
: 0
const redactedWorkflowInput =
workflowInput !== undefined ? redactApiKeys(filterForDisplay(workflowInput)) : undefined
const completedExecutionData = this.buildCompletedExecutionData({
existingExecutionData,
traceSpans: redactedTraceSpans,
@@ -380,7 +383,7 @@ export class ExecutionLogger implements IExecutionLoggerService {
completionFailure,
executionCost,
executionState,
workflowInput,
workflowInput: redactedWorkflowInput,
})
const [updatedLog] = await db