From 08271da363df7ef95a93d0bae833a25815f59d3f Mon Sep 17 00:00:00 2001 From: jasquat Date: Thu, 20 Apr 2023 08:31:55 -0400 Subject: [PATCH] store stacktrace as a json array so we can reverse it when displaying and fixed up display of errors --- .../{e0510795b643_.py => 5dae229f0a9b_.py} | 8 +++---- .../models/process_instance_error_detail.py | 2 +- .../services/task_service.py | 2 +- .../services/workflow_execution_service.py | 2 ++ .../src/components/ErrorDisplay.tsx | 22 +++++++++++++++---- spiffworkflow-frontend/src/interfaces.ts | 5 ++++- .../src/routes/ProcessInstanceLogList.tsx | 17 +++++--------- 7 files changed, 36 insertions(+), 22 deletions(-) rename spiffworkflow-backend/migrations/versions/{e0510795b643_.py => 5dae229f0a9b_.py} (99%) diff --git a/spiffworkflow-backend/migrations/versions/e0510795b643_.py b/spiffworkflow-backend/migrations/versions/5dae229f0a9b_.py similarity index 99% rename from spiffworkflow-backend/migrations/versions/e0510795b643_.py rename to spiffworkflow-backend/migrations/versions/5dae229f0a9b_.py index d613a74a9..25d6edffe 100644 --- a/spiffworkflow-backend/migrations/versions/e0510795b643_.py +++ b/spiffworkflow-backend/migrations/versions/5dae229f0a9b_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: e0510795b643 +Revision ID: 5dae229f0a9b Revises: -Create Date: 2023-04-19 14:36:11.004444 +Create Date: 2023-04-20 08:19:03.409112 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa from sqlalchemy.dialects import mysql # revision identifiers, used by Alembic. -revision = 'e0510795b643' +revision = '5dae229f0a9b' down_revision = None branch_labels = None depends_on = None @@ -467,7 +467,7 @@ def upgrade(): sa.Column('id', sa.Integer(), nullable=False), sa.Column('process_instance_event_id', sa.Integer(), nullable=False), sa.Column('message', sa.String(length=1024), nullable=False), - sa.Column('stacktrace', sa.Text(), nullable=False), + sa.Column('stacktrace', sa.JSON(), nullable=False), sa.Column('task_line_number', sa.Integer(), nullable=True), sa.Column('task_offset', sa.Integer(), nullable=True), sa.Column('task_line_contents', sa.String(length=255), nullable=True), diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_error_detail.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_error_detail.py index 663ab6cee..743cb5fd5 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_error_detail.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_error_detail.py @@ -17,7 +17,7 @@ class ProcessInstanceErrorDetailModel(SpiffworkflowBaseDBModel): message: str = db.Column(db.String(1024), nullable=False) # this should be 65k in mysql - stacktrace: str = db.Column(db.Text(), nullable=False) + stacktrace: Optional[list] = db.Column(db.JSON, nullable=False) task_line_number: Optional[int] = db.Column(db.Integer) task_offset: Optional[int] = db.Column(db.Integer) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py index 495394622..73baa7389 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py @@ -619,7 +619,7 @@ class TaskService: if exception is not None: # truncate to avoid database errors on large values. We observed that text in mysql is 65K. - stacktrace = traceback.format_exc()[0:63999] + stacktrace = traceback.format_exc().split("\n") message = str(exception)[0:1023] task_line_number = None diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py index 253c402ea..8ccd5456c 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py @@ -1,5 +1,6 @@ import copy import time +from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventType from typing import Callable from typing import Optional from typing import Set @@ -329,6 +330,7 @@ class WorkflowExecutionService: self.process_bpmn_messages() self.queue_waiting_receive_messages() except SpiffWorkflowException as swe: + TaskService.add_event_to_process_instance(self.process_instance_model, ProcessInstanceEventType.task_failed.value, exception=swe, task_guid=str(swe.task.id)) raise ApiError.from_workflow_exception("task_error", str(swe), swe) from swe finally: diff --git a/spiffworkflow-frontend/src/components/ErrorDisplay.tsx b/spiffworkflow-frontend/src/components/ErrorDisplay.tsx index 5b68d301e..40abec5d5 100644 --- a/spiffworkflow-frontend/src/components/ErrorDisplay.tsx +++ b/spiffworkflow-frontend/src/components/ErrorDisplay.tsx @@ -34,7 +34,9 @@ export const childrenForErrorObject = (errorObject: ErrorForDisplay) => { ); } - const message =
{errorObject.message}
; + const message = ( +
{errorObject.message}
+ ); const taskName = errorDetailDisplay(errorObject, 'task_name', 'Task Name'); const taskId = errorDetailDisplay(errorObject, 'task_id', 'Task ID'); const fileName = errorDetailDisplay(errorObject, 'file_name', 'File Name'); @@ -44,14 +46,26 @@ export const childrenForErrorObject = (errorObject: ErrorForDisplay) => { 'Line Number' ); const errorLine = errorDetailDisplay(errorObject, 'error_line', 'Context'); - let taskTrace = null; + let codeTrace = null; if (errorObject.task_trace && errorObject.task_trace.length > 0) { - taskTrace = ( + codeTrace = (
Call Activity Trace: {errorObject.task_trace.reverse().join(' -> ')}
); + } else if (errorObject.stacktrace) { + codeTrace = ( +
+        Stacktrace:
+        {errorObject.stacktrace.reverse().map((a) => (
+          <>
+            {a}
+            
+ + ))} +
+ ); } return [ @@ -63,7 +77,7 @@ export const childrenForErrorObject = (errorObject: ErrorForDisplay) => { fileName, lineNumber, errorLine, - taskTrace, + codeTrace, ]; }; diff --git a/spiffworkflow-frontend/src/interfaces.ts b/spiffworkflow-frontend/src/interfaces.ts index 7cf097d9e..f0b2f48cf 100644 --- a/spiffworkflow-frontend/src/interfaces.ts +++ b/spiffworkflow-frontend/src/interfaces.ts @@ -234,6 +234,8 @@ export type HotCrumbItem = HotCrumbItemArray | HotCrumbItemObject; export interface ErrorForDisplay { message: string; + + messageClassName?: string; sentry_link?: string; task_name?: string; task_id?: string; @@ -241,6 +243,7 @@ export interface ErrorForDisplay { error_line?: string; file_name?: string; task_trace?: string[]; + stacktrace?: string[]; } export interface AuthenticationParam { @@ -301,7 +304,7 @@ export interface JsonSchemaForm { export interface ProcessInstanceEventErrorDetail { id: number; message: string; - stacktrace: string; + stacktrace: string[]; task_line_contents?: string; task_line_number?: number; task_offset?: number; diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx index cae1e8802..dac849ac6 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx @@ -160,21 +160,16 @@ export default function ProcessInstanceLogList({ variant }: OwnProps) { if (eventErrorDetails) { const errorForDisplay: ErrorForDisplay = { message: eventErrorDetails.message, + messageClassName: 'failure-string', task_name: eventForModal.task_definition_name, task_id: eventForModal.task_definition_identifier, line_number: eventErrorDetails.task_line_number, error_line: eventErrorDetails.task_line_contents, task_trace: eventErrorDetails.task_trace, + stacktrace: eventErrorDetails.stacktrace, }; const errorChildren = childrenForErrorObject(errorForDisplay); - //
{eventErrorDetails.stacktrace}
- errorMessageTag = ( - <> -

{eventErrorDetails.message}

-
- {errorChildren} - - ); + errorMessageTag = <>{errorChildren}; } return ( { const errorObject: ProcessInstanceEventErrorDetail = { id: 0, - message: `ERROR: ${error.message}`, - stacktrace: '', + message: `ERROR retrieving error details: ${error.message}`, + stacktrace: [], }; setEventErrorDetails(errorObject); }, @@ -218,7 +213,7 @@ export default function ProcessInstanceLogList({ variant }: OwnProps) { const eventTypeCell = (logEntry: ProcessInstanceLogEntry) => { if ( - ['process_instance_error', 'task_error'].includes(logEntry.event_type) + ['process_instance_error', 'task_failed'].includes(logEntry.event_type) ) { const errorTitle = 'Event has an error'; const errorIcon = (