mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
improvement(logs): show Redacting status while PII masking runs (#5855)
* improvement(logs): show Redacting status while log-persist PII masking runs Log-stage PII redaction happens at persist time and can take minutes on large payloads, during which the Logs page showed the run as Running long after execution finished. The persist path now flips the log row to 'redacting' (guarded on 'running' so a concurrent cancellation is never clobbered) right before the masking work starts — only when the logs redaction stage is actually enabled — and the terminal update overwrites it with the final status. The Logs UI renders an amber non-filterable Redacting badge (row + details sidebar via the shared STATUS_CONFIG), keeps polling the detail query during the phase, and keeps resolving live progress markers. No migration: status is a free-text column, and the contract already types it as string. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A1JYstmLHk9qMGyBDqYRcJ * fix(logs): never let the cosmetic redacting write abort log finalization Review finding: the status flip was awaited without failure isolation, so a transient DB error there rejected applyPiiRedaction before masking and the terminal update never ran. The write is display-only; catch and warn instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A1JYstmLHk9qMGyBDqYRcJ --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
fad9728cbd
commit
b49fe16dae
@@ -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]
|
||||
)
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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<void>
|
||||
): Promise<RedactablePayload> {
|
||||
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),
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user