diff --git a/apps/sim/app/workspace/[workspaceId]/logs/logs.tsx b/apps/sim/app/workspace/[workspaceId]/logs/logs.tsx index 9274a26e23a..269993d45bd 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/logs.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/logs.tsx @@ -302,7 +302,9 @@ export default function Logs() { (query: { state: { data?: WorkflowLogDetail } }) => { if (!isLive) return false const status = query.state.data?.status - return status === 'running' || status === 'pending' ? ACTIVE_RUN_DETAIL_REFRESH_MS : false + return status === 'running' || status === 'pending' || status === 'redacting' + ? ACTIVE_RUN_DETAIL_REFRESH_MS + : false }, [isLive] ) diff --git a/apps/sim/app/workspace/[workspaceId]/logs/utils.ts b/apps/sim/app/workspace/[workspaceId]/logs/utils.ts index 6a28af7d077..dc1fecf2ab3 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/utils.ts +++ b/apps/sim/app/workspace/[workspaceId]/logs/utils.ts @@ -18,7 +18,14 @@ export const LOG_COLUMNS = { export const DELETED_WORKFLOW_LABEL = 'Deleted Workflow' -export type LogStatus = 'error' | 'pending' | 'running' | 'info' | 'cancelled' | 'cancelling' +export type LogStatus = + | 'error' + | 'pending' + | 'running' + | 'redacting' + | 'info' + | 'cancelled' + | 'cancelling' /** * Maps raw status string to LogStatus for display. @@ -29,6 +36,8 @@ export function getDisplayStatus(status: string | null | undefined): LogStatus { switch (status) { case 'running': return 'running' + case 'redacting': + return 'redacting' case 'pending': return 'pending' case 'cancelling': @@ -55,6 +64,7 @@ export const STATUS_CONFIG: Record< error: { variant: 'red', label: 'Error', color: 'var(--text-error)', filterable: true }, pending: { variant: 'amber', label: 'Pending', color: '#f59e0b', filterable: true }, running: { variant: 'amber', label: 'Running', color: '#f59e0b', filterable: true }, + redacting: { variant: 'amber', label: 'Redacting', color: '#f59e0b', filterable: false }, cancelling: { variant: 'amber', label: 'Cancelling...', color: '#f59e0b', filterable: false }, cancelled: { variant: 'orange', label: 'Cancelled', color: '#f97316', filterable: true }, info: { diff --git a/apps/sim/lib/logs/execution/logger.ts b/apps/sim/lib/logs/execution/logger.ts index 0f6c642e457..c4100005ec7 100644 --- a/apps/sim/lib/logs/execution/logger.ts +++ b/apps/sim/lib/logs/execution/logger.ts @@ -645,7 +645,8 @@ export class ExecutionLogger implements IExecutionLoggerService { private async applyPiiRedaction( workspaceId: string | null, payload: RedactablePayload, - storeContext: { workflowId?: string | null; executionId: string; userId?: string | null } + storeContext: { workflowId?: string | null; executionId: string; userId?: string | null }, + onRedactionStart?: () => Promise ): Promise { if (!workspaceId) return payload @@ -666,6 +667,10 @@ export class ExecutionLogger implements IExecutionLoggerService { const config = resolveEffectivePiiRedaction({ orgSettings: row.orgSettings, workspaceId }).logs if (!config.enabled) return payload + // Masking large payloads can take a while; let the caller surface the phase + // (e.g. flip the log row to 'redacting') before the slow work starts. + await onRedactionStart?.() + // The string redactor can't reach values already offloaded to large-value // storage (>8MB refs). Always hydrate → mask → re-store them under the LOGS // policy, even if the block-output stage already masked before offload: that @@ -867,6 +872,29 @@ export class ExecutionLogger implements IExecutionLoggerService { workflowId: existingLog?.workflowId ?? null, executionId, userId: actorUserId, + }, + async () => { + // Execution is done but the log payload is still being masked — surface + // that as 'redacting' so the Logs UI doesn't show a stale 'running'. + // Guarded on 'running' so a concurrent cancellation is never clobbered; + // the terminal update below overwrites with the final status either way. + // Purely cosmetic: a failed write must never abort masking/finalization. + try { + await db + .update(workflowExecutionLogs) + .set({ status: 'redacting' }) + .where( + and( + eq(workflowExecutionLogs.executionId, executionId), + eq(workflowExecutionLogs.status, 'running') + ) + ) + } catch (error) { + logger.warn('Failed to set redacting status on execution log', { + executionId, + error: getErrorMessage(error), + }) + } } ) diff --git a/apps/sim/lib/logs/fetch-log-detail.ts b/apps/sim/lib/logs/fetch-log-detail.ts index 5b38171d6b6..8a4b329a328 100644 --- a/apps/sim/lib/logs/fetch-log-detail.ts +++ b/apps/sim/lib/logs/fetch-log-detail.ts @@ -176,7 +176,7 @@ export async function fetchLogDetail({ ) const liveMarkers = - log.status === 'running' || log.status === 'pending' + log.status === 'running' || log.status === 'pending' || log.status === 'redacting' ? ((await getProgressMarkers(log.executionId)) ?? {}) : {} const rowMarkers = (executionData ?? {}) as ExecutionProgressMarkers