-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(nestjs): Add function-level span decorator to nestjs #12721
Changes from 7 commits
dd2ef7e
7d802e0
326969b
9afd76f
cef0a49
b297c41
2b4c8b1
b344930
dff2bef
f4c9618
059705a
d38d712
30f1dc3
394b324
4d16c2b
6b588cf
322c645
17739bb
e4ffdfd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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', | ||
}), | ||
]), | ||
); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. l: Is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SentryTraced There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, GetX is not super common in nest was just an initial idea. I like SentryTraced if everyone is happy with that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated to SentryTraced |
||
|
||
```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/) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
export * from '@sentry/node'; | ||
|
||
export { init } from './sdk'; | ||
|
||
export * from './span-decorator'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. h: In the index file, let's be specific about what we export. This is to avoid accidentally widening the API surface. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updated |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { captureException, startSpan } from '@sentry/node'; | ||
|
||
/** | ||
* A decorator usable to wrap arbitrary functions with spans. | ||
*/ | ||
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<any>; | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
descriptor.value = async function (...args: any[]) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. h: We should not modify the return value. Currently we are wrapping all non-promise return values in a Promise. To fix this we can just remove all There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
await startSpan( | ||
{ | ||
op: op, | ||
name: propertyKey, | ||
}, | ||
async () => { | ||
try { | ||
return await originalMethod.apply(this, args); | ||
} catch (e) { | ||
captureException(e); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. h: We do not want to wrap this with a try-catch-captureException. We only want to capture errors when they bubble up to an unexpected place. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updated |
||
}, | ||
); | ||
}; | ||
return descriptor; | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
m: Let's assert on the
sentry.origin
andsentry.source
attibutes in thedata
object.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I explicitly assert the data object now, there is no
sentry.source
property though