-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(tanstackstart-react): Trace server functions #18500
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
Merged
Merged
Changes from 11 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
bdad818
Fix e2e version to minimally supported
nicohrubec a3f5e05
Add default server entry with some logs to e2e test
nicohrubec 320aa98
add basic failing transaction test
nicohrubec c1d5251
proxy fetch with withSentry
nicohrubec 8dd117a
lint
nicohrubec 6de8816
make it work
nicohrubec 0f425fd
stuff works
nicohrubec 076dc79
remove redundant awaits
nicohrubec d7f246d
fix ServerEntry type and remove async
nicohrubec 2cb31a8
clean out logs
nicohrubec c5246a6
Add test for nested server functions
nicohrubec fb4736e
Origin is now server instead of serverFn
nicohrubec f14b8a3
Remove async
nicohrubec 3a1c4a2
Improve span name: add request method name and use path name instead …
nicohrubec 6eedc35
Add server function sha256 as span attribute
nicohrubec 4d9b2cb
yarn fix
nicohrubec 09ba63c
make sha256 extraction more defensive to never be undefined + add uni…
nicohrubec 9dc3d24
yarn fix
nicohrubec 59a790d
withSentry becomes wrapFetchWithSentry
nicohrubec a8f317e
Merge branch 'develop' into nh/tss-server-fn-tracing
nicohrubec File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
dev-packages/e2e-tests/test-applications/tanstackstart-react/src/routes/test-serverFn.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| import { createFileRoute } from '@tanstack/react-router'; | ||
| import { createServerFn } from '@tanstack/react-start'; | ||
| import { startSpan } from '@sentry/tanstackstart-react'; | ||
|
|
||
| const testLog = createServerFn().handler(async () => { | ||
| console.log('Test log from server function'); | ||
| return { message: 'Log created' }; | ||
| }); | ||
|
|
||
| const testNestedLog = createServerFn().handler(async () => { | ||
| await startSpan({ name: 'testNestedLog' }, async () => { | ||
| await testLog(); | ||
| }); | ||
|
|
||
| console.log('Outer test log from server function'); | ||
| return { message: 'Nested log created' }; | ||
| }); | ||
|
|
||
| export const Route = createFileRoute('/test-serverFn')({ | ||
| component: TestLog, | ||
| }); | ||
|
|
||
| function TestLog() { | ||
| return ( | ||
| <div> | ||
| <h1>Test Log Page</h1> | ||
| <button | ||
| type="button" | ||
| onClick={async () => { | ||
| await testLog(); | ||
| }} | ||
| > | ||
| Call server function | ||
| </button> | ||
| <button | ||
| type="button" | ||
| onClick={async () => { | ||
| await testNestedLog(); | ||
| }} | ||
| > | ||
| Call server function nested | ||
| </button> | ||
| </div> | ||
| ); | ||
| } |
12 changes: 12 additions & 0 deletions
12
dev-packages/e2e-tests/test-applications/tanstackstart-react/src/server.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import { withSentry } from '@sentry/tanstackstart-react'; | ||
|
|
||
| import handler, { createServerEntry } from '@tanstack/react-start/server-entry'; | ||
| import type { ServerEntry } from '@tanstack/react-start/server-entry'; | ||
|
|
||
| const requestHandler: ServerEntry = withSentry({ | ||
| fetch(request: Request) { | ||
| return handler.fetch(request); | ||
| }, | ||
| }); | ||
|
|
||
| export default createServerEntry(requestHandler); |
93 changes: 93 additions & 0 deletions
93
dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/transaction.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| import { expect, test } from '@playwright/test'; | ||
| import { waitForTransaction } from '@sentry-internal/test-utils'; | ||
|
|
||
| test('Sends a server function transaction with auto-instrumentation', async ({ page }) => { | ||
| const transactionEventPromise = waitForTransaction('tanstackstart-react', transactionEvent => { | ||
| return ( | ||
| transactionEvent?.contexts?.trace?.op === 'http.server' && | ||
| !!transactionEvent?.transaction?.startsWith('GET /_serverFn') | ||
| ); | ||
| }); | ||
|
|
||
| await page.goto('/test-serverFn'); | ||
|
|
||
| await expect(page.getByText('Call server function', { exact: true })).toBeVisible(); | ||
|
|
||
| await page.getByText('Call server function', { exact: true }).click(); | ||
|
|
||
| const transactionEvent = await transactionEventPromise; | ||
|
|
||
| // Check for the auto-instrumented server function span | ||
| expect(Array.isArray(transactionEvent?.spans)).toBe(true); | ||
| expect(transactionEvent?.spans).toEqual( | ||
| expect.arrayContaining([ | ||
| expect.objectContaining({ | ||
| description: expect.stringContaining('/_serverFn/'), | ||
| op: 'function.tanstackstart', | ||
| origin: 'auto.function.tanstackstart.serverFn', | ||
| data: { | ||
| 'sentry.op': 'function.tanstackstart', | ||
| 'sentry.origin': 'auto.function.tanstackstart.serverFn', | ||
| }, | ||
| }), | ||
| ]), | ||
| ); | ||
| }); | ||
|
|
||
| test('Sends a server function transaction for a nested server function only if it is manually instrumented', async ({ | ||
| page, | ||
| }) => { | ||
| const transactionEventPromise = waitForTransaction('tanstackstart-react', transactionEvent => { | ||
| return ( | ||
| transactionEvent?.contexts?.trace?.op === 'http.server' && | ||
| !!transactionEvent?.transaction?.startsWith('GET /_serverFn') | ||
| ); | ||
| }); | ||
|
|
||
| await page.goto('/test-serverFn'); | ||
|
|
||
| await expect(page.getByText('Call server function nested')).toBeVisible(); | ||
|
|
||
| await page.getByText('Call server function nested').click(); | ||
|
|
||
| const transactionEvent = await transactionEventPromise; | ||
|
|
||
| expect(Array.isArray(transactionEvent?.spans)).toBe(true); | ||
|
|
||
| // Check for the auto-instrumented server function span | ||
| expect(transactionEvent?.spans).toEqual( | ||
| expect.arrayContaining([ | ||
| expect.objectContaining({ | ||
| description: expect.stringContaining('/_serverFn/'), | ||
| op: 'function.tanstackstart', | ||
| origin: 'auto.function.tanstackstart.serverFn', | ||
| status: 'ok', | ||
| }), | ||
| ]), | ||
| ); | ||
|
|
||
| // Check for the manually instrumented nested span | ||
| expect(transactionEvent?.spans).toEqual( | ||
| expect.arrayContaining([ | ||
| expect.objectContaining({ | ||
| description: 'testNestedLog', | ||
| origin: 'manual', | ||
| status: 'ok', | ||
| }), | ||
| ]), | ||
| ); | ||
|
|
||
| // Verify that the auto span is the parent of the nested span | ||
| const autoSpan = transactionEvent?.spans?.find( | ||
| (span: { op?: string; origin?: string }) => | ||
| span.op === 'function.tanstackstart' && span.origin === 'auto.function.tanstackstart.serverFn', | ||
| ); | ||
| const nestedSpan = transactionEvent?.spans?.find( | ||
| (span: { description?: string; origin?: string }) => | ||
| span.description === 'testNestedLog' && span.origin === 'manual', | ||
| ); | ||
|
|
||
| expect(autoSpan).toBeDefined(); | ||
| expect(nestedSpan).toBeDefined(); | ||
| expect(nestedSpan?.parent_span_id).toBe(autoSpan?.span_id); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/node'; | ||
|
|
||
| export type ServerEntry = { | ||
| fetch: (request: Request, opts?: unknown) => Promise<Response> | Response; | ||
| }; | ||
|
|
||
| /** | ||
| * This function can be used to wrap the server entry request handler to add tracing to server-side functionality. | ||
| * You must explicitly define a server entry point in your application for this to work. This is done by passing the request handler to the `createServerEntry` function. | ||
| * For more information about the server entry point, see the [TanStack Start documentation](https://tanstack.com/start/docs/server-entry). | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * import { withSentry } from '@sentry/tanstackstart-react'; | ||
| * | ||
| * import handler, { createServerEntry } from '@tanstack/react-start/server-entry'; | ||
| * import type { ServerEntry } from '@tanstack/react-start/server-entry'; | ||
| * | ||
| * const requestHandler: ServerEntry = withSentry({ | ||
| * fetch(request: Request) { | ||
| * return handler.fetch(request); | ||
| * }, | ||
| * }); | ||
| * | ||
| * export default serverEntry = createServerEntry(requestHandler); | ||
| * ``` | ||
| * | ||
| * @param serverEntry - request handler to wrap | ||
| * @returns - wrapped request handler | ||
| */ | ||
| export function withSentry(serverEntry: ServerEntry): ServerEntry { | ||
| if (serverEntry.fetch) { | ||
| serverEntry.fetch = new Proxy<typeof serverEntry.fetch>(serverEntry.fetch, { | ||
| apply: async (target, thisArg, args) => { | ||
| const request: Request = args[0]; | ||
|
|
||
| // instrument server functions | ||
| if (request.url?.includes('_serverFn') || request.url?.includes('createServerFn')) { | ||
| const op = 'function.tanstackstart'; | ||
| return startSpan( | ||
| { | ||
| op: op, | ||
| name: request.url, | ||
nicohrubec marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| attributes: { | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.tanstackstart.serverFn', | ||
andreiborza marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| [SEMANTIC_ATTRIBUTE_SENTRY_OP]: op, | ||
| }, | ||
| }, | ||
| () => { | ||
| return target.apply(thisArg, args); | ||
| }, | ||
| ); | ||
| } | ||
|
|
||
| return target.apply(thisArg, args); | ||
| }, | ||
nicohrubec marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }); | ||
| } | ||
| return serverEntry; | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.