feat(logging): standardize logging across manual, scheduled, and api executions. persist manual executions to db

This commit is contained in:
Waleed Latif
2025-02-28 10:32:23 -08:00
parent f97cfddcc1
commit 87bb34e0dd
3 changed files with 108 additions and 4 deletions
+6 -4
View File
@@ -175,6 +175,8 @@ export async function GET(req: NextRequest) {
cronExpression: schedule.cronExpression,
})
const executionId = uuidv4()
try {
// Skip if this workflow is already running
if (runningExecutions.has(schedule.workflowId)) {
@@ -276,7 +278,6 @@ export async function GET(req: NextRequest) {
// Serialize and execute the workflow
const serializedWorkflow = new Serializer().serializeWorkflow(mergedStates, edges, loops)
const executor = new Executor(serializedWorkflow, currentBlockStates, decryptedEnvVars)
const executionId = uuidv4()
const result = await executor.execute(schedule.workflowId)
// Log each execution step
@@ -351,11 +352,12 @@ export async function GET(req: NextRequest) {
await persistLog({
id: uuidv4(),
workflowId: schedule.workflowId,
executionId: uuidv4(),
executionId,
level: 'error',
message: error.message || 'Unknown error during scheduled workflow execution',
createdAt: new Date(),
message: `Scheduled workflow execution failed: ${error.message || 'Unknown error'}`,
duration: 'NA',
trigger: 'schedule',
createdAt: new Date(),
})
// On error, increment next_run_at by a small delay to prevent immediate retries
+40
View File
@@ -0,0 +1,40 @@
import { NextRequest } from 'next/server'
import { v4 as uuidv4 } from 'uuid'
import { persistLog } from '@/lib/logging'
import { validateWorkflowAccess } from '../../middleware'
import { createErrorResponse, createSuccessResponse } from '../../utils'
export const dynamic = 'force-dynamic'
export async function POST(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
const { id } = await params
try {
const validation = await validateWorkflowAccess(request, id, false)
if (validation.error) {
return createErrorResponse(validation.error.message, validation.error.status)
}
const body = await request.json()
const { logs, executionId } = body
// Persist each log
for (const log of logs) {
await persistLog({
id: uuidv4(),
workflowId: id,
executionId,
level: log.level,
message: log.message,
duration: log.duration,
trigger: 'manual',
createdAt: new Date(log.createdAt || new Date()),
})
}
return createSuccessResponse({ message: 'Logs persisted successfully' })
} catch (error: any) {
console.error('Error persisting logs:', error)
return createErrorResponse(error.message || 'Failed to persist logs', 500)
}
}
+62
View File
@@ -1,4 +1,5 @@
import { useCallback, useState } from 'react'
import { v4 as uuidv4 } from 'uuid'
import { useConsoleStore } from '@/stores/console/store'
import { useNotificationStore } from '@/stores/notifications/store'
import { useEnvironmentStore } from '@/stores/settings/environment/store'
@@ -19,6 +20,24 @@ export function useWorkflowExecution() {
const { toggleConsole, isOpen } = useConsoleStore()
const { getAllVariables } = useEnvironmentStore()
const persistLogs = async (logs: any[], executionId: string) => {
try {
const response = await fetch(`/api/workflow/${activeWorkflowId}/log`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ logs, executionId }),
})
if (!response.ok) {
throw new Error('Failed to persist logs')
}
} catch (error) {
console.error('Error persisting logs:', error)
}
}
const handleRunWorkflow = useCallback(async () => {
if (!activeWorkflowId) return
setIsExecuting(true)
@@ -28,6 +47,8 @@ export function useWorkflowExecution() {
toggleConsole()
}
const executionId = uuidv4()
try {
// Use the mergeSubblockState utility to get all block states
const mergedStates = mergeSubblockState(blocks)
@@ -70,6 +91,34 @@ export function useWorkflowExecution() {
const executor = new Executor(workflow, currentBlockStates, envVarValues)
const result = await executor.execute(activeWorkflowId)
// Prepare logs for persistence
const blockLogs = (result.logs || []).map((log) => ({
level: log.success ? 'info' : 'error',
message: `Block ${log.blockName || log.blockId} (${log.blockType}): ${
log.error || 'Completed successfully'
}`,
duration: log.success ? `${log.durationMs}ms` : 'NA',
createdAt: new Date(log.endedAt || log.startedAt).toISOString(),
}))
// Calculate total duration from successful block logs
const totalDuration = (result.logs || [])
.filter((log) => log.success)
.reduce((sum, log) => sum + log.durationMs, 0)
// Add final execution result log
blockLogs.push({
level: result.success ? 'info' : 'error',
message: result.success
? 'Manual workflow executed successfully'
: `Manual workflow execution failed: ${result.error}`,
duration: result.success ? `${totalDuration}ms` : 'NA',
createdAt: new Date().toISOString(),
})
// Persist all logs
await persistLogs(blockLogs, executionId)
setExecutionResult(result)
// Show execution result notification
@@ -90,6 +139,19 @@ export function useWorkflowExecution() {
logs: [],
})
// Persist error log
await persistLogs(
[
{
level: 'error',
message: `Manual workflow execution failed: ${errorMessage}`,
duration: 'NA',
createdAt: new Date().toISOString(),
},
],
executionId
)
addNotification('error', `Workflow execution failed: ${errorMessage}`, activeWorkflowId)
} finally {
setIsExecuting(false)