From 08cb963f43902e8dc276dcf9218f4f965851e6de Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Fri, 15 Nov 2024 13:10:19 +0000 Subject: [PATCH 1/2] feat(nestjs): Add alias `@SentryExceptionCaptured` for `@WithSentry` --- .../src/example-global.filter.ts | 4 +- packages/nestjs/README.md | 6 +- packages/nestjs/src/decorators.ts | 87 +++++++++++++++++++ packages/nestjs/src/decorators/sentry-cron.ts | 24 ----- .../nestjs/src/decorators/sentry-traced.ts | 34 -------- packages/nestjs/src/decorators/with-sentry.ts | 24 ----- packages/nestjs/src/index.ts | 9 +- 7 files changed, 98 insertions(+), 90 deletions(-) create mode 100644 packages/nestjs/src/decorators.ts delete mode 100644 packages/nestjs/src/decorators/sentry-cron.ts delete mode 100644 packages/nestjs/src/decorators/sentry-traced.ts delete mode 100644 packages/nestjs/src/decorators/with-sentry.ts diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-global.filter.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-global.filter.ts index cee50d0d2c7c..a2afcff4dc1b 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-global.filter.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules-decorator/src/example-global.filter.ts @@ -1,10 +1,10 @@ import { ArgumentsHost, BadRequestException, Catch, ExceptionFilter } from '@nestjs/common'; -import { WithSentry } from '@sentry/nestjs'; +import { SentryExceptionCaptured } from '@sentry/nestjs'; import { Request, Response } from 'express'; @Catch() export class ExampleWrappedGlobalFilter implements ExceptionFilter { - @WithSentry() + @SentryExceptionCaptured() catch(exception: BadRequestException, host: ArgumentsHost): void { const ctx = host.switchToHttp(); const response = ctx.getResponse(); diff --git a/packages/nestjs/README.md b/packages/nestjs/README.md index 0cdb832a75f6..011a74230eb3 100644 --- a/packages/nestjs/README.md +++ b/packages/nestjs/README.md @@ -72,8 +72,8 @@ export class AppModule {} In case you are using a global catch-all exception filter (which is either a filter registered with `app.useGlobalFilters()` or a filter registered in your app module providers annotated with an empty `@Catch()` -decorator), add a `@WithSentry()` decorator to the `catch()` method of this global error filter. This decorator will -report all unexpected errors that are received by your global error filter to Sentry: +decorator), add a `@SentryExceptionCaptured()` decorator to the `catch()` method of this global error filter. This +decorator will report all unexpected errors that are received by your global error filter to Sentry: ```typescript import { Catch, ExceptionFilter } from '@nestjs/common'; @@ -81,7 +81,7 @@ import { WithSentry } from '@sentry/nestjs'; @Catch() export class YourCatchAllExceptionFilter implements ExceptionFilter { - @WithSentry() + @SentryExceptionCaptured() catch(exception, host): void { // your implementation here } diff --git a/packages/nestjs/src/decorators.ts b/packages/nestjs/src/decorators.ts new file mode 100644 index 000000000000..62900e0defec --- /dev/null +++ b/packages/nestjs/src/decorators.ts @@ -0,0 +1,87 @@ +import * as Sentry from '@sentry/node'; +import type { MonitorConfig } from '@sentry/types'; +import { captureException } from '@sentry/core'; +import { isExpectedError } from './helpers'; +import { startSpan } from '@sentry/node'; + +/** + * A decorator wrapping the native nest Cron decorator, sending check-ins to Sentry. + */ +export const SentryCron = (monitorSlug: string, monitorConfig?: MonitorConfig): MethodDecorator => { + return (target: unknown, propertyKey, descriptor: PropertyDescriptor) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const originalMethod = descriptor.value as (...args: any[]) => Promise; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + descriptor.value = function (...args: any[]) { + return Sentry.withMonitor( + monitorSlug, + () => { + return originalMethod.apply(this, args); + }, + monitorConfig, + ); + }; + return descriptor; + }; +}; + +/** + * A decorator usable to wrap arbitrary functions with spans. + */ +export function SentryTraced(op: string = 'function') { + return function (target: unknown, propertyKey: string, descriptor: PropertyDescriptor) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const originalMethod = descriptor.value as (...args: any[]) => Promise | any; // function can be sync or async + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + descriptor.value = function (...args: any[]) { + return startSpan( + { + op: op, + name: propertyKey, + }, + () => { + return originalMethod.apply(this, args); + }, + ); + }; + + // preserve the original name on the decorated function + Object.defineProperty(descriptor.value, 'name', { + value: originalMethod.name, + configurable: true, + enumerable: true, + writable: true, + }); + + return descriptor; + }; +} + +/** + * A decorator to wrap user-defined exception filters and add Sentry error reporting. + */ +export function SentryExceptionCaptured() { + return function (target: unknown, propertyKey: string, descriptor: PropertyDescriptor) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const originalCatch = descriptor.value as (exception: unknown, host: unknown, ...args: any[]) => void; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + descriptor.value = function (exception: unknown, host: unknown, ...args: any[]) { + if (isExpectedError(exception)) { + return originalCatch.apply(this, [exception, host, ...args]); + } + + captureException(exception); + return originalCatch.apply(this, [exception, host, ...args]); + }; + + return descriptor; + }; +} + +/** + * A decorator to wrap user-defined exception filters and add Sentry error reporting. + */ +export const WithSentry = SentryExceptionCaptured; diff --git a/packages/nestjs/src/decorators/sentry-cron.ts b/packages/nestjs/src/decorators/sentry-cron.ts deleted file mode 100644 index 8cb86c6d66cc..000000000000 --- a/packages/nestjs/src/decorators/sentry-cron.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as Sentry from '@sentry/node'; -import type { MonitorConfig } from '@sentry/types'; - -/** - * A decorator wrapping the native nest Cron decorator, sending check-ins to Sentry. - */ -export const SentryCron = (monitorSlug: string, monitorConfig?: MonitorConfig): MethodDecorator => { - return (target: unknown, propertyKey, descriptor: PropertyDescriptor) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const originalMethod = descriptor.value as (...args: any[]) => Promise; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - descriptor.value = function (...args: any[]) { - return Sentry.withMonitor( - monitorSlug, - () => { - return originalMethod.apply(this, args); - }, - monitorConfig, - ); - }; - return descriptor; - }; -}; diff --git a/packages/nestjs/src/decorators/sentry-traced.ts b/packages/nestjs/src/decorators/sentry-traced.ts deleted file mode 100644 index 2f90e4dab5d9..000000000000 --- a/packages/nestjs/src/decorators/sentry-traced.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { startSpan } from '@sentry/node'; - -/** - * A decorator usable to wrap arbitrary functions with spans. - */ -export function SentryTraced(op: string = 'function') { - return function (target: unknown, propertyKey: string, descriptor: PropertyDescriptor) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const originalMethod = descriptor.value as (...args: any[]) => Promise | any; // function can be sync or async - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - descriptor.value = function (...args: any[]) { - return startSpan( - { - op: op, - name: propertyKey, - }, - () => { - return originalMethod.apply(this, args); - }, - ); - }; - - // preserve the original name on the decorated function - Object.defineProperty(descriptor.value, 'name', { - value: originalMethod.name, - configurable: true, - enumerable: true, - writable: true, - }); - - return descriptor; - }; -} diff --git a/packages/nestjs/src/decorators/with-sentry.ts b/packages/nestjs/src/decorators/with-sentry.ts deleted file mode 100644 index cf86ea6e7cc5..000000000000 --- a/packages/nestjs/src/decorators/with-sentry.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { captureException } from '@sentry/core'; -import { isExpectedError } from '../helpers'; - -/** - * A decorator to wrap user-defined exception filters and add Sentry error reporting. - */ -export function WithSentry() { - return function (target: unknown, propertyKey: string, descriptor: PropertyDescriptor) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const originalCatch = descriptor.value as (exception: unknown, host: unknown, ...args: any[]) => void; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - descriptor.value = function (exception: unknown, host: unknown, ...args: any[]) { - if (isExpectedError(exception)) { - return originalCatch.apply(this, [exception, host, ...args]); - } - - captureException(exception); - return originalCatch.apply(this, [exception, host, ...args]); - }; - - return descriptor; - }; -} diff --git a/packages/nestjs/src/index.ts b/packages/nestjs/src/index.ts index 71fb1ae4f78c..d99f491c1f6c 100644 --- a/packages/nestjs/src/index.ts +++ b/packages/nestjs/src/index.ts @@ -2,6 +2,9 @@ export * from '@sentry/node'; export { init } from './sdk'; -export { SentryTraced } from './decorators/sentry-traced'; -export { SentryCron } from './decorators/sentry-cron'; -export { WithSentry } from './decorators/with-sentry'; +export { + SentryTraced, + SentryCron, + WithSentry, + SentryExceptionCaptured, +} from './decorators'; From c36b7cbb4633bc7ddb6657612d3271cab9d6a41f Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Fri, 15 Nov 2024 13:17:37 +0000 Subject: [PATCH 2/2] lint --- packages/nestjs/src/decorators.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nestjs/src/decorators.ts b/packages/nestjs/src/decorators.ts index 62900e0defec..60e1049b3fd2 100644 --- a/packages/nestjs/src/decorators.ts +++ b/packages/nestjs/src/decorators.ts @@ -1,8 +1,8 @@ +import { captureException } from '@sentry/core'; import * as Sentry from '@sentry/node'; +import { startSpan } from '@sentry/node'; import type { MonitorConfig } from '@sentry/types'; -import { captureException } from '@sentry/core'; import { isExpectedError } from './helpers'; -import { startSpan } from '@sentry/node'; /** * A decorator wrapping the native nest Cron decorator, sending check-ins to Sentry.