store stacktrace as a json array so we can reverse it when displaying and fixed up display of errors

This commit is contained in:
jasquat 2023-04-20 08:31:55 -04:00
parent 6fa18c53df
commit 08271da363
7 changed files with 36 additions and 22 deletions

View File

@ -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),

View File

@ -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)

View File

@ -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

View File

@ -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:

View File

@ -34,7 +34,9 @@ export const childrenForErrorObject = (errorObject: ErrorForDisplay) => {
);
}
const message = <div>{errorObject.message}</div>;
const message = (
<div className={errorObject.messageClassName}>{errorObject.message}</div>
);
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 = (
<div className="error_info">
<span className="error_title">Call Activity Trace:</span>
{errorObject.task_trace.reverse().join(' -> ')}
</div>
);
} else if (errorObject.stacktrace) {
codeTrace = (
<pre className="error_info">
<span className="error_title">Stacktrace:</span>
{errorObject.stacktrace.reverse().map((a) => (
<>
{a}
<br />
</>
))}
</pre>
);
}
return [
@ -63,7 +77,7 @@ export const childrenForErrorObject = (errorObject: ErrorForDisplay) => {
fileName,
lineNumber,
errorLine,
taskTrace,
codeTrace,
];
};

View File

@ -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;

View File

@ -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);
// <pre>{eventErrorDetails.stacktrace}</pre>
errorMessageTag = (
<>
<p className="failure-string">{eventErrorDetails.message}</p>
<br />
{errorChildren}
</>
);
errorMessageTag = <>{errorChildren}</>;
}
return (
<Modal
@ -207,8 +202,8 @@ export default function ProcessInstanceLogList({ variant }: OwnProps) {
failureCallback: (error: any) => {
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 = (