feat: introduce Sentry
This commit is contained in:
@@ -1,16 +1,15 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import logger from './logger';
|
||||
|
||||
type Error = {
|
||||
message: string;
|
||||
};
|
||||
import BaseError from '../errors/base';
|
||||
|
||||
const errorHandler = (err: Error, req: Request, res: Response): void => {
|
||||
// Do not remove `next` argument as the function signature will not fit for an error handler middleware
|
||||
const errorHandler = (err: BaseError, req: Request, res: Response, next: NextFunction): void => {
|
||||
if (err.message === 'Not Found') {
|
||||
res.status(404).end();
|
||||
} else {
|
||||
logger.error(err.message);
|
||||
res.status(500).end();
|
||||
res.status(err.statusCode || 500).send(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -4,8 +4,10 @@ import { loadSchemaSync } from '@graphql-tools/load';
|
||||
import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader';
|
||||
import { addResolversToSchema } from '@graphql-tools/schema';
|
||||
import { applyMiddleware } from 'graphql-middleware';
|
||||
|
||||
import logger from '../helpers/logger';
|
||||
import authentication from '../helpers/authentication';
|
||||
import * as Sentry from '../helpers/sentry.ee';
|
||||
import resolvers from '../graphql/resolvers';
|
||||
import HttpError from '../errors/http';
|
||||
|
||||
@@ -28,6 +30,15 @@ const graphQLInstance = graphqlHTTP({
|
||||
delete (error.originalError as HttpError).response;
|
||||
}
|
||||
|
||||
Sentry.captureException(error, {
|
||||
tags: { graphql: true },
|
||||
extra: {
|
||||
source: error.source?.body,
|
||||
positions: error.positions,
|
||||
path: error.path
|
||||
}
|
||||
})
|
||||
|
||||
return error;
|
||||
},
|
||||
});
|
||||
|
51
packages/backend/src/helpers/sentry.ee.ts
Normal file
51
packages/backend/src/helpers/sentry.ee.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { Express } from 'express';
|
||||
import * as Sentry from '@sentry/node';
|
||||
import type { CaptureContext } from '@sentry/types';
|
||||
import * as Tracing from '@sentry/tracing';
|
||||
|
||||
import appConfig from '../config/app';
|
||||
|
||||
export function init(app?: Express) {
|
||||
if (!appConfig.isCloud) return;
|
||||
|
||||
return Sentry.init({
|
||||
enabled: !!appConfig.sentryDsn,
|
||||
dsn: appConfig.sentryDsn,
|
||||
integrations: [
|
||||
app && new Sentry.Integrations.Http({ tracing: true }),
|
||||
app && new Tracing.Integrations.Express({ app }),
|
||||
app && new Tracing.Integrations.GraphQL(),
|
||||
].filter(Boolean),
|
||||
tracesSampleRate: 1.0,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export function attachRequestHandler(app: Express) {
|
||||
if (!appConfig.isCloud) return;
|
||||
|
||||
app.use(Sentry.Handlers.requestHandler());
|
||||
}
|
||||
|
||||
export function attachTracingHandler(app: Express) {
|
||||
if (!appConfig.isCloud) return;
|
||||
|
||||
app.use(Sentry.Handlers.tracingHandler());
|
||||
}
|
||||
|
||||
export function attachErrorHandler(app: Express) {
|
||||
if (!appConfig.isCloud) return;
|
||||
|
||||
app.use(Sentry.Handlers.errorHandler({
|
||||
shouldHandleError() {
|
||||
// TODO: narrow down the captured errors in time as we receive samples
|
||||
return true;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
export function captureException(exception: any, captureContext?: CaptureContext) {
|
||||
if (!appConfig.isCloud) return;
|
||||
|
||||
return Sentry.captureException(exception, captureContext);
|
||||
}
|
Reference in New Issue
Block a user