Add graphql queries error codes metrics (#12833)
## Context Added to the existing useGraphQLErrorHandlerHook yoga hook to increment metrics after all query executions based on their error codes. I originally wanted to create a new useMetrics hook but most of the error handling was done in useGraphQLErrorHandlerHook so we decided to keep it there for now. <img width="1310" alt="Screenshot 2025-06-24 at 15 58 26" src="https://github.com/user-attachments/assets/498d3754-851a-4051-a5c2-23ac8253aa6a" />
This commit is contained in:
@ -1,8 +1,8 @@
|
||||
import {
|
||||
OnExecuteDoneHookResultOnNextHook,
|
||||
Plugin,
|
||||
getDocumentString,
|
||||
handleStreamOrSingleExecutionResult,
|
||||
OnExecuteDoneHookResultOnNextHook,
|
||||
Plugin,
|
||||
} from '@envelop/core';
|
||||
import { GraphQLError, Kind, OperationDefinitionNode, print } from 'graphql';
|
||||
|
||||
@ -13,8 +13,14 @@ import { generateGraphQLErrorFromError } from 'src/engine/core-modules/graphql/u
|
||||
import {
|
||||
BaseGraphQLError,
|
||||
convertGraphQLErrorToBaseGraphQLError,
|
||||
ErrorCode,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { shouldCaptureException } from 'src/engine/utils/global-exception-handler.util';
|
||||
import { MetricsService } from 'src/engine/core-modules/metrics/metrics.service';
|
||||
import { MetricsKeys } from 'src/engine/core-modules/metrics/types/metrics-keys.type';
|
||||
import {
|
||||
graphQLErrorCodesToFilter,
|
||||
shouldCaptureException,
|
||||
} from 'src/engine/utils/global-exception-handler.util';
|
||||
|
||||
const DEFAULT_EVENT_ID_KEY = 'exceptionEventId';
|
||||
const SCHEMA_VERSION_HEADER = 'x-schema-version';
|
||||
@ -22,6 +28,8 @@ const SCHEMA_MISMATCH_ERROR =
|
||||
'Your workspace has been updated with a new data model. Please refresh the page.';
|
||||
|
||||
type GraphQLErrorHandlerHookOptions = {
|
||||
metricsService: MetricsService;
|
||||
|
||||
/**
|
||||
* The exception handler service to use.
|
||||
*/
|
||||
@ -81,6 +89,10 @@ export const useGraphQLErrorHandlerHook = <
|
||||
setResult,
|
||||
}) => {
|
||||
if (!result.errors || result.errors.length === 0) {
|
||||
options.metricsService.incrementCounter({
|
||||
key: MetricsKeys.GraphqlOperation200,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -105,6 +117,48 @@ export const useGraphQLErrorHandlerHook = <
|
||||
return originalError;
|
||||
});
|
||||
|
||||
// Error metrics
|
||||
const codeToMetricKey: Partial<Record<ErrorCode, MetricsKeys>> = {
|
||||
[ErrorCode.UNAUTHENTICATED]: MetricsKeys.GraphqlOperation401,
|
||||
[ErrorCode.FORBIDDEN]: MetricsKeys.GraphqlOperation403,
|
||||
[ErrorCode.NOT_FOUND]: MetricsKeys.GraphqlOperation404,
|
||||
[ErrorCode.INTERNAL_SERVER_ERROR]:
|
||||
MetricsKeys.GraphqlOperation500,
|
||||
};
|
||||
|
||||
const statusToMetricKey: Record<number, MetricsKeys> = {
|
||||
400: MetricsKeys.GraphqlOperation400,
|
||||
401: MetricsKeys.GraphqlOperation401,
|
||||
403: MetricsKeys.GraphqlOperation403,
|
||||
404: MetricsKeys.GraphqlOperation404,
|
||||
500: MetricsKeys.GraphqlOperation500,
|
||||
};
|
||||
|
||||
processedErrors.forEach((error) => {
|
||||
let metricKey: MetricsKeys | undefined;
|
||||
|
||||
if (error instanceof BaseGraphQLError) {
|
||||
const code = error.extensions?.code as ErrorCode;
|
||||
|
||||
metricKey = codeToMetricKey[code];
|
||||
if (!metricKey && graphQLErrorCodesToFilter.includes(code)) {
|
||||
metricKey = MetricsKeys.GraphqlOperation400;
|
||||
}
|
||||
} else if (error instanceof GraphQLError) {
|
||||
const status = error.extensions?.http?.status as number;
|
||||
|
||||
metricKey = statusToMetricKey[status];
|
||||
}
|
||||
|
||||
if (metricKey) {
|
||||
options.metricsService.incrementCounter({ key: metricKey });
|
||||
} else {
|
||||
options.metricsService.incrementCounter({
|
||||
key: MetricsKeys.GraphqlOperationUnknown,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Step 2: Send errors to monitoring service (with stack traces)
|
||||
const errorsToCapture = processedErrors.filter(
|
||||
shouldCaptureException,
|
||||
|
||||
@ -15,7 +15,7 @@ export class MetricsService {
|
||||
shouldStoreInCache = true,
|
||||
}: {
|
||||
key: MetricsKeys;
|
||||
eventId: string;
|
||||
eventId?: string;
|
||||
shouldStoreInCache?: boolean;
|
||||
}) {
|
||||
//TODO : Define meter name usage in monitoring
|
||||
@ -24,7 +24,7 @@ export class MetricsService {
|
||||
|
||||
counter.add(1);
|
||||
|
||||
if (shouldStoreInCache) {
|
||||
if (shouldStoreInCache && eventId) {
|
||||
this.metricsCacheService.updateCounter(key, [eventId]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,13 @@ export enum MetricsKeys {
|
||||
CalendarEventSyncJobFailedInsufficientPermissions = 'calendar-event-sync-job/failed-insufficient-permissions',
|
||||
CalendarEventSyncJobFailedUnknown = 'calendar-event-sync-job/failed-unknown',
|
||||
InvalidCaptcha = 'invalid-captcha',
|
||||
GraphqlOperation200 = 'graphql-operation/200',
|
||||
GraphqlOperation400 = 'graphql-operation/400',
|
||||
GraphqlOperation401 = 'graphql-operation/401',
|
||||
GraphqlOperation403 = 'graphql-operation/403',
|
||||
GraphqlOperation404 = 'graphql-operation/404',
|
||||
GraphqlOperation500 = 'graphql-operation/500',
|
||||
GraphqlOperationUnknown = 'graphql-operation/unknown',
|
||||
WorkflowRunStartedDatabaseEventTrigger = 'workflow-run/started/database-event-trigger',
|
||||
WorkflowRunStartedCronTrigger = 'workflow-run/started/cron-trigger',
|
||||
WorkflowRunStartedWebhookTrigger = 'workflow-run/started/webhook-trigger',
|
||||
|
||||
Reference in New Issue
Block a user