mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-13 16:14:07 -08:00
Rename to ErrorReporter and improve code
This commit is contained in:
parent
e795d0bae7
commit
4fceebf8c2
|
@ -35,10 +35,10 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@n8n/config": "workspace:*",
|
||||
"acorn": "8.14.0",
|
||||
"acorn-walk": "8.3.4",
|
||||
"@sentry/integrations": "catalog:",
|
||||
"@sentry/node": "catalog:",
|
||||
"acorn": "8.14.0",
|
||||
"acorn-walk": "8.3.4",
|
||||
"n8n-core": "workspace:*",
|
||||
"n8n-workflow": "workspace:*",
|
||||
"nanoid": "^3.3.6",
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import { mock } from 'jest-mock-extended';
|
||||
import { ApplicationError } from 'n8n-workflow';
|
||||
|
||||
import { ErrorReporter } from '../error-reporter';
|
||||
|
||||
describe('ErrorReporter', () => {
|
||||
const errorReporting = new ErrorReporter(mock());
|
||||
|
||||
describe('beforeSend', () => {
|
||||
it('should return null if originalException is an ApplicationError with level warning', () => {
|
||||
const hint = { originalException: new ApplicationError('Test error', { level: 'warning' }) };
|
||||
expect(errorReporting.beforeSend(mock(), hint)).toBeNull();
|
||||
});
|
||||
|
||||
it('should return event if originalException is an ApplicationError with level error', () => {
|
||||
const hint = { originalException: new ApplicationError('Test error', { level: 'error' }) };
|
||||
expect(errorReporting.beforeSend(mock(), hint)).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should return null if originalException is an Error with a non-unique stack', () => {
|
||||
const hint = { originalException: new Error('Test error') };
|
||||
errorReporting.beforeSend(mock(), hint);
|
||||
expect(errorReporting.beforeSend(mock(), hint)).toBeNull();
|
||||
});
|
||||
|
||||
it('should return event if originalException is an Error with a unique stack', () => {
|
||||
const hint = { originalException: new Error('Test error') };
|
||||
expect(errorReporting.beforeSend(mock(), hint)).not.toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,5 +1,6 @@
|
|||
import { RewriteFrames } from '@sentry/integrations';
|
||||
import { init, setTag, captureException, close } from '@sentry/node';
|
||||
import type { ErrorEvent, EventHint } from '@sentry/types';
|
||||
import * as a from 'assert/strict';
|
||||
import { createHash } from 'crypto';
|
||||
import { ApplicationError } from 'n8n-workflow';
|
||||
|
@ -9,9 +10,12 @@ import type { SentryConfig } from '@/config/sentry-config';
|
|||
/**
|
||||
* Handles error reporting using Sentry
|
||||
*/
|
||||
export class ErrorReporting {
|
||||
export class ErrorReporter {
|
||||
private isInitialized = false;
|
||||
|
||||
/** Hashes of error stack traces, to deduplicate error reports. */
|
||||
private readonly seenErrors = new Set<string>();
|
||||
|
||||
private get dsn() {
|
||||
return this.sentryConfig.sentryDsn;
|
||||
}
|
||||
|
@ -37,7 +41,8 @@ export class ErrorReporting {
|
|||
'OnUnhandledRejection',
|
||||
'ContextLines',
|
||||
];
|
||||
const seenErrors = new Set<string>();
|
||||
|
||||
setTag('server_type', 'task_runner');
|
||||
|
||||
init({
|
||||
dsn: this.dsn,
|
||||
|
@ -46,37 +51,13 @@ export class ErrorReporting {
|
|||
enableTracing: false,
|
||||
serverName: this.sentryConfig.deploymentName,
|
||||
beforeBreadcrumb: () => null,
|
||||
beforeSend: (event, hint) => this.beforeSend(event, hint),
|
||||
integrations: (integrations) => [
|
||||
...integrations.filter(({ name }) => enabledIntegrations.includes(name)),
|
||||
new RewriteFrames({ root: process.cwd() }),
|
||||
],
|
||||
async beforeSend(event, { originalException }) {
|
||||
if (!originalException) return null;
|
||||
|
||||
if (originalException instanceof Promise) {
|
||||
originalException = await originalException.catch((error) => error as Error);
|
||||
}
|
||||
|
||||
if (originalException instanceof ApplicationError) {
|
||||
const { level, extra, tags } = originalException;
|
||||
if (level === 'warning') return null;
|
||||
event.level = level;
|
||||
if (extra) event.extra = { ...event.extra, ...extra };
|
||||
if (tags) event.tags = { ...event.tags, ...tags };
|
||||
}
|
||||
|
||||
if (originalException instanceof Error && originalException.stack) {
|
||||
const eventHash = createHash('sha1').update(originalException.stack).digest('base64');
|
||||
if (seenErrors.has(eventHash)) return null;
|
||||
seenErrors.add(eventHash);
|
||||
}
|
||||
|
||||
return event;
|
||||
},
|
||||
});
|
||||
|
||||
setTag('server_type', 'task_runner');
|
||||
|
||||
this.isInitialized = true;
|
||||
}
|
||||
|
||||
|
@ -87,4 +68,24 @@ export class ErrorReporting {
|
|||
|
||||
await close(1000);
|
||||
}
|
||||
|
||||
beforeSend(event: ErrorEvent, { originalException }: EventHint) {
|
||||
if (!originalException) return null;
|
||||
|
||||
if (originalException instanceof ApplicationError) {
|
||||
const { level, extra, tags } = originalException;
|
||||
if (level === 'warning') return null;
|
||||
event.level = level;
|
||||
if (extra) event.extra = { ...event.extra, ...extra };
|
||||
if (tags) event.tags = { ...event.tags, ...tags };
|
||||
}
|
||||
|
||||
if (originalException instanceof Error && originalException.stack) {
|
||||
const eventHash = createHash('sha1').update(originalException.stack).digest('base64');
|
||||
if (this.seenErrors.has(eventHash)) return null;
|
||||
this.seenErrors.add(eventHash);
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
}
|
|
@ -2,12 +2,12 @@ import { ensureError } from 'n8n-workflow';
|
|||
import Container from 'typedi';
|
||||
|
||||
import { MainConfig } from './config/main-config';
|
||||
import type { ErrorReporting } from './error-reporting';
|
||||
import type { ErrorReporter } from './error-reporter';
|
||||
import { JsTaskRunner } from './js-task-runner/js-task-runner';
|
||||
|
||||
let runner: JsTaskRunner | undefined;
|
||||
let isShuttingDown = false;
|
||||
let errorReporting: ErrorReporting | undefined;
|
||||
let errorReporter: ErrorReporter | undefined;
|
||||
|
||||
function createSignalHandler(signal: string) {
|
||||
return async function onSignal() {
|
||||
|
@ -24,9 +24,9 @@ function createSignalHandler(signal: string) {
|
|||
runner = undefined;
|
||||
}
|
||||
|
||||
if (errorReporting) {
|
||||
await errorReporting.stop();
|
||||
errorReporting = undefined;
|
||||
if (errorReporter) {
|
||||
await errorReporter.stop();
|
||||
errorReporter = undefined;
|
||||
}
|
||||
} catch (e) {
|
||||
const error = ensureError(e);
|
||||
|
@ -42,9 +42,9 @@ void (async function start() {
|
|||
const config = Container.get(MainConfig);
|
||||
|
||||
if (config.sentryConfig.sentryDsn) {
|
||||
const { ErrorReporting } = await import('@/error-reporting');
|
||||
errorReporting = new ErrorReporting(config.sentryConfig);
|
||||
await errorReporting.start();
|
||||
const { ErrorReporter } = await import('@/error-reporter');
|
||||
errorReporter = new ErrorReporter(config.sentryConfig);
|
||||
await errorReporter.start();
|
||||
}
|
||||
|
||||
runner = new JsTaskRunner(config);
|
||||
|
|
Loading…
Reference in a new issue