mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 04:04:06 -08:00
feat(editor): Improve Sentry ignore definitions for class instance types (no-changelog) (#11208)
Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
This commit is contained in:
parent
6c6a8efdea
commit
3d2fbcfd93
|
@ -252,6 +252,7 @@ export class Server extends AbstractServer {
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
dsn: this.globalConfig.sentry.frontendDsn,
|
dsn: this.globalConfig.sentry.frontendDsn,
|
||||||
environment: process.env.ENVIRONMENT || 'development',
|
environment: process.env.ENVIRONMENT || 'development',
|
||||||
|
serverName: process.env.DEPLOYMENT_NAME,
|
||||||
release: N8N_VERSION,
|
release: N8N_VERSION,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue';
|
||||||
import * as Sentry from '@sentry/vue';
|
|
||||||
|
|
||||||
import '@vue-flow/core/dist/style.css';
|
import '@vue-flow/core/dist/style.css';
|
||||||
import '@vue-flow/core/dist/theme-default.css';
|
import '@vue-flow/core/dist/theme-default.css';
|
||||||
|
@ -30,32 +29,13 @@ import { FontAwesomePlugin } from './plugins/icons';
|
||||||
import { createPinia, PiniaVuePlugin } from 'pinia';
|
import { createPinia, PiniaVuePlugin } from 'pinia';
|
||||||
import { JsPlumbPlugin } from '@/plugins/jsplumb';
|
import { JsPlumbPlugin } from '@/plugins/jsplumb';
|
||||||
import { ChartJSPlugin } from '@/plugins/chartjs';
|
import { ChartJSPlugin } from '@/plugins/chartjs';
|
||||||
import { AxiosError } from 'axios';
|
import { SentryPlugin } from '@/plugins/sentry';
|
||||||
|
|
||||||
const pinia = createPinia();
|
const pinia = createPinia();
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
|
||||||
if (window.sentry?.dsn) {
|
app.use(SentryPlugin);
|
||||||
const { dsn, release, environment } = window.sentry;
|
|
||||||
Sentry.init({
|
|
||||||
app,
|
|
||||||
dsn,
|
|
||||||
release,
|
|
||||||
environment,
|
|
||||||
beforeSend(event, { originalException }) {
|
|
||||||
if (
|
|
||||||
!originalException ||
|
|
||||||
originalException instanceof AxiosError ||
|
|
||||||
(originalException instanceof Error && originalException.message.includes('ResizeObserver'))
|
|
||||||
) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return event;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
app.use(TelemetryPlugin);
|
app.use(TelemetryPlugin);
|
||||||
app.use(PiniaVuePlugin);
|
app.use(PiniaVuePlugin);
|
||||||
app.use(I18nPlugin);
|
app.use(I18nPlugin);
|
||||||
|
|
46
packages/editor-ui/src/plugins/sentry.spec.ts
Normal file
46
packages/editor-ui/src/plugins/sentry.spec.ts
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import type * as Sentry from '@sentry/vue';
|
||||||
|
import { beforeSend } from '@/plugins/sentry';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import { ResponseError } from '@/utils/apiUtils';
|
||||||
|
|
||||||
|
function createErrorEvent(): Sentry.ErrorEvent {
|
||||||
|
return {} as Sentry.ErrorEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('beforeSend', () => {
|
||||||
|
it('should return null when originalException is undefined', () => {
|
||||||
|
const event = createErrorEvent();
|
||||||
|
const hint = { originalException: undefined };
|
||||||
|
expect(beforeSend(event, hint)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null when originalException matches ignoredErrors by instance and message', () => {
|
||||||
|
const event = createErrorEvent();
|
||||||
|
const hint = { originalException: new ResponseError("Can't connect to n8n.") };
|
||||||
|
expect(beforeSend(event, hint)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null when originalException matches ignoredErrors by instance and message regex', () => {
|
||||||
|
const event = createErrorEvent();
|
||||||
|
const hint = { originalException: new ResponseError('ECONNREFUSED') };
|
||||||
|
expect(beforeSend(event, hint)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null when originalException matches ignoredErrors by instance only', () => {
|
||||||
|
const event = createErrorEvent();
|
||||||
|
const hint = { originalException: new AxiosError() };
|
||||||
|
expect(beforeSend(event, hint)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null when originalException matches ignoredErrors by instance and message regex (ResizeObserver)', () => {
|
||||||
|
const event = createErrorEvent();
|
||||||
|
const hint = { originalException: new Error('ResizeObserver loop limit exceeded') };
|
||||||
|
expect(beforeSend(event, hint)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return event when originalException does not match any ignoredErrors', () => {
|
||||||
|
const event = createErrorEvent();
|
||||||
|
const hint = { originalException: new Error('Some other error') };
|
||||||
|
expect(beforeSend(event, hint)).toEqual(event);
|
||||||
|
});
|
||||||
|
});
|
59
packages/editor-ui/src/plugins/sentry.ts
Normal file
59
packages/editor-ui/src/plugins/sentry.ts
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import type { Plugin } from 'vue';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import { ResponseError } from '@/utils/apiUtils';
|
||||||
|
import * as Sentry from '@sentry/vue';
|
||||||
|
|
||||||
|
const ignoredErrors = [
|
||||||
|
{ instanceof: AxiosError },
|
||||||
|
{ instanceof: ResponseError, message: /ECONNREFUSED/ },
|
||||||
|
{ instanceof: ResponseError, message: "Can't connect to n8n." },
|
||||||
|
{ instanceof: Error, message: /ResizeObserver/ },
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export function beforeSend(event: Sentry.ErrorEvent, { originalException }: Sentry.EventHint) {
|
||||||
|
if (
|
||||||
|
!originalException ||
|
||||||
|
ignoredErrors.some((entry) => {
|
||||||
|
const typeMatch = originalException instanceof entry.instanceof;
|
||||||
|
if (!typeMatch) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('message' in entry) {
|
||||||
|
if (entry.message instanceof RegExp) {
|
||||||
|
return entry.message.test(originalException.message ?? '');
|
||||||
|
} else {
|
||||||
|
return originalException.message === entry.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SentryPlugin: Plugin = {
|
||||||
|
install: (app) => {
|
||||||
|
if (!window.sentry?.dsn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { dsn, release, environment, serverName } = window.sentry;
|
||||||
|
|
||||||
|
Sentry.init({
|
||||||
|
app,
|
||||||
|
dsn,
|
||||||
|
release,
|
||||||
|
environment,
|
||||||
|
beforeSend,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (serverName) {
|
||||||
|
Sentry.setTag('server_name', serverName);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
2
packages/editor-ui/src/shims.d.ts
vendored
2
packages/editor-ui/src/shims.d.ts
vendored
|
@ -18,7 +18,7 @@ declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
BASE_PATH: string;
|
BASE_PATH: string;
|
||||||
REST_ENDPOINT: string;
|
REST_ENDPOINT: string;
|
||||||
sentry?: { dsn?: string; environment: string; release: string };
|
sentry?: { dsn?: string; environment: string; release: string; serverName?: string };
|
||||||
n8nExternalHooks?: PartialDeep<ExternalHooks>;
|
n8nExternalHooks?: PartialDeep<ExternalHooks>;
|
||||||
preventNodeViewBeforeUnload?: boolean;
|
preventNodeViewBeforeUnload?: boolean;
|
||||||
maxPinnedDataSize?: number;
|
maxPinnedDataSize?: number;
|
||||||
|
|
Loading…
Reference in a new issue