Improve workflow metrics with faillure reason (#18768)
- split workflow errors into system and user errors - add workspace id to attributes for debugging
This commit is contained in:
@@ -85,11 +85,13 @@ export class MetricsService {
|
||||
eventId,
|
||||
attributes,
|
||||
shouldStoreInCache = true,
|
||||
debugLog,
|
||||
}: {
|
||||
key: MetricsKeys;
|
||||
eventId?: string;
|
||||
attributes?: Attributes;
|
||||
shouldStoreInCache?: boolean;
|
||||
debugLog?: string;
|
||||
}) {
|
||||
const counter = this.getMeter().createCounter(key);
|
||||
|
||||
@@ -98,6 +100,10 @@ export class MetricsService {
|
||||
if (shouldStoreInCache && eventId) {
|
||||
this.metricsCacheService.updateCounter(key, [eventId]);
|
||||
}
|
||||
|
||||
if (isDefined(debugLog)) {
|
||||
this.logger.debug(debugLog);
|
||||
}
|
||||
}
|
||||
|
||||
async batchIncrementCounter({
|
||||
|
||||
@@ -22,6 +22,7 @@ export enum MetricsKeys {
|
||||
WorkflowRunStopped = 'workflow-run/stopped',
|
||||
WorkflowRunThrottled = 'workflow-run/throttled',
|
||||
WorkflowRunFailedToEnqueue = 'workflow-run/failed/to-enqueue',
|
||||
WorkflowRunSystemError = 'workflow-run/system-error',
|
||||
AIToolExecutionFailed = 'ai-tool-execution/failed',
|
||||
AIToolExecutionSucceeded = 'ai-tool-execution/succeeded',
|
||||
SchemaVersionMismatch = 'schema-version/mismatch',
|
||||
|
||||
+3
-2
@@ -62,14 +62,14 @@ export class ResumeDelayedWorkflowJob {
|
||||
if (!step || !isWorkflowDelayAction(step)) {
|
||||
throw new WorkflowRunException(
|
||||
'Step not found or is not a delay action',
|
||||
WorkflowRunExceptionCode.INVALID_INPUT,
|
||||
WorkflowRunExceptionCode.INVALID_OPERATION,
|
||||
);
|
||||
}
|
||||
|
||||
if (stepInfo?.status !== StepStatus.PENDING) {
|
||||
throw new WorkflowRunException(
|
||||
'Step is not pending',
|
||||
WorkflowRunExceptionCode.INVALID_INPUT,
|
||||
WorkflowRunExceptionCode.INVALID_OPERATION,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ export class ResumeDelayedWorkflowJob {
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: `Error during delay resume: ${String(error)}`,
|
||||
isSystemError: true,
|
||||
});
|
||||
|
||||
throw error;
|
||||
|
||||
+12
-2
@@ -9,6 +9,10 @@ import { HttpTool } from 'src/engine/core-modules/tool/tools/http-tool/http-tool
|
||||
import { SendEmailTool } from 'src/engine/core-modules/tool/tools/email-tool/send-email-tool';
|
||||
import { type ToolInput } from 'src/engine/core-modules/tool/types/tool-input.type';
|
||||
import { type Tool } from 'src/engine/core-modules/tool/types/tool.type';
|
||||
import {
|
||||
WorkflowStepExecutorException,
|
||||
WorkflowStepExecutorExceptionCode,
|
||||
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
|
||||
import { type WorkflowActionInput } from 'src/modules/workflow/workflow-executor/types/workflow-action-input';
|
||||
import { type WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type';
|
||||
import { type WorkflowSendEmailActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/types/workflow-send-email-action-input.type';
|
||||
@@ -39,13 +43,19 @@ export class ToolExecutorWorkflowAction implements WorkflowAction {
|
||||
const step = steps.find((step) => step.id === currentStepId);
|
||||
|
||||
if (!step) {
|
||||
throw new Error('Step not found');
|
||||
throw new WorkflowStepExecutorException(
|
||||
'Step not found',
|
||||
WorkflowStepExecutorExceptionCode.STEP_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const tool = this.toolsByActionType.get(step.type);
|
||||
|
||||
if (!tool) {
|
||||
throw new Error(`No tool found for workflow action type: ${step.type}`);
|
||||
throw new WorkflowStepExecutorException(
|
||||
`No tool found for workflow action type: ${step.type}`,
|
||||
WorkflowStepExecutorExceptionCode.INVALID_STEP_TYPE,
|
||||
);
|
||||
}
|
||||
|
||||
let toolInput = step.settings.input;
|
||||
|
||||
+2
@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
|
||||
|
||||
import { BillingModule } from 'src/engine/core-modules/billing/billing.module';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { MetricsModule } from 'src/engine/core-modules/metrics/metrics.module';
|
||||
import { ToolModule } from 'src/engine/core-modules/tool/tool.module';
|
||||
import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module';
|
||||
import { WorkflowActionFactory } from 'src/modules/workflow/workflow-executor/factories/workflow-action.factory';
|
||||
@@ -36,6 +37,7 @@ import { WorkflowRunModule } from 'src/modules/workflow/workflow-runner/workflow
|
||||
EmptyActionModule,
|
||||
FeatureFlagModule,
|
||||
ToolModule,
|
||||
MetricsModule,
|
||||
],
|
||||
providers: [
|
||||
WorkflowExecutorWorkspaceService,
|
||||
|
||||
+10
-1
@@ -6,7 +6,9 @@ import { BILLING_FEATURE_USED } from 'src/engine/core-modules/billing/constants/
|
||||
import { BILLING_WORKFLOW_EXECUTION_ERROR_MESSAGE } from 'src/engine/core-modules/billing/constants/billing-workflow-execution-error-message.constant';
|
||||
import { BillingMeterEventName } from 'src/engine/core-modules/billing/enums/billing-meter-event-names';
|
||||
import { BillingService } from 'src/engine/core-modules/billing/services/billing.service';
|
||||
import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { MetricsService } from 'src/engine/core-modules/metrics/metrics.service';
|
||||
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
||||
import { WorkflowActionFactory } from 'src/modules/workflow/workflow-executor/factories/workflow-action.factory';
|
||||
import { shouldExecuteStep } from 'src/modules/workflow/workflow-executor/utils/should-execute-step.util';
|
||||
@@ -14,7 +16,6 @@ import {
|
||||
type WorkflowAction,
|
||||
WorkflowActionType,
|
||||
} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service';
|
||||
import { WorkflowExecutorWorkspaceService } from 'src/modules/workflow/workflow-executor/workspace-services/workflow-executor.workspace-service';
|
||||
import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service';
|
||||
|
||||
@@ -61,6 +62,10 @@ describe('WorkflowExecutorWorkspaceService', () => {
|
||||
captureExceptions: jest.fn(),
|
||||
};
|
||||
|
||||
const mockMetricsService = {
|
||||
incrementCounter: jest.fn(),
|
||||
};
|
||||
|
||||
const mockMessageQueueService = {
|
||||
add: jest.fn(),
|
||||
};
|
||||
@@ -97,6 +102,10 @@ describe('WorkflowExecutorWorkspaceService', () => {
|
||||
provide: `MESSAGE_QUEUE_${MessageQueue.workflowQueue}`,
|
||||
useValue: mockMessageQueueService,
|
||||
},
|
||||
{
|
||||
provide: MetricsService,
|
||||
useValue: mockMetricsService,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
|
||||
+12
-2
@@ -19,6 +19,8 @@ import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handl
|
||||
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
|
||||
import { MetricsService } from 'src/engine/core-modules/metrics/metrics.service';
|
||||
import { MetricsKeys } from 'src/engine/core-modules/metrics/types/metrics-keys.type';
|
||||
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
||||
import { WorkflowRunStatus } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity';
|
||||
import { workflowHasRunningSteps } from 'src/modules/workflow/common/utils/workflow-has-running-steps.util';
|
||||
@@ -57,6 +59,7 @@ export class WorkflowExecutorWorkspaceService {
|
||||
private readonly workflowRunWorkspaceService: WorkflowRunWorkspaceService,
|
||||
private readonly billingService: BillingService,
|
||||
private readonly exceptionHandlerService: ExceptionHandlerService,
|
||||
private readonly metricsService: MetricsService,
|
||||
@InjectMessageQueue(MessageQueue.workflowQueue)
|
||||
private readonly messageQueueService: MessageQueueService,
|
||||
) {}
|
||||
@@ -70,7 +73,7 @@ export class WorkflowExecutorWorkspaceService {
|
||||
}: WorkflowExecutorInput) {
|
||||
await Promise.all(
|
||||
stepIds.map(async (stepIdToExecute) => {
|
||||
await this.executeFromStep({
|
||||
return this.executeFromStep({
|
||||
stepId: stepIdToExecute,
|
||||
workflowRunId,
|
||||
workspaceId,
|
||||
@@ -92,7 +95,7 @@ export class WorkflowExecutorWorkspaceService {
|
||||
workflowRunId,
|
||||
workspaceId,
|
||||
executedStepsCount,
|
||||
}: WorkflowBranchExecutorInput) {
|
||||
}: WorkflowBranchExecutorInput): Promise<void> {
|
||||
const workflowRun =
|
||||
await this.workflowRunWorkspaceService.getWorkflowRunOrFail({
|
||||
workflowRunId,
|
||||
@@ -111,6 +114,7 @@ export class WorkflowExecutorWorkspaceService {
|
||||
workspaceId,
|
||||
status: WorkflowRunStatus.FAILED,
|
||||
error: 'Step not found',
|
||||
isSystemError: true,
|
||||
});
|
||||
|
||||
return;
|
||||
@@ -500,6 +504,12 @@ export class WorkflowExecutorWorkspaceService {
|
||||
this.exceptionHandlerService.captureExceptions([error], {
|
||||
workspace: { id: workspaceId },
|
||||
});
|
||||
|
||||
await this.metricsService.incrementCounter({
|
||||
key: MetricsKeys.WorkflowRunSystemError,
|
||||
eventId: workflowRunId,
|
||||
debugLog: `[Workflow Run System Error] Workflow run ${workflowRunId} in workspace ${workspaceId} ended with system error`,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -61,6 +61,7 @@ export class RunWorkflowJob {
|
||||
workflowRunId,
|
||||
status: WorkflowRunStatus.FAILED,
|
||||
error: error.message,
|
||||
isSystemError: true,
|
||||
});
|
||||
|
||||
throw error;
|
||||
|
||||
+21
-9
@@ -187,11 +187,13 @@ export class WorkflowRunWorkspaceService {
|
||||
workspaceId,
|
||||
status,
|
||||
error,
|
||||
isSystemError,
|
||||
}: {
|
||||
workflowRunId: string;
|
||||
workspaceId: string;
|
||||
status: Extract<WorkflowRunStatus, 'COMPLETED' | 'FAILED' | 'STOPPED'>;
|
||||
error?: string;
|
||||
isSystemError?: boolean;
|
||||
}) {
|
||||
const workflowRunToUpdate = await this.getWorkflowRunOrFail({
|
||||
workflowRunId,
|
||||
@@ -216,15 +218,25 @@ export class WorkflowRunWorkspaceService {
|
||||
|
||||
await this.updateWorkflowRun({ workflowRunId, workspaceId, partialUpdate });
|
||||
|
||||
const metricKey =
|
||||
status === WorkflowRunStatus.COMPLETED
|
||||
? MetricsKeys.WorkflowRunCompleted
|
||||
: status === WorkflowRunStatus.STOPPED
|
||||
? MetricsKeys.WorkflowRunStopped
|
||||
: MetricsKeys.WorkflowRunFailed;
|
||||
|
||||
await this.metricsService.incrementCounter({
|
||||
key:
|
||||
status === WorkflowRunStatus.COMPLETED
|
||||
? MetricsKeys.WorkflowRunCompleted
|
||||
: status === WorkflowRunStatus.STOPPED
|
||||
? MetricsKeys.WorkflowRunStopped
|
||||
: MetricsKeys.WorkflowRunFailed,
|
||||
key: metricKey,
|
||||
eventId: workflowRunId,
|
||||
});
|
||||
|
||||
if (isSystemError) {
|
||||
await this.metricsService.incrementCounter({
|
||||
key: MetricsKeys.WorkflowRunSystemError,
|
||||
eventId: workflowRunId,
|
||||
debugLog: `[Workflow Run System Error] Workflow run ${workflowRunId} in workspace ${workspaceId} ended with system error`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@WithLock('workflowRunId')
|
||||
@@ -250,7 +262,7 @@ export class WorkflowRunWorkspaceService {
|
||||
stepInfos: {
|
||||
...workflowRunToUpdate.state?.stepInfos,
|
||||
[stepId]: {
|
||||
...(workflowRunToUpdate.state?.stepInfos[stepId] || {}),
|
||||
...workflowRunToUpdate.state?.stepInfos[stepId],
|
||||
result: stepInfo?.result,
|
||||
error: stepInfo?.error,
|
||||
status: stepInfo.status,
|
||||
@@ -283,7 +295,7 @@ export class WorkflowRunWorkspaceService {
|
||||
|
||||
for (const [stepId, info] of Object.entries(stepInfos)) {
|
||||
mergedStepInfos[stepId] = {
|
||||
...(existingStepInfos[stepId] || {}),
|
||||
...existingStepInfos[stepId],
|
||||
...info,
|
||||
};
|
||||
}
|
||||
@@ -335,7 +347,7 @@ export class WorkflowRunWorkspaceService {
|
||||
state: {
|
||||
...workflowRunToUpdate.state,
|
||||
flow: {
|
||||
...(workflowRunToUpdate.state?.flow ?? {}),
|
||||
...workflowRunToUpdate.state?.flow,
|
||||
steps: updatedSteps,
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user