Skip to content

feat: implement execution logging for workflows#77

Merged
Vamsi-o merged 1 commit into
mainfrom
execution-logs
Apr 16, 2026
Merged

feat: implement execution logging for workflows#77
Vamsi-o merged 1 commit into
mainfrom
execution-logs

Conversation

@Teja-Budumuru

@Teja-Budumuru Teja-Budumuru commented Apr 16, 2026

Copy link
Copy Markdown
Contributor
  • Added execution logging functionality to track workflow and node executions.
  • Created new API endpoint to fetch execution logs for a specific workflow.
  • Introduced ExecutionHistoryFooter component to display execution history in the UI.
  • Enhanced error handling and formatting for better user experience.
  • Updated Redux slice to manage execution state and pagination.
  • Added utility functions for formatting dates and statuses.
  • Integrated execution logs into the existing workflow management system.

Summary by CodeRabbit

Release Notes

New Features

  • Added Execution History footer panel displaying workflow run status, timing, and error details with manual and automatic refresh controls
  • Implemented execution log tracking showing individual node execution results and performance metrics
  • Enhanced error reporting with formatted messages, severity classification, and detailed execution context for faster debugging

- Added execution logging functionality to track workflow and node executions.
- Created new API endpoint to fetch execution logs for a specific workflow.
- Introduced ExecutionHistoryFooter component to display execution history in the UI.
- Enhanced error handling and formatting for better user experience.
- Updated Redux slice to manage execution state and pagination.
- Added utility functions for formatting dates and statuses.
- Integrated execution logs into the existing workflow management system.
Copilot AI review requested due to automatic review settings April 16, 2026 08:37
@Teja-Budumuru Teja-Budumuru requested a review from Vamsi-o as a code owner April 16, 2026 08:37
@coderabbitai

coderabbitai Bot commented Apr 16, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 17eb31f0-090a-4db8-930c-ee98fdd3154b

📥 Commits

Reviewing files that changed from the base of the PR and between b511115 and f586ecd.

📒 Files selected for processing (12)
  • apps/http-backend/src/routes/userRoutes/executionRoutes.ts
  • apps/http-backend/src/routes/userRoutes/userRoutes.ts
  • apps/web/app/components/ExecutionHistoryFooter.tsx
  • apps/web/app/lib/api.ts
  • apps/web/app/lib/errorHelpers.ts
  • apps/web/app/lib/formatters.ts
  • apps/web/app/types/execution.types.ts
  • apps/web/app/workflows/[id]/page.tsx
  • apps/web/store/root-reducer.ts
  • apps/web/store/slices/executionSlice.ts
  • packages/common/src/index.ts
  • packages/db/prisma/schema.prisma

📝 Walkthrough

Walkthrough

This PR introduces execution history tracking across the workflow system. The backend now wraps execution flows in database transactions to record workflow and node execution states with timestamps and metadata. The frontend adds a persistent footer component that displays execution history with auto-polling, manual refresh, and detailed execution details including errors and node execution counts.

Changes

Cohort / File(s) Summary
Backend Execution Recording
apps/http-backend/src/routes/userRoutes/executionRoutes.ts, apps/http-backend/src/routes/userRoutes/userRoutes.ts, packages/db/prisma/schema.prisma
Wrapped execution flow in Prisma transaction to create/update workflowExecution and nodeExecution records with statuses, timestamps, input/output data, and errors. Added new GET /workflow/logs/:workflowId endpoint to retrieve execution history. Added isTest boolean field to NodeExecution model.
Frontend Execution Footer UI
apps/web/app/components/ExecutionHistoryFooter.tsx, apps/web/app/workflows/[id]/page.tsx
Created new ExecutionHistoryFooter component with fixed bottom taskbar displaying latest execution status, manual refresh, auto-polling toggle with configurable interval, and expandable execution details panel. Integrated footer into workflow canvas layout.
API Integration Layer
apps/web/app/lib/api.ts
Added api.executions.getWorkflowLogs() method to fetch workflow execution history with pagination support (skip/take parameters).
Execution Types & Schemas
apps/web/app/types/execution.types.ts, packages/common/src/index.ts
Defined TypeScript interfaces and Zod validation schemas for Status, NodeExecutionLog, WorkflowExecutionLog, and WorkflowExecutionResponse types with required/optional fields and defaults.
Frontend Utilities
apps/web/app/lib/formatters.ts, apps/web/app/lib/errorHelpers.ts
Added utility functions for formatting dates, durations, status labels/colors/icons, JSON parsing, and error extraction/severity classification with fallback handling.
Redux State Management
apps/web/store/slices/executionSlice.ts, apps/web/store/root-reducer.ts
Created new execution slice managing execution list, selection, loading state, footer expansion (with localStorage persistence), and pagination. Integrated slice into root reducer and exported ExecutionState type.

Sequence Diagram

sequenceDiagram
    actor User
    participant Frontend as Browser / Frontend
    participant API as Backend API
    participant DB as Database
    
    User->>Frontend: Trigger Workflow Execution
    Frontend->>API: POST /execute (execution request)
    API->>DB: BEGIN TRANSACTION
    API->>DB: CREATE workflowExecution (status: Start)
    API->>DB: CREATE nodeExecution (status: Start)
    API->>API: Execute Node Logic
    alt Execution Success
        API->>DB: UPDATE nodeExecution (status: Completed, outputData)
        API->>DB: UPDATE workflowExecution (status: Completed)
        API->>DB: COMMIT
        API-->>Frontend: 202 Accepted (execution result)
    else Execution Failed
        API->>DB: UPDATE nodeExecution (status: Failed, error)
        API->>DB: UPDATE workflowExecution (status: Failed)
        API->>DB: COMMIT
        API-->>Frontend: 202 Accepted (with error)
    end
    
    Frontend->>Frontend: Auto-polling Enabled
    loop Every autoRefreshInterval
        Frontend->>API: GET /workflow/logs/:workflowId (skip, take)
        API->>DB: Query workflowExecution + nodeExecutions
        DB-->>API: Execution history records
        API-->>Frontend: { data: WorkflowExecutionLog[] }
        Frontend->>Frontend: Update execution history state
        Frontend->>Frontend: Render execution footer
    end
    
    User->>Frontend: Click Execution Row
    Frontend->>Frontend: Select & Display Execution Details
    Frontend->>User: Show status, timestamps, errors, node count
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Dev-Pross/BuildFlow#19 — Modifies workflowExecution and nodeExecution record creation/updates in similar execution flow patterns.
  • Dev-Pross/BuildFlow#47 — Implements transactional pattern for workflowExecution creation and status updates around node execution.
  • Dev-Pross/BuildFlow#26 — Modifies the same userRoutes.ts file and packages/common/src/index.ts schemas for shared validation.

Suggested reviewers

  • Vamsi-o

Poem

🐰 Hops of joy through history's trails,
Executions logged, no more failed tales,
Transactions safe, polling flows smooth,
Footer reveals what workflows can prove,
Status and errors, all tracked with grace! ✨📜

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch execution-logs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Vamsi-o Vamsi-o merged commit fc965fe into main Apr 16, 2026
3 of 4 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements workflow/node execution logging end-to-end (DB + backend endpoint + shared schemas + web UI component) to surface execution history for a workflow in the web app.

Changes:

  • Adds isTest to NodeExecution and introduces shared Zod schemas for execution log responses.
  • Adds backend endpoint to fetch workflow execution logs and updates node test-execution route to persist executions.
  • Adds web UI footer component for execution history plus Redux slice/types/utilities for formatting and errors.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
packages/db/prisma/schema.prisma Adds isTest flag to NodeExecution records.
packages/common/src/index.ts Adds Zod schemas/enums for execution-log API responses.
apps/web/store/slices/executionSlice.ts Introduces Redux state shape/actions for execution history + pagination.
apps/web/store/root-reducer.ts Registers execution reducer and re-exports ExecutionState.
apps/web/app/workflows/[id]/page.tsx Mounts ExecutionHistoryFooter on the workflow canvas page.
apps/web/app/types/execution.types.ts Adds TS types for workflow/node execution logs.
apps/web/app/lib/formatters.ts Adds formatting helpers for date/duration/status/json.
apps/web/app/lib/errorHelpers.ts Adds error formatting and severity helpers for UI display.
apps/web/app/lib/api.ts Adds api.executions.getWorkflowLogs() client helper.
apps/web/app/components/ExecutionHistoryFooter.tsx Adds execution history footer UI with polling + details panel.
apps/http-backend/src/routes/userRoutes/userRoutes.ts Adds GET /workflow/logs/:workflowId endpoint.
apps/http-backend/src/routes/userRoutes/executionRoutes.ts Updates node test execution endpoint to write workflow/node execution logs.

Comment on lines +51 to +96
setIsFooterExpanded: (state, action: PayloadAction<boolean>) => {
state.isFooterExpanded = action.payload;
// Persist to localStorage
if (typeof window !== "undefined") {
localStorage.setItem(
"executionFooterExpanded",
JSON.stringify(action.payload)
);
}
},

// Set loading state
setIsLoading: (state, action: PayloadAction<boolean>) => {
state.isLoading = action.payload;
},

// Set error
setError: (state, action: PayloadAction<string | null>) => {
state.error = action.payload;
},

// Set if more data available
setHasMore: (state, action: PayloadAction<boolean>) => {
state.hasMore = action.payload;
},

// Reset pagination
resetPagination: (state) => {
state.skip = 0;
state.executions = [];
},

// Increment skip by take amount
incrementSkip: (state) => {
state.skip += state.take;
},

// Initialize from localStorage
initializeFromStorage: (state) => {
if (typeof window !== "undefined") {
const expanded = localStorage.getItem("executionFooterExpanded");
if (expanded !== null) {
state.isFooterExpanded = JSON.parse(expanded);
}
}
},

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reducers should be pure; reading/writing localStorage inside slice reducers introduces side effects and can break time-travel/debugging and SSR assumptions. Move the persistence logic to a thunk/middleware (e.g., listener middleware) or to a component effect that dispatches setIsFooterExpanded after reading from storage.

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +68
success: boolean;
data: WorkflowExecutionLog | null;
message?: string;

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WorkflowExecutionResponse currently models success and a single WorkflowExecutionLog | null, but the new backend endpoint returns { message, data: WorkflowExecution[] } (an array) and does not include a success boolean. Update this type (and any callers) to match the actual response shape to avoid incorrect assumptions at compile time.

Suggested change
success: boolean;
data: WorkflowExecutionLog | null;
message?: string;
message: string;
data: WorkflowExecutionLog[];

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +18
// If error starts with a backtick, it's likely JSON or code, don't modify
if (message.startsWith('`')) {

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatErrorMessage slices off the first and last character whenever the message starts with a backtick, but it doesn’t verify there is a trailing backtick. For inputs like "foo" or "" this will drop valid characters or return an empty string unexpectedly. Only strip backticks when the string both startsWith and endsWith a backtick (and has length >= 2), otherwise return the message unchanged.

Suggested change
// If error starts with a backtick, it's likely JSON or code, don't modify
if (message.startsWith('`')) {
// If error is wrapped in backticks, preserve the inner content without the wrappers
if (message.length >= 2 && message.startsWith('`') && message.endsWith('`')) {

Copilot uses AI. Check for mistakes.
Comment on lines +39 to +84
const result = await prismaClient.$transaction(async(tx)=>{
// // console.log(`Execution context: ${JSON.stringify(context)}`)
const workflowExecution = await tx.workflowExecution.create({
data:{
workflowId: nodeData.workflowId || "",
status: "Start",
startAt: new Date(),
metadata:{"isTesting": true},
}
})
const NodeExecution = await tx.nodeExecution.create({
data:{
status: "Start",
nodeId: nodeData.id,
workflowExecId: workflowExecution.id,
startedAt: new Date(),
inputData: context,
isTest: true
}
})
const executionResult = await ExecutionRegister.execute(type, context)


// console.log(`Execution result: ${executionResult}`)

if(executionResult.success){
await tx.nodeExecution.update({
where: { id: NodeExecution.id},
data:{
status: "Completed",
outputData: executionResult.output,
completedAt: new Date()
}
})
await tx.workflowExecution.update({
where: {id: workflowExecution.id},
data: {
status: "Completed",
completedAt: new Date()
}
})
return res.status(statusCodes.ACCEPTED).json({
message: `${nodeData.name} node execution done` ,
data: executionResult
})
}

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The route sends an HTTP response from inside the Prisma $transaction callback, then execution continues after the transaction and a second response is sent (the 403 at the end). This will trigger “headers already sent” and can leave the transaction in an unclear state. Refactor to have the transaction return data/status (or throw), then send exactly one res.status(...).json(...) after the transaction resolves.

Copilot uses AI. Check for mistakes.
data:{
status: "Failed",
completedAt: new Date(),
error: executionResult.output

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On failure, workflowExecution.error is being set to executionResult.output, but WorkflowExecution.error is a String in Prisma. If output is an object (common), this will throw at runtime and also loses the real error message. Use executionResult.error (and/or serialize output into metadata) when updating workflowExecution on failure.

Suggested change
error: executionResult.output
error: typeof executionResult.error === "string"
? executionResult.error
: JSON.stringify(executionResult.error ?? executionResult.output)

Copilot uses AI. Check for mistakes.
Comment on lines +742 to +746
const executions = await prismaClient.workflowExecution.findMany({
where: { workflowId: workflowId },
include: {
nodeExecutions: { include: { node: true } },
}

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow execution logs endpoint does not verify that the requested workflow belongs to the authenticated user. As written, any logged-in user can fetch logs for any workflowId if they can guess/obtain it. Add an ownership constraint (e.g., where: { workflowId, workflow: { userId: req.user.sub } }) or first load the workflow by id+userId and then query executions.

Copilot uses AI. Check for mistakes.
Comment on lines +742 to +755
const executions = await prismaClient.workflowExecution.findMany({
where: { workflowId: workflowId },
include: {
nodeExecutions: { include: { node: true } },
}
})

if (!executions) {
return res.status(statusCodes.NOT_FOUND).json({
message: `logs not found for ${workflowId}`
})
}

return res.status(statusCodes.ACCEPTED).json({

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This endpoint ignores the skip/take query params the frontend is already sending, and returns 202 (ACCEPTED) for a synchronous GET. This can lead to unbounded result sizes and slow responses as executions grow. Parse skip/take from req.query, add an orderBy (e.g., startAt desc), and return 200 (OK); also note that findMany() returns [] (never null), so if (!executions) won’t detect “no logs”.

Suggested change
const executions = await prismaClient.workflowExecution.findMany({
where: { workflowId: workflowId },
include: {
nodeExecutions: { include: { node: true } },
}
})
if (!executions) {
return res.status(statusCodes.NOT_FOUND).json({
message: `logs not found for ${workflowId}`
})
}
return res.status(statusCodes.ACCEPTED).json({
const skip =
req.query.skip !== undefined ? Number.parseInt(String(req.query.skip), 10) : 0;
const take =
req.query.take !== undefined ? Number.parseInt(String(req.query.take), 10) : 50;
if (
Number.isNaN(skip) ||
Number.isNaN(take) ||
skip < 0 ||
take < 0
) {
return res.status(statusCodes.BAD_REQUEST).json({
message: "Invalid pagination params"
});
}
const executions = await prismaClient.workflowExecution.findMany({
where: { workflowId: workflowId },
orderBy: { startAt: "desc" },
skip,
take,
include: {
nodeExecutions: { include: { node: true } },
}
})
if (executions.length === 0) {
return res.status(statusCodes.NOT_FOUND).json({
message: `logs not found for ${workflowId}`
})
}
return res.status(statusCodes.OK).json({

Copilot uses AI. Check for mistakes.
Comment on lines +92 to +131
// Load initial execution history
useEffect(() => {
fetchExecutionLogs();

// Initialize localStorage state
const savedExpandedState = localStorage.getItem('executionFooterExpanded');
if (savedExpandedState !== null) {
setIsExpanded(JSON.parse(savedExpandedState));
}
}, [workflowId]);

// Setup auto-refresh polling
useEffect(() => {
if (!autoRefreshEnabled) {
if (refreshIntervalRef.current) {
clearInterval(refreshIntervalRef.current);
refreshIntervalRef.current = null;
}
return;
}

// Initial fetch immediately
fetchExecutionLogs();

// Setup polling interval
refreshIntervalRef.current = setInterval(() => {
const now = Date.now();
// Only fetch if enough time has passed since last fetch
if (now - lastFetchRef.current >= autoRefreshInterval) {
fetchExecutionLogs();
}
}, autoRefreshInterval);

return () => {
if (refreshIntervalRef.current) {
clearInterval(refreshIntervalRef.current);
refreshIntervalRef.current = null;
}
};
}, [autoRefreshEnabled, autoRefreshInterval]);

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The polling useEffect doesn’t include workflowId (or a stable fetchExecutionLogs callback) in its dependency list, so when workflowId changes the interval keeps calling the stale closure and will continue fetching the old workflow’s logs. Also, fetchExecutionLogs is called in both the “initial load” effect and again in the polling effect, causing duplicate fetches on mount. Make fetchExecutionLogs stable (useCallback) and include workflowId in deps, and ensure only one initial fetch runs.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants