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 """empty message
Revision ID: e0510795b643 Revision ID: 5dae229f0a9b
Revises: Revises:
Create Date: 2023-04-19 14:36:11.004444 Create Date: 2023-04-20 08:19:03.409112
""" """
from alembic import op from alembic import op
@ -10,7 +10,7 @@ import sqlalchemy as sa
from sqlalchemy.dialects import mysql from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = 'e0510795b643' revision = '5dae229f0a9b'
down_revision = None down_revision = None
branch_labels = None branch_labels = None
depends_on = None depends_on = None
@ -467,7 +467,7 @@ def upgrade():
sa.Column('id', sa.Integer(), nullable=False), sa.Column('id', sa.Integer(), nullable=False),
sa.Column('process_instance_event_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('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_line_number', sa.Integer(), nullable=True),
sa.Column('task_offset', sa.Integer(), nullable=True), sa.Column('task_offset', sa.Integer(), nullable=True),
sa.Column('task_line_contents', sa.String(length=255), 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) message: str = db.Column(db.String(1024), nullable=False)
# this should be 65k in mysql # 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_line_number: Optional[int] = db.Column(db.Integer)
task_offset: 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: if exception is not None:
# truncate to avoid database errors on large values. We observed that text in mysql is 65K. # 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] message = str(exception)[0:1023]
task_line_number = None task_line_number = None

View File

@ -1,5 +1,6 @@
import copy import copy
import time import time
from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventType
from typing import Callable from typing import Callable
from typing import Optional from typing import Optional
from typing import Set from typing import Set
@ -329,6 +330,7 @@ class WorkflowExecutionService:
self.process_bpmn_messages() self.process_bpmn_messages()
self.queue_waiting_receive_messages() self.queue_waiting_receive_messages()
except SpiffWorkflowException as swe: 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 raise ApiError.from_workflow_exception("task_error", str(swe), swe) from swe
finally: 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 taskName = errorDetailDisplay(errorObject, 'task_name', 'Task Name');
const taskId = errorDetailDisplay(errorObject, 'task_id', 'Task ID'); const taskId = errorDetailDisplay(errorObject, 'task_id', 'Task ID');
const fileName = errorDetailDisplay(errorObject, 'file_name', 'File Name'); const fileName = errorDetailDisplay(errorObject, 'file_name', 'File Name');
@ -44,14 +46,26 @@ export const childrenForErrorObject = (errorObject: ErrorForDisplay) => {
'Line Number' 'Line Number'
); );
const errorLine = errorDetailDisplay(errorObject, 'error_line', 'Context'); const errorLine = errorDetailDisplay(errorObject, 'error_line', 'Context');
let taskTrace = null; let codeTrace = null;
if (errorObject.task_trace && errorObject.task_trace.length > 0) { if (errorObject.task_trace && errorObject.task_trace.length > 0) {
taskTrace = ( codeTrace = (
<div className="error_info"> <div className="error_info">
<span className="error_title">Call Activity Trace:</span> <span className="error_title">Call Activity Trace:</span>
{errorObject.task_trace.reverse().join(' -> ')} {errorObject.task_trace.reverse().join(' -> ')}
</div> </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 [ return [
@ -63,7 +77,7 @@ export const childrenForErrorObject = (errorObject: ErrorForDisplay) => {
fileName, fileName,
lineNumber, lineNumber,
errorLine, errorLine,
taskTrace, codeTrace,
]; ];
}; };

View File

@ -234,6 +234,8 @@ export type HotCrumbItem = HotCrumbItemArray | HotCrumbItemObject;
export interface ErrorForDisplay { export interface ErrorForDisplay {
message: string; message: string;
messageClassName?: string;
sentry_link?: string; sentry_link?: string;
task_name?: string; task_name?: string;
task_id?: string; task_id?: string;
@ -241,6 +243,7 @@ export interface ErrorForDisplay {
error_line?: string; error_line?: string;
file_name?: string; file_name?: string;
task_trace?: string[]; task_trace?: string[];
stacktrace?: string[];
} }
export interface AuthenticationParam { export interface AuthenticationParam {
@ -301,7 +304,7 @@ export interface JsonSchemaForm {
export interface ProcessInstanceEventErrorDetail { export interface ProcessInstanceEventErrorDetail {
id: number; id: number;
message: string; message: string;
stacktrace: string; stacktrace: string[];
task_line_contents?: string; task_line_contents?: string;
task_line_number?: number; task_line_number?: number;
task_offset?: number; task_offset?: number;

View File

@ -160,21 +160,16 @@ export default function ProcessInstanceLogList({ variant }: OwnProps) {
if (eventErrorDetails) { if (eventErrorDetails) {
const errorForDisplay: ErrorForDisplay = { const errorForDisplay: ErrorForDisplay = {
message: eventErrorDetails.message, message: eventErrorDetails.message,
messageClassName: 'failure-string',
task_name: eventForModal.task_definition_name, task_name: eventForModal.task_definition_name,
task_id: eventForModal.task_definition_identifier, task_id: eventForModal.task_definition_identifier,
line_number: eventErrorDetails.task_line_number, line_number: eventErrorDetails.task_line_number,
error_line: eventErrorDetails.task_line_contents, error_line: eventErrorDetails.task_line_contents,
task_trace: eventErrorDetails.task_trace, task_trace: eventErrorDetails.task_trace,
stacktrace: eventErrorDetails.stacktrace,
}; };
const errorChildren = childrenForErrorObject(errorForDisplay); const errorChildren = childrenForErrorObject(errorForDisplay);
// <pre>{eventErrorDetails.stacktrace}</pre> errorMessageTag = <>{errorChildren}</>;
errorMessageTag = (
<>
<p className="failure-string">{eventErrorDetails.message}</p>
<br />
{errorChildren}
</>
);
} }
return ( return (
<Modal <Modal
@ -207,8 +202,8 @@ export default function ProcessInstanceLogList({ variant }: OwnProps) {
failureCallback: (error: any) => { failureCallback: (error: any) => {
const errorObject: ProcessInstanceEventErrorDetail = { const errorObject: ProcessInstanceEventErrorDetail = {
id: 0, id: 0,
message: `ERROR: ${error.message}`, message: `ERROR retrieving error details: ${error.message}`,
stacktrace: '', stacktrace: [],
}; };
setEventErrorDetails(errorObject); setEventErrorDetails(errorObject);
}, },
@ -218,7 +213,7 @@ export default function ProcessInstanceLogList({ variant }: OwnProps) {
const eventTypeCell = (logEntry: ProcessInstanceLogEntry) => { const eventTypeCell = (logEntry: ProcessInstanceLogEntry) => {
if ( 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 errorTitle = 'Event has an error';
const errorIcon = ( const errorIcon = (