twenty/packages/twenty-server/src/integrations/exception-handler/drivers/sentry.driver.ts
Jérémy M cdc51add7d
feat: add user to sentry (#3467)
* feat: wip add user to sentry

* feat: wip interceptor

* feat: wip add user to sentry

* feat: add user into sentry errors

* fix: hide stack trace in production

* fix: properly log commands and handle exceptions

* fix: filter command exceptions

* feat: handle jobs errors
2024-02-01 16:14:08 +01:00

101 lines
2.8 KiB
TypeScript

import * as Sentry from '@sentry/node';
import { ProfilingIntegration } from '@sentry/profiling-node';
import { ExceptionHandlerUser } from 'src/integrations/exception-handler/interfaces/exception-handler-user.interface';
import { ExceptionHandlerOptions } from 'src/integrations/exception-handler/interfaces/exception-handler-options.interface';
import {
ExceptionHandlerDriverInterface,
ExceptionHandlerSentryDriverFactoryOptions,
} from 'src/integrations/exception-handler/interfaces';
export class ExceptionHandlerSentryDriver
implements ExceptionHandlerDriverInterface
{
constructor(options: ExceptionHandlerSentryDriverFactoryOptions['options']) {
Sentry.init({
dsn: options.dsn,
integrations: [
// enable HTTP calls tracing
new Sentry.Integrations.Http({ tracing: true }),
// enable Express.js middleware tracing
new Sentry.Integrations.Express({ app: options.serverInstance }),
new Sentry.Integrations.GraphQL(),
new Sentry.Integrations.Postgres({
usePgNative: true,
}),
new ProfilingIntegration(),
],
tracesSampleRate: 1.0,
profilesSampleRate: 1.0,
environment: options.debug ? 'development' : 'production',
debug: options.debug,
});
}
captureExceptions(
exceptions: ReadonlyArray<any>,
options?: ExceptionHandlerOptions,
) {
const eventIds: string[] = [];
Sentry.withScope((scope) => {
if (options?.operation) {
scope.setTag('operation', options.operation.name);
scope.setTag('operationName', options.operation.name);
}
if (options?.document) {
scope.setExtra('document', options.document);
}
for (const exception of exceptions) {
const errorPath = (exception.path ?? [])
.map((v: string | number) => (typeof v === 'number' ? '$index' : v))
.join(' > ');
if (errorPath) {
scope.addBreadcrumb({
category: 'execution-path',
message: errorPath,
level: 'debug',
});
}
const eventId = Sentry.captureException(exception, {
fingerprint: [
'graphql',
errorPath,
options?.operation?.name,
options?.operation?.type,
],
contexts: {
GraphQL: {
operationName: options?.operation?.name,
operationType: options?.operation?.type,
},
},
});
eventIds.push(eventId);
}
});
return eventIds;
}
captureMessage(message: string, user?: ExceptionHandlerUser) {
Sentry.captureMessage(message, (scope) => {
if (user) {
scope.setUser({
id: user.id,
ip_address: user.ipAddress,
email: user.email,
username: user.username,
});
}
return scope;
});
}
}