mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
refactor(core): Consolidate path-related errors in Sentry (no-changelog) (#7757)
Keep reporting [path-related errors](https://n8nio.sentry.io/issues/4649493725) in Sentry but consolidate them in a single error group. Also, add `options.extra` as `meta` so they remain visible in debug logs: ``` 2023-11-24T11:50:54.852Z | error | ReportableError: Something went wrong "{ test: 123, file: 'LoggerProxy.js', function: 'exports.error' }" ``` --------- Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
This commit is contained in:
parent
5914e97e9a
commit
eec2ec1ff8
|
@ -1,6 +1,6 @@
|
||||||
import { createHash } from 'crypto';
|
import { createHash } from 'crypto';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import { ErrorReporterProxy, ExecutionBaseError } from 'n8n-workflow';
|
import { ErrorReporterProxy, ExecutionBaseError, ReportableError } from 'n8n-workflow';
|
||||||
|
|
||||||
let initialized = false;
|
let initialized = false;
|
||||||
|
|
||||||
|
@ -41,10 +41,19 @@ export const initErrorHandling = async () => {
|
||||||
addGlobalEventProcessor((event, { originalException }) => {
|
addGlobalEventProcessor((event, { originalException }) => {
|
||||||
if (originalException instanceof ExecutionBaseError && originalException.severity === 'warning')
|
if (originalException instanceof ExecutionBaseError && originalException.severity === 'warning')
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
if (originalException instanceof ReportableError) {
|
||||||
|
const { level, extra } = originalException;
|
||||||
|
if (level === 'warning') return null;
|
||||||
|
event.level = level;
|
||||||
|
if (extra) event.extra = { ...event.extra, ...extra };
|
||||||
|
}
|
||||||
|
|
||||||
if (!event.exception) return null;
|
if (!event.exception) return null;
|
||||||
const eventHash = createHash('sha1').update(JSON.stringify(event.exception)).digest('base64');
|
const eventHash = createHash('sha1').update(JSON.stringify(event.exception)).digest('base64');
|
||||||
if (seenErrors.has(eventHash)) return null;
|
if (seenErrors.has(eventHash)) return null;
|
||||||
seenErrors.add(eventHash);
|
seenErrors.add(eventHash);
|
||||||
|
|
||||||
return event;
|
return event;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
export class FileNotFoundError extends Error {
|
|
||||||
constructor(readonly filePath: string) {
|
|
||||||
super(`File not found: ${filePath}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class BinaryFileNotFoundError extends FileNotFoundError {}
|
|
||||||
|
|
||||||
export class InvalidPathError extends Error {
|
|
||||||
constructor(readonly filePath: string) {
|
|
||||||
super(`Invalid path detected: ${filePath}`);
|
|
||||||
}
|
|
||||||
}
|
|
21
packages/core/src/errors/filesystem.errors.ts
Normal file
21
packages/core/src/errors/filesystem.errors.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { ReportableError } from 'n8n-workflow';
|
||||||
|
|
||||||
|
abstract class FileSystemError extends ReportableError {
|
||||||
|
constructor(message: string, filePath: string) {
|
||||||
|
super(message, { extra: { filePath } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FileNotFoundError extends FileSystemError {
|
||||||
|
constructor(filePath: string) {
|
||||||
|
super('File not found', filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BinaryFileNotFoundError extends FileNotFoundError {}
|
||||||
|
|
||||||
|
export class InvalidPathError extends FileSystemError {
|
||||||
|
constructor(filePath: string) {
|
||||||
|
super('Invalid path detected', filePath);
|
||||||
|
}
|
||||||
|
}
|
1
packages/core/src/errors/index.ts
Normal file
1
packages/core/src/errors/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { BinaryFileNotFoundError, FileNotFoundError, InvalidPathError } from './filesystem.errors';
|
|
@ -1,5 +1,6 @@
|
||||||
import * as NodeExecuteFunctions from './NodeExecuteFunctions';
|
import * as NodeExecuteFunctions from './NodeExecuteFunctions';
|
||||||
|
|
||||||
|
export * from './errors';
|
||||||
export * from './ActiveWorkflows';
|
export * from './ActiveWorkflows';
|
||||||
export * from './BinaryData/BinaryData.service';
|
export * from './BinaryData/BinaryData.service';
|
||||||
export * from './BinaryData/types';
|
export * from './BinaryData/types';
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
import type { Primitives } from './utils';
|
|
||||||
import * as Logger from './LoggerProxy';
|
import * as Logger from './LoggerProxy';
|
||||||
|
import { ReportableError, type ReportingOptions } from './errors/reportable.error';
|
||||||
export interface ReportingOptions {
|
|
||||||
level?: 'warning' | 'error' | 'fatal';
|
|
||||||
tags?: Record<string, Primitives>;
|
|
||||||
extra?: Record<string, unknown>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ErrorReporter {
|
interface ErrorReporter {
|
||||||
report: (error: Error | string, options?: ReportingOptions) => void;
|
report: (error: Error | string, options?: ReportingOptions) => void;
|
||||||
|
@ -16,7 +10,8 @@ const instance: ErrorReporter = {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
let e = error;
|
let e = error;
|
||||||
do {
|
do {
|
||||||
Logger.error(`${e.constructor.name}: ${e.message}`);
|
const meta = e instanceof ReportableError ? e.extra : undefined;
|
||||||
|
Logger.error(`${e.constructor.name}: ${e.message}`, meta);
|
||||||
e = e.cause as Error;
|
e = e.cause as Error;
|
||||||
} while (e);
|
} while (e);
|
||||||
}
|
}
|
||||||
|
|
1
packages/workflow/src/errors/index.ts
Normal file
1
packages/workflow/src/errors/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { ReportableError } from './reportable.error';
|
24
packages/workflow/src/errors/reportable.error.ts
Normal file
24
packages/workflow/src/errors/reportable.error.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import type { Event } from '@sentry/node';
|
||||||
|
|
||||||
|
type Level = 'warning' | 'error' | 'fatal';
|
||||||
|
|
||||||
|
export type ReportingOptions = {
|
||||||
|
level?: Level;
|
||||||
|
} & Pick<Event, 'tags' | 'extra'>;
|
||||||
|
|
||||||
|
export type ReportableErrorOptions = Partial<ErrorOptions> & ReportingOptions;
|
||||||
|
|
||||||
|
export class ReportableError extends Error {
|
||||||
|
readonly level: Level;
|
||||||
|
|
||||||
|
readonly tags?: Event['tags'];
|
||||||
|
|
||||||
|
readonly extra?: Event['extra'];
|
||||||
|
|
||||||
|
constructor(message: string, { level, tags, extra, ...rest }: ReportableErrorOptions) {
|
||||||
|
super(message, rest);
|
||||||
|
this.level = level ?? 'error';
|
||||||
|
this.tags = tags;
|
||||||
|
this.extra = extra;
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import * as NodeHelpers from './NodeHelpers';
|
||||||
import * as ObservableObject from './ObservableObject';
|
import * as ObservableObject from './ObservableObject';
|
||||||
import * as TelemetryHelpers from './TelemetryHelpers';
|
import * as TelemetryHelpers from './TelemetryHelpers';
|
||||||
|
|
||||||
|
export * from './errors';
|
||||||
export * from './Authentication';
|
export * from './Authentication';
|
||||||
export * from './Constants';
|
export * from './Constants';
|
||||||
export * from './Cron';
|
export * from './Cron';
|
||||||
|
|
Loading…
Reference in a new issue