mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 01:12:56 +03:00
console: programmaticallyTraceError logs to the Console too
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/7714 GitOrigin-RevId: 7307093f9db4a871a43011705b42c06332c57d18
This commit is contained in:
parent
710a33bf45
commit
03bf7e8c6f
@ -64,7 +64,6 @@ export const clickRunQueryButton = () => {
|
||||
'Could not find run query button in the DOM (.execute-button)'
|
||||
);
|
||||
programmaticallyTraceError(error);
|
||||
console.warn(error);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -164,13 +164,10 @@ export function useCreateHasuraCloudDatasource(
|
||||
setState(prevState => {
|
||||
if (prevState.status === 'adding-env-var') {
|
||||
// this is an unexpected error; so we need alerts about this
|
||||
programmaticallyTraceError(
|
||||
new Error('Failed creating env vars in Hasura'),
|
||||
{
|
||||
sourceError: error,
|
||||
errorMessage: error.message ?? '',
|
||||
}
|
||||
);
|
||||
programmaticallyTraceError({
|
||||
error: 'Failed creating env vars in Hasura',
|
||||
cause: error,
|
||||
});
|
||||
return {
|
||||
status: 'adding-env-var-failed',
|
||||
payload: { dbUrl },
|
||||
@ -178,13 +175,10 @@ export function useCreateHasuraCloudDatasource(
|
||||
// if adding data-source fails unexpectedly, set the error state
|
||||
} else if (prevState.status === 'adding-data-source') {
|
||||
// this is an unexpected error; so we need alerts about this
|
||||
programmaticallyTraceError(
|
||||
new Error('Failed adding created data source in Hasura'),
|
||||
{
|
||||
sourceError: error,
|
||||
errorMessage: error.message ?? '',
|
||||
}
|
||||
);
|
||||
programmaticallyTraceError({
|
||||
error: 'Failed adding created data source in Hasura',
|
||||
cause: error,
|
||||
});
|
||||
|
||||
return {
|
||||
status: 'adding-data-source-failed',
|
||||
|
@ -134,7 +134,6 @@ export function Analytics(props: AnalyticsProps) {
|
||||
const overrideError = new Error(
|
||||
`All the following attributes will be overridden: ${overrideHtmlAttributes} for the element with name "${name}"`
|
||||
);
|
||||
console.error(overrideError);
|
||||
programmaticallyTraceError(overrideError);
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,113 @@
|
||||
import type { ExceptionContext } from './sentry/captureException';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { captureException } from './sentry/captureException';
|
||||
|
||||
type HumanReadableString = string;
|
||||
|
||||
type Options = {
|
||||
level?: 'error' | 'warning';
|
||||
/**
|
||||
* Logging to the Console has a specific purpose: Sentry is NOT enabled for every type of Console
|
||||
* (at the time of writing, Sentry is enabled only for the Cloud Console). Logging to the browser's
|
||||
* console allows the eventual customers that is trying to understand the root cause of a problem,
|
||||
* to report what they see in the browser's console in the issue they are going to open.
|
||||
*/
|
||||
logToConsole?: boolean;
|
||||
};
|
||||
|
||||
type HumanReadableStringWithErrorCauseAndOptions = Options & {
|
||||
error: HumanReadableString;
|
||||
cause?: Error;
|
||||
};
|
||||
type ErrorWithOptions = Options & {
|
||||
error: Error;
|
||||
};
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
/**
|
||||
* Programmatically trace a caught error.
|
||||
*
|
||||
* @example <caption>Simplest usage: pass just a string.</caption>
|
||||
* programmaticallyTraceError('Something went wrong when updating the metadata')
|
||||
*
|
||||
* @example <caption>Simplest usage: pass the error you receive.</caption>
|
||||
* catch (error) {
|
||||
* programmaticallyTraceError(error)
|
||||
* }
|
||||
*
|
||||
* @example <caption>Pass a human-readable message, but also the causing error.</caption>
|
||||
* catch (error) {
|
||||
* programmaticallyTraceError({ error: 'Something went wrong when updating the metadata', cause: error })
|
||||
* }
|
||||
*/
|
||||
export function programmaticallyTraceError(
|
||||
error: Error,
|
||||
exceptionContext: ExceptionContext = {},
|
||||
level: 'error' | 'warning' = 'error'
|
||||
errorOrErrorWithOptions:
|
||||
| HumanReadableString
|
||||
| Error
|
||||
| HumanReadableStringWithErrorCauseAndOptions
|
||||
| ErrorWithOptions
|
||||
) {
|
||||
captureException(error, exceptionContext, level);
|
||||
// --------------------------------------------------
|
||||
// SIMPLE USAGE
|
||||
// --------------------------------------------------
|
||||
|
||||
// If you pass a string, it's converted to an error and passed to Sentry.
|
||||
if (typeof errorOrErrorWithOptions === 'string') {
|
||||
captureException(new Error(errorOrErrorWithOptions));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If you pass an error, it's passed to Sentry as is.
|
||||
if (errorOrErrorWithOptions instanceof Error) {
|
||||
captureException(errorOrErrorWithOptions);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// OPTIONS-RICH USAGE
|
||||
// --------------------------------------------------
|
||||
|
||||
const {
|
||||
error,
|
||||
level = 'error',
|
||||
logToConsole = true,
|
||||
} = errorOrErrorWithOptions;
|
||||
|
||||
const consoleLog = logToConsole
|
||||
? level === 'warning'
|
||||
? console.warn
|
||||
: console.error
|
||||
: noop;
|
||||
|
||||
// If you pass a cause, the cause itself is the original error and IT IS the error that will be
|
||||
// tracked to Sentry. Instead, the passed human-friendly string will be added as a breadcrumb that
|
||||
// you can see in Sentry right after the error.
|
||||
if ('cause' in errorOrErrorWithOptions && !!errorOrErrorWithOptions.cause) {
|
||||
const { cause } = errorOrErrorWithOptions;
|
||||
|
||||
const message = typeof error === 'string' ? error : error.message;
|
||||
|
||||
Sentry.addBreadcrumb({ level, message });
|
||||
captureException(cause);
|
||||
consoleLog(message);
|
||||
consoleLog(cause);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// The string will be converted to an error and tracked in Sentry
|
||||
if (typeof error === 'string') {
|
||||
const errorToLog = new Error(error);
|
||||
|
||||
captureException(errorToLog, level);
|
||||
consoleLog(errorToLog);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// The error is tracked in Sentry
|
||||
captureException(error, level);
|
||||
consoleLog(error);
|
||||
}
|
||||
|
@ -1,23 +1,15 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
|
||||
export type ExceptionContext = {
|
||||
sourceError?: Error;
|
||||
errorMessage?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* This function allows us to capture caught exceptions that we want the engineering team to be
|
||||
* alerted about
|
||||
* A simple wrapper around Sentry's captureException.
|
||||
*
|
||||
* @see https://docs.sentry.io/platforms/javascript/enriching-events/context/
|
||||
*/
|
||||
export function captureException(
|
||||
error: Error,
|
||||
exceptionContext: ExceptionContext = {},
|
||||
level: 'error' | 'warning' = 'error'
|
||||
) {
|
||||
Sentry.captureException(error, {
|
||||
level,
|
||||
contexts: { debug: exceptionContext },
|
||||
});
|
||||
}
|
||||
|
@ -56,6 +56,12 @@ export function startSentryTracing(globalVars: Globals, envVars: EnvVars) {
|
||||
// sensitive data
|
||||
dom: false,
|
||||
}),
|
||||
|
||||
// ATTENTION: functions like programmaticallyTraceError could internally log errors to the
|
||||
// browser's console, causing an infinite loop!
|
||||
// new CaptureConsoleIntegration({
|
||||
// levels: ['error'],
|
||||
// }),
|
||||
],
|
||||
|
||||
// Allow grouping logs by environment
|
||||
|
@ -62,13 +62,10 @@ export function TemplateSummary(props: Props) {
|
||||
staleTime,
|
||||
onError: (e: any) => {
|
||||
// this is unexpected; so get alerted
|
||||
programmaticallyTraceError(
|
||||
new Error('failed to get a sample query in template summary'),
|
||||
{
|
||||
sourceError: e,
|
||||
errorMessage: e.message ?? '',
|
||||
}
|
||||
);
|
||||
programmaticallyTraceError({
|
||||
error: 'failed to get a sample query in template summary',
|
||||
cause: e,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -118,7 +118,6 @@ export const emitOnboardingEvent = (variables: Record<string, unknown>) => {
|
||||
variables,
|
||||
cloudHeaders
|
||||
).catch(error => {
|
||||
console.error(error);
|
||||
programmaticallyTraceError(error);
|
||||
});
|
||||
};
|
||||
|
@ -159,14 +159,10 @@ export function WorkflowProgress(props: WorkflowProgressProps) {
|
||||
);
|
||||
},
|
||||
error => {
|
||||
programmaticallyTraceError(
|
||||
new Error('failed subscribing to one click deployment status'),
|
||||
{
|
||||
errorMessage: error.message,
|
||||
sourceError: error,
|
||||
},
|
||||
'error'
|
||||
);
|
||||
programmaticallyTraceError({
|
||||
error: 'failed subscribing to one click deployment status',
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
);
|
||||
return () => {
|
||||
|
@ -80,12 +80,9 @@ export function useOnSetOpenTelemetryError(
|
||||
}
|
||||
);
|
||||
|
||||
programmaticallyTraceError(
|
||||
new Error(
|
||||
'OpenTelemetry set_opentelemetry_config error not parsed',
|
||||
// @ts-expect-error This error will automatically disappear with Nx that targets new browsers by default
|
||||
{ cause: err }
|
||||
)
|
||||
);
|
||||
programmaticallyTraceError({
|
||||
error: 'OpenTelemetry set_opentelemetry_config error not parsed',
|
||||
cause: err instanceof Error ? err : undefined,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -58,11 +58,8 @@ export function useTrackTypeMisalignments(
|
||||
}
|
||||
);
|
||||
|
||||
programmaticallyTraceError(
|
||||
new Error(
|
||||
'OpenTelemetry metadata not parsed',
|
||||
// @ts-expect-error This error will automatically disappear with Nx that targets new browsers by default
|
||||
{ cause: result.error }
|
||||
)
|
||||
);
|
||||
programmaticallyTraceError({
|
||||
error: 'OpenTelemetry metadata not parsed',
|
||||
cause: result.error,
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user