From dd2ef7ea9957a80d20169163f1b91d6afaca08c2 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Tue, 2 Jul 2024 13:13:30 +0200 Subject: [PATCH 01/18] Add span decorator to nestjs --- packages/nestjs/src/index.ts | 2 ++ packages/nestjs/src/span-decorator.ts | 28 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 packages/nestjs/src/span-decorator.ts diff --git a/packages/nestjs/src/index.ts b/packages/nestjs/src/index.ts index 6ac8d97b4241..b72e5f193535 100644 --- a/packages/nestjs/src/index.ts +++ b/packages/nestjs/src/index.ts @@ -1,3 +1,5 @@ export * from '@sentry/node'; export { init } from './sdk'; + +export * from './span-decorator'; diff --git a/packages/nestjs/src/span-decorator.ts b/packages/nestjs/src/span-decorator.ts new file mode 100644 index 000000000000..f9663357926a --- /dev/null +++ b/packages/nestjs/src/span-decorator.ts @@ -0,0 +1,28 @@ +import { captureException, startSpan } from '@sentry/node'; + +/** + * A decorator usable to wrap arbitrary functions with spans. + */ +export function GetSentrySpan() { + 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; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + descriptor.value = async function (...args: any[]) { + await startSpan( + { + name: propertyKey, + }, + async () => { + try { + return await originalMethod.apply(this, args); + } catch (e) { + captureException(e); + } + }, + ); + }; + return descriptor; + }; +} From 7d802e0b34c81fcac3f3bcff56fc012ef53fc2c8 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Tue, 2 Jul 2024 14:02:26 +0200 Subject: [PATCH 02/18] Set op for decorator traces --- packages/nestjs/src/span-decorator.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/nestjs/src/span-decorator.ts b/packages/nestjs/src/span-decorator.ts index f9663357926a..2f1885b79a35 100644 --- a/packages/nestjs/src/span-decorator.ts +++ b/packages/nestjs/src/span-decorator.ts @@ -12,6 +12,7 @@ export function GetSentrySpan() { descriptor.value = async function (...args: any[]) { await startSpan( { + op: 'function', name: propertyKey, }, async () => { From 326969b1a3d43c5f972ba8f60b3a2be441fa8a8e Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Wed, 3 Jul 2024 14:26:56 +0200 Subject: [PATCH 03/18] Allow users to define op name --- packages/nestjs/src/span-decorator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nestjs/src/span-decorator.ts b/packages/nestjs/src/span-decorator.ts index 2f1885b79a35..e5d63d30b247 100644 --- a/packages/nestjs/src/span-decorator.ts +++ b/packages/nestjs/src/span-decorator.ts @@ -3,7 +3,7 @@ import { captureException, startSpan } from '@sentry/node'; /** * A decorator usable to wrap arbitrary functions with spans. */ -export function GetSentrySpan() { +export function GetSentrySpan(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; @@ -12,7 +12,7 @@ export function GetSentrySpan() { descriptor.value = async function (...args: any[]) { await startSpan( { - op: 'function', + op: op, name: propertyKey, }, async () => { From 9afd76f05cda3f587e319736b65d42dbb033b384 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Wed, 3 Jul 2024 14:51:44 +0200 Subject: [PATCH 04/18] Add e2e test --- .../nestjs/src/app.controller.ts | 5 +++ .../nestjs/src/app.service.ts | 12 +++++++ .../nestjs/tests/span-decorator.test.ts | 33 +++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts index 154f62ada912..7598bd9dbe47 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts @@ -69,6 +69,11 @@ export class AppController1 { async testOutgoingHttpExternalDisallowed() { return this.appService.testOutgoingHttpExternalDisallowed(); } + + @Get('test-span-decorator') + async testSpanDecorator() { + return this.appService.testSpanDecorator(); + } } @Controller() diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts index 1103c65941a1..baba573fe0bc 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts @@ -1,6 +1,7 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import * as Sentry from '@sentry/nestjs'; import { makeHttpRequest } from './utils'; +import { GetSentrySpan } from '@sentry/nestjs'; @Injectable() export class AppService1 { @@ -75,6 +76,17 @@ export class AppService1 { async testOutgoingHttpExternalDisallowed() { return makeHttpRequest('http://localhost:3040/external-disallowed'); } + + @GetSentrySpan('wait function') + async wait() { + console.log("INSIDE WAIT"); + return new Promise((resolve) => setTimeout(resolve, 500)); + } + + async testSpanDecorator() { + console.log("INSIDE TEST SPAN DECORATOR"); + await this.wait(); + } } @Injectable() diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts b/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts new file mode 100644 index 000000000000..0ffb9e001240 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts @@ -0,0 +1,33 @@ +import { expect, test } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/test-utils'; + +test('Transaction includes span for decorated function', async ({ baseURL }) => { + const transactionEventPromise = waitForTransaction('nestjs', transactionEvent => { + return ( + transactionEvent?.contexts?.trace?.op === 'http.server' && + transactionEvent?.transaction === 'GET /test-span-decorator' + ); + }); + + await fetch(`${baseURL}/test-span-decorator`); + + const transactionEvent = await transactionEventPromise; + + console.log(transactionEvent); + + expect(transactionEvent.spans).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + span_id: expect.any(String), + trace_id: expect.any(String), + data: expect.any(Object), + description: 'wait', + parent_span_id: expect.any(String), + start_timestamp: expect.any(Number), + status: 'ok', + op: 'wait function', + origin: 'manual', + }), + ]), + ); +}); From cef0a49642c5b056045a3b39369485aff2a8f16a Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Wed, 3 Jul 2024 15:30:46 +0200 Subject: [PATCH 05/18] Lint --- .../e2e-tests/test-applications/nestjs/src/app.service.ts | 8 ++++---- packages/nestjs/src/span-decorator.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts index baba573fe0bc..958f71fb18af 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts @@ -1,7 +1,7 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import * as Sentry from '@sentry/nestjs'; -import { makeHttpRequest } from './utils'; import { GetSentrySpan } from '@sentry/nestjs'; +import { makeHttpRequest } from './utils'; @Injectable() export class AppService1 { @@ -79,12 +79,12 @@ export class AppService1 { @GetSentrySpan('wait function') async wait() { - console.log("INSIDE WAIT"); - return new Promise((resolve) => setTimeout(resolve, 500)); + console.log('INSIDE WAIT'); + return new Promise(resolve => setTimeout(resolve, 500)); } async testSpanDecorator() { - console.log("INSIDE TEST SPAN DECORATOR"); + console.log('INSIDE TEST SPAN DECORATOR'); await this.wait(); } } diff --git a/packages/nestjs/src/span-decorator.ts b/packages/nestjs/src/span-decorator.ts index e5d63d30b247..05699faadf94 100644 --- a/packages/nestjs/src/span-decorator.ts +++ b/packages/nestjs/src/span-decorator.ts @@ -3,7 +3,7 @@ import { captureException, startSpan } from '@sentry/node'; /** * A decorator usable to wrap arbitrary functions with spans. */ -export function GetSentrySpan(op : string = 'function') { +export function GetSentrySpan(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; From b297c4157e9e2df7b782af3782e70bdb5f2682f2 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Wed, 3 Jul 2024 15:53:15 +0200 Subject: [PATCH 06/18] Update README --- packages/nestjs/README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/nestjs/README.md b/packages/nestjs/README.md index 58ab6bc95372..348d7f8c0e7d 100644 --- a/packages/nestjs/README.md +++ b/packages/nestjs/README.md @@ -38,6 +38,23 @@ Sentry.init({ Note that it is necessary to initialize Sentry **before you import any package that may be instrumented by us**. +## Span Decorator + +Use the @GetSentrySpan() decorator to gain additional performance insights for any function within your NestJS application. + +```js +import { Injectable } from '@nestjs/common'; +import { GetSentrySpan } from '@sentry/nestjs'; + +@Injectable() +export class ExampleService { + @GetSentrySpan() + async performTask() { + // Your business logic here + } +} +``` + ## Links - [Official SDK Docs](https://docs.sentry.io/platforms/javascript/guides/nestjs/) From 2b4c8b17e7f52b1e6b750cb55d40031f9c6fdbce Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Wed, 3 Jul 2024 15:56:54 +0200 Subject: [PATCH 07/18] . --- .../e2e-tests/test-applications/nestjs/src/app.service.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts index 958f71fb18af..091f5c7ca2d6 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts @@ -79,12 +79,10 @@ export class AppService1 { @GetSentrySpan('wait function') async wait() { - console.log('INSIDE WAIT'); return new Promise(resolve => setTimeout(resolve, 500)); } async testSpanDecorator() { - console.log('INSIDE TEST SPAN DECORATOR'); await this.wait(); } } From b344930c51860dc4cb9be4120fea6512959c62e4 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Wed, 3 Jul 2024 16:00:03 +0200 Subject: [PATCH 08/18] Lint --- packages/nestjs/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/nestjs/README.md b/packages/nestjs/README.md index 348d7f8c0e7d..722d9c2f9807 100644 --- a/packages/nestjs/README.md +++ b/packages/nestjs/README.md @@ -40,7 +40,8 @@ Note that it is necessary to initialize Sentry **before you import any package t ## Span Decorator -Use the @GetSentrySpan() decorator to gain additional performance insights for any function within your NestJS application. +Use the @GetSentrySpan() decorator to gain additional performance insights for any function within your NestJS +application. ```js import { Injectable } from '@nestjs/common'; From f4c9618807af288a90ff22eaa3fd5d0c651dc7c1 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Wed, 3 Jul 2024 16:29:11 +0200 Subject: [PATCH 09/18] Rename decorator --- .../e2e-tests/test-applications/nestjs/src/app.service.ts | 4 ++-- packages/nestjs/README.md | 6 +++--- packages/nestjs/src/span-decorator.ts | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts index 091f5c7ca2d6..95bdd4fc16bd 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts @@ -1,6 +1,6 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import * as Sentry from '@sentry/nestjs'; -import { GetSentrySpan } from '@sentry/nestjs'; +import { SentryTraced } from '@sentry/nestjs'; import { makeHttpRequest } from './utils'; @Injectable() @@ -77,7 +77,7 @@ export class AppService1 { return makeHttpRequest('http://localhost:3040/external-disallowed'); } - @GetSentrySpan('wait function') + @SentryTraced('wait function') async wait() { return new Promise(resolve => setTimeout(resolve, 500)); } diff --git a/packages/nestjs/README.md b/packages/nestjs/README.md index 722d9c2f9807..271669a52f1c 100644 --- a/packages/nestjs/README.md +++ b/packages/nestjs/README.md @@ -40,16 +40,16 @@ Note that it is necessary to initialize Sentry **before you import any package t ## Span Decorator -Use the @GetSentrySpan() decorator to gain additional performance insights for any function within your NestJS +Use the @SentryTraced() decorator to gain additional performance insights for any function within your NestJS application. ```js import { Injectable } from '@nestjs/common'; -import { GetSentrySpan } from '@sentry/nestjs'; +import { SentryTraced } from '@sentry/nestjs'; @Injectable() export class ExampleService { - @GetSentrySpan() + @SentryTraced() async performTask() { // Your business logic here } diff --git a/packages/nestjs/src/span-decorator.ts b/packages/nestjs/src/span-decorator.ts index 05699faadf94..d045056c3be3 100644 --- a/packages/nestjs/src/span-decorator.ts +++ b/packages/nestjs/src/span-decorator.ts @@ -3,7 +3,7 @@ import { captureException, startSpan } from '@sentry/node'; /** * A decorator usable to wrap arbitrary functions with spans. */ -export function GetSentrySpan(op: string = 'function') { +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; From 059705a7e27fcce2a7d9a264774b5b1b14356cd7 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Wed, 3 Jul 2024 16:30:11 +0200 Subject: [PATCH 10/18] . --- .../test-applications/nestjs/tests/span-decorator.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts b/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts index 0ffb9e001240..bda91b197253 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts @@ -13,8 +13,6 @@ test('Transaction includes span for decorated function', async ({ baseURL }) => const transactionEvent = await transactionEventPromise; - console.log(transactionEvent); - expect(transactionEvent.spans).toEqual( expect.arrayContaining([ expect.objectContaining({ From d38d71224937630466e5fa8b6cb7626cc469e929 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Wed, 3 Jul 2024 16:35:59 +0200 Subject: [PATCH 11/18] Update README --- packages/nestjs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nestjs/README.md b/packages/nestjs/README.md index 271669a52f1c..8928327b1470 100644 --- a/packages/nestjs/README.md +++ b/packages/nestjs/README.md @@ -49,7 +49,7 @@ import { SentryTraced } from '@sentry/nestjs'; @Injectable() export class ExampleService { - @SentryTraced() + @SentryTraced('example function') async performTask() { // Your business logic here } From 30f1dc37aade8cf35fe438990a425435cafcd9f1 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Thu, 4 Jul 2024 12:58:12 +0200 Subject: [PATCH 12/18] Explicit exports in index file --- packages/nestjs/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nestjs/src/index.ts b/packages/nestjs/src/index.ts index b72e5f193535..668187a21e29 100644 --- a/packages/nestjs/src/index.ts +++ b/packages/nestjs/src/index.ts @@ -2,4 +2,4 @@ export * from '@sentry/node'; export { init } from './sdk'; -export * from './span-decorator'; +export { SentryTraced } from './span-decorator'; From 394b3247ffd11138f7691a70517bb77f05e4bc50 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Thu, 4 Jul 2024 13:02:11 +0200 Subject: [PATCH 13/18] Do not capture exceptions thrown in decorator in sentry --- packages/nestjs/src/span-decorator.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/nestjs/src/span-decorator.ts b/packages/nestjs/src/span-decorator.ts index d045056c3be3..f6d3efd230a2 100644 --- a/packages/nestjs/src/span-decorator.ts +++ b/packages/nestjs/src/span-decorator.ts @@ -1,4 +1,4 @@ -import { captureException, startSpan } from '@sentry/node'; +import { startSpan } from '@sentry/node'; /** * A decorator usable to wrap arbitrary functions with spans. @@ -16,11 +16,7 @@ export function SentryTraced(op: string = 'function') { name: propertyKey, }, async () => { - try { - return await originalMethod.apply(this, args); - } catch (e) { - captureException(e); - } + return originalMethod.apply(this, args); }, ); }; From 4d16c2b4cce44aaa718a74d53c4b9b5e4fae3feb Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Thu, 4 Jul 2024 13:10:55 +0200 Subject: [PATCH 14/18] Explicitly check data in test --- .../test-applications/nestjs/tests/span-decorator.test.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts b/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts index bda91b197253..e4dc9a15aa21 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts @@ -18,7 +18,11 @@ test('Transaction includes span for decorated function', async ({ baseURL }) => expect.objectContaining({ span_id: expect.any(String), trace_id: expect.any(String), - data: expect.any(Object), + data: { + 'sentry.origin': 'manual', + 'sentry.op': 'wait function', + 'otel.kind': 'INTERNAL', + }, description: 'wait', parent_span_id: expect.any(String), start_timestamp: expect.any(Number), From 6b588cf889d0e1703aad950ab5674304cbad3ca3 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Thu, 4 Jul 2024 13:27:58 +0200 Subject: [PATCH 15/18] rename test route --- .../e2e-tests/test-applications/nestjs/src/app.controller.ts | 4 ++-- .../e2e-tests/test-applications/nestjs/src/app.service.ts | 2 +- .../test-applications/nestjs/tests/span-decorator.test.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts index 7598bd9dbe47..85ad04b49718 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts @@ -70,9 +70,9 @@ export class AppController1 { return this.appService.testOutgoingHttpExternalDisallowed(); } - @Get('test-span-decorator') + @Get('test-span-decorator-async') async testSpanDecorator() { - return this.appService.testSpanDecorator(); + return this.appService.testSpanDecoratorAsync(); } } diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts index 95bdd4fc16bd..75f0b55a0626 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts @@ -82,7 +82,7 @@ export class AppService1 { return new Promise(resolve => setTimeout(resolve, 500)); } - async testSpanDecorator() { + async testSpanDecoratorAsync() { await this.wait(); } } diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts b/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts index e4dc9a15aa21..fb26d74b2c0d 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts @@ -5,11 +5,11 @@ test('Transaction includes span for decorated function', async ({ baseURL }) => const transactionEventPromise = waitForTransaction('nestjs', transactionEvent => { return ( transactionEvent?.contexts?.trace?.op === 'http.server' && - transactionEvent?.transaction === 'GET /test-span-decorator' + transactionEvent?.transaction === 'GET /test-span-decorator-async' ); }); - await fetch(`${baseURL}/test-span-decorator`); + await fetch(`${baseURL}/test-span-decorator-async`); const transactionEvent = await transactionEventPromise; From 322c645e3d71e59c1fa8ac744c1681fea3491f9a Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Thu, 4 Jul 2024 14:18:40 +0200 Subject: [PATCH 16/18] Add sync span decorator test --- .../nestjs/src/app.controller.ts | 7 +++- .../nestjs/src/app.service.ts | 9 ++++ .../nestjs/tests/span-decorator.test.ts | 41 ++++++++++++++++++- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts index 85ad04b49718..8bd641b6f253 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts @@ -71,9 +71,14 @@ export class AppController1 { } @Get('test-span-decorator-async') - async testSpanDecorator() { + async testSpanDecoratorAsync() { return this.appService.testSpanDecoratorAsync(); } + + @Get('test-span-decorator-sync') + async testSpanDecoratorSync() { + return { "result": await this.appService.testSpanDecoratorSync() }; + } } @Controller() diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts index 75f0b55a0626..72f6823b59ce 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts @@ -85,6 +85,15 @@ export class AppService1 { async testSpanDecoratorAsync() { await this.wait(); } + + @SentryTraced('return a string') + getString(): string { + return "test"; + } + + async testSpanDecoratorSync() { + return this.getString(); + } } @Injectable() diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts b/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts index fb26d74b2c0d..eadb531a8b6f 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts @@ -1,7 +1,7 @@ import { expect, test } from '@playwright/test'; import { waitForTransaction } from '@sentry-internal/test-utils'; -test('Transaction includes span for decorated function', async ({ baseURL }) => { +test('Transaction includes span for decorated async function', async ({ baseURL }) => { const transactionEventPromise = waitForTransaction('nestjs', transactionEvent => { return ( transactionEvent?.contexts?.trace?.op === 'http.server' && @@ -33,3 +33,42 @@ test('Transaction includes span for decorated function', async ({ baseURL }) => ]), ); }); + +test('Transaction includes span and correct value for decorated sync function', async ({ baseURL }) => { + const transactionEventPromise = waitForTransaction('nestjs', transactionEvent => { + return ( + transactionEvent?.contexts?.trace?.op === 'http.server' && + transactionEvent?.transaction === 'GET /test-span-decorator-sync' + ); + }); + + const response = await fetch(`${baseURL}/test-span-decorator-sync`); + const body = await response.json(); + + expect(body.result).toEqual('test'); + + const transactionEvent = await transactionEventPromise; + + console.log(transactionEvent); + + expect(transactionEvent.spans).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + span_id: expect.any(String), + trace_id: expect.any(String), + data: { + 'sentry.origin': 'manual', + 'sentry.op': 'return a string', + 'otel.kind': 'INTERNAL', + }, + description: 'getString', + parent_span_id: expect.any(String), + start_timestamp: expect.any(Number), + status: 'ok', + op: 'return a string', + origin: 'manual', + }), + ]), + ); +}); + From 17739bb232ea8174d37167ef7b50f30c6f20127c Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Thu, 4 Jul 2024 14:24:10 +0200 Subject: [PATCH 17/18] Fix e2e tests --- packages/nestjs/src/span-decorator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nestjs/src/span-decorator.ts b/packages/nestjs/src/span-decorator.ts index f6d3efd230a2..c56056a26621 100644 --- a/packages/nestjs/src/span-decorator.ts +++ b/packages/nestjs/src/span-decorator.ts @@ -9,8 +9,8 @@ export function SentryTraced(op: string = 'function') { const originalMethod = descriptor.value as (...args: any[]) => Promise; // eslint-disable-next-line @typescript-eslint/no-explicit-any - descriptor.value = async function (...args: any[]) { - await startSpan( + descriptor.value = function (...args: any[]) { + return startSpan( { op: op, name: propertyKey, From e4ffdfd9493fbb1a7d34d3a8004906e9d7af3240 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Thu, 4 Jul 2024 14:43:23 +0200 Subject: [PATCH 18/18] Check return value in async test --- .../test-applications/nestjs/src/app.controller.ts | 4 ++-- .../test-applications/nestjs/src/app.service.ts | 9 +++++---- .../nestjs/tests/span-decorator.test.ts | 14 +++++++------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts index 8bd641b6f253..5ba6bcb2a68e 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts @@ -72,12 +72,12 @@ export class AppController1 { @Get('test-span-decorator-async') async testSpanDecoratorAsync() { - return this.appService.testSpanDecoratorAsync(); + return { result: await this.appService.testSpanDecoratorAsync() }; } @Get('test-span-decorator-sync') async testSpanDecoratorSync() { - return { "result": await this.appService.testSpanDecoratorSync() }; + return { result: await this.appService.testSpanDecoratorSync() }; } } diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts index 72f6823b59ce..7e0df6b7e1c8 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts @@ -77,18 +77,19 @@ export class AppService1 { return makeHttpRequest('http://localhost:3040/external-disallowed'); } - @SentryTraced('wait function') + @SentryTraced('wait and return a string') async wait() { - return new Promise(resolve => setTimeout(resolve, 500)); + await new Promise(resolve => setTimeout(resolve, 500)); + return 'test'; } async testSpanDecoratorAsync() { - await this.wait(); + return await this.wait(); } @SentryTraced('return a string') getString(): string { - return "test"; + return 'test'; } async testSpanDecoratorSync() { diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts b/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts index eadb531a8b6f..3efdfb979d73 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts @@ -1,7 +1,7 @@ import { expect, test } from '@playwright/test'; import { waitForTransaction } from '@sentry-internal/test-utils'; -test('Transaction includes span for decorated async function', async ({ baseURL }) => { +test('Transaction includes span and correct value for decorated async function', async ({ baseURL }) => { const transactionEventPromise = waitForTransaction('nestjs', transactionEvent => { return ( transactionEvent?.contexts?.trace?.op === 'http.server' && @@ -9,7 +9,10 @@ test('Transaction includes span for decorated async function', async ({ baseURL ); }); - await fetch(`${baseURL}/test-span-decorator-async`); + const response = await fetch(`${baseURL}/test-span-decorator-async`); + const body = await response.json(); + + expect(body.result).toEqual('test'); const transactionEvent = await transactionEventPromise; @@ -20,14 +23,14 @@ test('Transaction includes span for decorated async function', async ({ baseURL trace_id: expect.any(String), data: { 'sentry.origin': 'manual', - 'sentry.op': 'wait function', + 'sentry.op': 'wait and return a string', 'otel.kind': 'INTERNAL', }, description: 'wait', parent_span_id: expect.any(String), start_timestamp: expect.any(Number), status: 'ok', - op: 'wait function', + op: 'wait and return a string', origin: 'manual', }), ]), @@ -49,8 +52,6 @@ test('Transaction includes span and correct value for decorated sync function', const transactionEvent = await transactionEventPromise; - console.log(transactionEvent); - expect(transactionEvent.spans).toEqual( expect.arrayContaining([ expect.objectContaining({ @@ -71,4 +72,3 @@ test('Transaction includes span and correct value for decorated sync function', ]), ); }); -