diff --git a/packages/nextjs/src/config/wrappers/withSentryGetServerSideProps.ts b/packages/nextjs/src/config/wrappers/withSentryGetServerSideProps.ts index 49fe55535d0e..dee5ce918c45 100644 --- a/packages/nextjs/src/config/wrappers/withSentryGetServerSideProps.ts +++ b/packages/nextjs/src/config/wrappers/withSentryGetServerSideProps.ts @@ -1,8 +1,9 @@ import { hasTracingEnabled } from '@sentry/tracing'; +import { serializeBaggage } from '@sentry/utils'; import { GetServerSideProps } from 'next'; import { isBuild } from '../../utils/isBuild'; -import { callTracedServerSideDataFetcher, withErrorInstrumentation } from './wrapperUtils'; +import { callTracedServerSideDataFetcher, getTransactionFromRequest, withErrorInstrumentation } from './wrapperUtils'; /** * Create a wrapped version of the user's exported `getServerSideProps` function @@ -28,11 +29,27 @@ export function withSentryGetServerSideProps( const errorWrappedGetServerSideProps = withErrorInstrumentation(origGetServerSideProps); if (hasTracingEnabled()) { - return callTracedServerSideDataFetcher(errorWrappedGetServerSideProps, getServerSidePropsArguments, req, res, { - dataFetcherRouteName: parameterizedRoute, - requestedRouteName: parameterizedRoute, - dataFetchingMethodName: 'getServerSideProps', - }); + const serverSideProps = await callTracedServerSideDataFetcher( + errorWrappedGetServerSideProps, + getServerSidePropsArguments, + req, + res, + { + dataFetcherRouteName: parameterizedRoute, + requestedRouteName: parameterizedRoute, + dataFetchingMethodName: 'getServerSideProps', + }, + ); + + if ('props' in serverSideProps) { + const requestTransaction = getTransactionFromRequest(req); + if (requestTransaction) { + serverSideProps.props._sentryTraceData = requestTransaction.toTraceparent(); + serverSideProps.props._sentryBaggage = serializeBaggage(requestTransaction.getBaggage()); + } + } + + return serverSideProps; } else { return errorWrappedGetServerSideProps(...getServerSidePropsArguments); } diff --git a/packages/nextjs/src/config/wrappers/withSentryServerSideAppGetInitialProps.ts b/packages/nextjs/src/config/wrappers/withSentryServerSideAppGetInitialProps.ts index 798ceeae2e8f..86cf92fb544e 100644 --- a/packages/nextjs/src/config/wrappers/withSentryServerSideAppGetInitialProps.ts +++ b/packages/nextjs/src/config/wrappers/withSentryServerSideAppGetInitialProps.ts @@ -1,8 +1,9 @@ import { hasTracingEnabled } from '@sentry/tracing'; +import { serializeBaggage } from '@sentry/utils'; import App from 'next/app'; import { isBuild } from '../../utils/isBuild'; -import { callTracedServerSideDataFetcher, withErrorInstrumentation } from './wrapperUtils'; +import { callTracedServerSideDataFetcher, getTransactionFromRequest, withErrorInstrumentation } from './wrapperUtils'; type AppGetInitialProps = typeof App['getInitialProps']; @@ -30,12 +31,30 @@ export function withSentryServerSideAppGetInitialProps(origAppGetInitialProps: A if (hasTracingEnabled()) { // Since this wrapper is only applied to `getInitialProps` running on the server, we can assert that `req` and // `res` are always defined: https://nextjs.org/docs/api-reference/data-fetching/get-initial-props#context-object - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return callTracedServerSideDataFetcher(errorWrappedAppGetInitialProps, appGetInitialPropsArguments, req!, res!, { - dataFetcherRouteName: '/_app', - requestedRouteName: context.ctx.pathname, - dataFetchingMethodName: 'getInitialProps', - }); + const appGetInitialProps: { + pageProps: { + _sentryTraceData?: string; + _sentryBaggage?: string; + }; + } = await callTracedServerSideDataFetcher( + errorWrappedAppGetInitialProps, + appGetInitialPropsArguments, + req!, + res!, + { + dataFetcherRouteName: '/_app', + requestedRouteName: context.ctx.pathname, + dataFetchingMethodName: 'getInitialProps', + }, + ); + + const requestTransaction = getTransactionFromRequest(req!); + if (requestTransaction) { + appGetInitialProps.pageProps._sentryTraceData = requestTransaction.toTraceparent(); + appGetInitialProps.pageProps._sentryBaggage = serializeBaggage(requestTransaction.getBaggage()); + } + + return appGetInitialProps; } else { return errorWrappedAppGetInitialProps(...appGetInitialPropsArguments); } diff --git a/packages/nextjs/src/config/wrappers/withSentryServerSideErrorGetInitialProps.ts b/packages/nextjs/src/config/wrappers/withSentryServerSideErrorGetInitialProps.ts index 379b18ad05f8..e7d4c7c5b46c 100644 --- a/packages/nextjs/src/config/wrappers/withSentryServerSideErrorGetInitialProps.ts +++ b/packages/nextjs/src/config/wrappers/withSentryServerSideErrorGetInitialProps.ts @@ -1,9 +1,10 @@ import { hasTracingEnabled } from '@sentry/tracing'; +import { serializeBaggage } from '@sentry/utils'; import { NextPageContext } from 'next'; import { ErrorProps } from 'next/error'; import { isBuild } from '../../utils/isBuild'; -import { callTracedServerSideDataFetcher, withErrorInstrumentation } from './wrapperUtils'; +import { callTracedServerSideDataFetcher, getTransactionFromRequest, withErrorInstrumentation } from './wrapperUtils'; type ErrorGetInitialProps = (context: NextPageContext) => Promise; @@ -33,12 +34,28 @@ export function withSentryServerSideErrorGetInitialProps( if (hasTracingEnabled()) { // Since this wrapper is only applied to `getInitialProps` running on the server, we can assert that `req` and // `res` are always defined: https://nextjs.org/docs/api-reference/data-fetching/get-initial-props#context-object - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return callTracedServerSideDataFetcher(errorWrappedGetInitialProps, errorGetInitialPropsArguments, req!, res!, { - dataFetcherRouteName: '/_error', - requestedRouteName: context.pathname, - dataFetchingMethodName: 'getInitialProps', - }); + const errorGetInitialProps: ErrorProps & { + _sentryTraceData?: string; + _sentryBaggage?: string; + } = await callTracedServerSideDataFetcher( + errorWrappedGetInitialProps, + errorGetInitialPropsArguments, + req!, + res!, + { + dataFetcherRouteName: '/_error', + requestedRouteName: context.pathname, + dataFetchingMethodName: 'getInitialProps', + }, + ); + + const requestTransaction = getTransactionFromRequest(req!); + if (requestTransaction) { + errorGetInitialProps._sentryTraceData = requestTransaction.toTraceparent(); + errorGetInitialProps._sentryBaggage = serializeBaggage(requestTransaction.getBaggage()); + } + + return errorGetInitialProps; } else { return errorWrappedGetInitialProps(...errorGetInitialPropsArguments); } diff --git a/packages/nextjs/src/config/wrappers/withSentryServerSideGetInitialProps.ts b/packages/nextjs/src/config/wrappers/withSentryServerSideGetInitialProps.ts index 458609a9646b..8837ae121cf5 100644 --- a/packages/nextjs/src/config/wrappers/withSentryServerSideGetInitialProps.ts +++ b/packages/nextjs/src/config/wrappers/withSentryServerSideGetInitialProps.ts @@ -1,8 +1,9 @@ import { hasTracingEnabled } from '@sentry/tracing'; +import { serializeBaggage } from '@sentry/utils'; import { NextPage } from 'next'; import { isBuild } from '../../utils/isBuild'; -import { callTracedServerSideDataFetcher, withErrorInstrumentation } from './wrapperUtils'; +import { callTracedServerSideDataFetcher, getTransactionFromRequest, withErrorInstrumentation } from './wrapperUtils'; type GetInitialProps = Required['getInitialProps']; @@ -29,12 +30,22 @@ export function withSentryServerSideGetInitialProps(origGetInitialProps: GetInit if (hasTracingEnabled()) { // Since this wrapper is only applied to `getInitialProps` running on the server, we can assert that `req` and // `res` are always defined: https://nextjs.org/docs/api-reference/data-fetching/get-initial-props#context-object - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return callTracedServerSideDataFetcher(errorWrappedGetInitialProps, getInitialPropsArguments, req!, res!, { + const initialProps: { + _sentryTraceData?: string; + _sentryBaggage?: string; + } = await callTracedServerSideDataFetcher(errorWrappedGetInitialProps, getInitialPropsArguments, req!, res!, { dataFetcherRouteName: context.pathname, requestedRouteName: context.pathname, dataFetchingMethodName: 'getInitialProps', }); + + const requestTransaction = getTransactionFromRequest(req!); + if (requestTransaction) { + initialProps._sentryTraceData = requestTransaction.toTraceparent(); + initialProps._sentryBaggage = serializeBaggage(requestTransaction.getBaggage()); + } + + return initialProps; } else { return errorWrappedGetInitialProps(...getInitialPropsArguments); } diff --git a/packages/nextjs/src/config/wrappers/wrapperUtils.ts b/packages/nextjs/src/config/wrappers/wrapperUtils.ts index 0042143b15f3..a7be6784dbf6 100644 --- a/packages/nextjs/src/config/wrappers/wrapperUtils.ts +++ b/packages/nextjs/src/config/wrappers/wrapperUtils.ts @@ -2,7 +2,7 @@ import { captureException, getCurrentHub, startTransaction } from '@sentry/core' import { addRequestDataToEvent } from '@sentry/node'; import { getActiveTransaction } from '@sentry/tracing'; import { Transaction } from '@sentry/types'; -import { fill } from '@sentry/utils'; +import { extractTraceparentData, fill, isString, parseBaggageSetMutability } from '@sentry/utils'; import * as domain from 'domain'; import { IncomingMessage, ServerResponse } from 'http'; @@ -12,7 +12,14 @@ declare module 'http' { } } -function getTransactionFromRequest(req: IncomingMessage): Transaction | undefined { +/** + * Grabs a transaction off a Next.js datafetcher request object, if it was previously put there via + * `setTransactionOnRequest`. + * + * @param req The Next.js datafetcher request object + * @returns the Transaction on the request object if there is one, or `undefined` if the request object didn't have one. + */ +export function getTransactionFromRequest(req: IncomingMessage): Transaction | undefined { return req._sentryTransaction; } @@ -31,20 +38,15 @@ function autoEndTransactionOnResponseEnd(transaction: Transaction, res: ServerRe /** * Wraps a function that potentially throws. If it does, the error is passed to `captureException` and rethrown. + * + * Note: This function turns the wrapped function into an asynchronous one. */ export function withErrorInstrumentation any>( origFunction: F, -): (...params: Parameters) => ReturnType { - return function (this: unknown, ...origFunctionArguments: Parameters): ReturnType { +): (...params: Parameters) => Promise> { + return async function (this: unknown, ...origFunctionArguments: Parameters): Promise> { try { - const potentialPromiseResult = origFunction.call(this, ...origFunctionArguments); - // First of all, we need to capture promise rejections so we have the following check, as well as the try-catch block. - // Additionally, we do the following instead of `await`-ing so we do not change the method signature of the passed function from `() => unknown` to `() => Promise. - Promise.resolve(potentialPromiseResult).catch(err => { - // TODO: Extract error logic from `withSentry` in here or create a new wrapper with said logic or something like that. - captureException(err); - }); - return potentialPromiseResult; + return await origFunction.call(this, ...origFunctionArguments); } catch (e) { // TODO: Extract error logic from `withSentry` in here or create a new wrapper with said logic or something like that. captureException(e); @@ -85,13 +87,20 @@ export function callTracedServerSideDataFetcher Pr let requestTransaction: Transaction | undefined = getTransactionFromRequest(req); if (requestTransaction === undefined) { - // TODO: Extract trace data from `req` object (trace and baggage headers) and attach it to transaction + const sentryTraceHeader = req.headers['sentry-trace']; + const rawBaggageString = req.headers && isString(req.headers.baggage) && req.headers.baggage; + const traceparentData = + typeof sentryTraceHeader === 'string' ? extractTraceparentData(sentryTraceHeader) : undefined; + + const baggage = parseBaggageSetMutability(rawBaggageString, traceparentData); const newTransaction = startTransaction({ op: 'nextjs.data.server', name: options.requestedRouteName, + ...traceparentData, metadata: { source: 'route', + baggage, }, }); @@ -121,7 +130,6 @@ export function callTracedServerSideDataFetcher Pr } try { - // TODO: Inject trace data into returned props return await origFunction(...origFunctionArguments); } finally { dataFetcherSpan.finish(); diff --git a/packages/nextjs/src/performance/client.ts b/packages/nextjs/src/performance/client.ts index db10a8b6799a..257c21c429bf 100644 --- a/packages/nextjs/src/performance/client.ts +++ b/packages/nextjs/src/performance/client.ts @@ -22,20 +22,14 @@ type StartTransactionCb = (context: TransactionContext) => Transaction | undefin * Describes data located in the __NEXT_DATA__ script tag. This tag is present on every page of a Next.js app. */ interface SentryEnhancedNextData extends NextData { - // contains props returned by `getInitialProps` - except for `pageProps`, these are the props that got returned by `getServerSideProps` or `getStaticProps` props: { - _sentryGetInitialPropsTraceData?: string; // trace parent info, if injected by server-side `getInitialProps` - _sentryGetInitialPropsBaggage?: string; // baggage, if injected by server-side `getInitialProps` pageProps?: { - _sentryGetServerSidePropsTraceData?: string; // trace parent info, if injected by server-side `getServerSideProps` - _sentryGetServerSidePropsBaggage?: string; // baggage, if injected by server-side `getServerSideProps` - - // The following two values are only injected in a very special case with the following conditions: + _sentryTraceData?: string; // trace parent info, if injected by a data-fetcher + _sentryBaggage?: string; // baggage, if injected by a data-fetcher + // These two values are only injected by `getStaticProps` in a very special case with the following conditions: // 1. The page's `getStaticPaths` method must have returned `fallback: 'blocking'`. // 2. The requested page must be a "miss" in terms of "Incremental Static Regeneration", meaning the requested page has not been generated before. // In this case, a page is requested and only served when `getStaticProps` is done. There is not even a fallback page or similar. - _sentryGetStaticPropsTraceData?: string; // trace parent info, if injected by server-side `getStaticProps` - _sentryGetStaticPropsBaggage?: string; // baggage, if injected by server-side `getStaticProps` }; }; } @@ -79,29 +73,19 @@ function extractNextDataTagInformation(): NextDataTagInfo { const { page, query, props } = nextData; - // `nextData.page` always contains the parameterized route + // `nextData.page` always contains the parameterized route - except for when an error occurs in a data fetching + // function, then it is "/_error", but that isn't a problem since users know which route threw by looking at the + // parent transaction nextDataTagInfo.route = page; nextDataTagInfo.params = query; - if (props) { - const { pageProps } = props; - - const getInitialPropsBaggage = props._sentryGetInitialPropsBaggage; - const getServerSidePropsBaggage = pageProps && pageProps._sentryGetServerSidePropsBaggage; - const getStaticPropsBaggage = pageProps && pageProps._sentryGetStaticPropsBaggage; - // Ordering of the following shouldn't matter but `getInitialProps` generally runs before `getServerSideProps` or `getStaticProps` so we give it priority. - const baggage = getInitialPropsBaggage || getServerSidePropsBaggage || getStaticPropsBaggage; - if (baggage) { - nextDataTagInfo.baggage = baggage; + if (props && props.pageProps) { + if (props.pageProps._sentryBaggage) { + nextDataTagInfo.baggage = props.pageProps._sentryBaggage; } - const getInitialPropsTraceData = props._sentryGetInitialPropsTraceData; - const getServerSidePropsTraceData = pageProps && pageProps._sentryGetServerSidePropsTraceData; - const getStaticPropsTraceData = pageProps && pageProps._sentryGetStaticPropsTraceData; - // Ordering of the following shouldn't matter but `getInitialProps` generally runs before `getServerSideProps` or `getStaticProps` so we give it priority. - const traceData = getInitialPropsTraceData || getServerSidePropsTraceData || getStaticPropsTraceData; - if (traceData) { - nextDataTagInfo.traceParentData = extractTraceparentData(traceData); + if (props.pageProps._sentryTraceData) { + nextDataTagInfo.traceParentData = extractTraceparentData(props.pageProps._sentryTraceData); } } diff --git a/packages/nextjs/test/integration/next.config.js b/packages/nextjs/test/integration/next.config.js index c6356f77e3bf..4d32445a3cef 100644 --- a/packages/nextjs/test/integration/next.config.js +++ b/packages/nextjs/test/integration/next.config.js @@ -4,6 +4,9 @@ const moduleExports = { eslint: { ignoreDuringBuilds: true, }, + sentry: { + experiments: { autoWrapDataFetchers: true }, + }, }; const SentryWebpackPluginOptions = { dryRun: true, diff --git a/packages/nextjs/test/integration/next10.config.template b/packages/nextjs/test/integration/next10.config.template index e9e52049a4f3..61aa3332bbc0 100644 --- a/packages/nextjs/test/integration/next10.config.template +++ b/packages/nextjs/test/integration/next10.config.template @@ -5,6 +5,9 @@ const moduleExports = { future: { webpack5: %RUN_WEBPACK_5%, }, + sentry: { + experiments: { autoWrapDataFetchers: true }, + }, }; const SentryWebpackPluginOptions = { diff --git a/packages/nextjs/test/integration/next11.config.template b/packages/nextjs/test/integration/next11.config.template index 5de08b436a67..454d300a8b7b 100644 --- a/packages/nextjs/test/integration/next11.config.template +++ b/packages/nextjs/test/integration/next11.config.template @@ -6,6 +6,9 @@ const moduleExports = { eslint: { ignoreDuringBuilds: true, }, + sentry: { + experiments: { autoWrapDataFetchers: true }, + }, }; const SentryWebpackPluginOptions = { diff --git a/packages/nextjs/test/integration/pages/[id]/withInitialProps.tsx b/packages/nextjs/test/integration/pages/[id]/withInitialProps.tsx new file mode 100644 index 000000000000..01b557bdd09f --- /dev/null +++ b/packages/nextjs/test/integration/pages/[id]/withInitialProps.tsx @@ -0,0 +1,7 @@ +const WithInitialPropsPage = ({ data }: { data: string }) =>

WithInitialPropsPage {data}

; + +WithInitialPropsPage.getInitialProps = () => { + return { data: '[some getInitialProps data]' }; +}; + +export default WithInitialPropsPage; diff --git a/packages/nextjs/test/integration/pages/[id]/withServerSideProps.tsx b/packages/nextjs/test/integration/pages/[id]/withServerSideProps.tsx new file mode 100644 index 000000000000..486313a9b9c3 --- /dev/null +++ b/packages/nextjs/test/integration/pages/[id]/withServerSideProps.tsx @@ -0,0 +1,7 @@ +const WithServerSidePropsPage = ({ data }: { data: string }) =>

WithServerSidePropsPage {data}

; + +export async function getServerSideProps() { + return { props: { data: '[some getServerSideProps data]' } }; +} + +export default WithServerSidePropsPage; diff --git a/packages/nextjs/test/integration/pages/withServerSideProps.tsx b/packages/nextjs/test/integration/pages/withErrorServerSideProps.tsx similarity index 100% rename from packages/nextjs/test/integration/pages/withServerSideProps.tsx rename to packages/nextjs/test/integration/pages/withErrorServerSideProps.tsx diff --git a/packages/nextjs/test/integration/test/client/tracingClientGetInitialProps.js b/packages/nextjs/test/integration/test/client/tracingClientGetInitialProps.js new file mode 100644 index 000000000000..3c53a0f87654 --- /dev/null +++ b/packages/nextjs/test/integration/test/client/tracingClientGetInitialProps.js @@ -0,0 +1,48 @@ +const { + expectRequestCount, + isTransactionRequest, + expectTransaction, + extractEnvelopeFromRequest, +} = require('../utils/client'); +const assert = require('assert').strict; + +module.exports = async ({ page, url, requests }) => { + await page.goto(`${url}/42/withInitialProps`); + await page.waitForRequest(isTransactionRequest); + + const transactionEnvelope = extractEnvelopeFromRequest(requests.transactions[0]); + + expectTransaction(requests.transactions[0], { + contexts: { + trace: { + op: 'pageload', + }, + }, + }); + + const nextDataTag = await page.waitForSelector('#__NEXT_DATA__'); + const nextDataTagValue = JSON.parse(await nextDataTag.evaluate(tag => tag.innerText)); + + assert.strictEqual( + nextDataTagValue.props.pageProps.data, + '[some getInitialProps data]', + 'Returned data must contain original data returned from getInitialProps.', + ); + + assert.ok(nextDataTagValue.props.pageProps._sentryTraceData, '_sentryTraceData must exist in __NEXT_DATA__ tag'); + assert.ok(nextDataTagValue.props.pageProps._sentryBaggage, '_sentryBaggage must exist in __NEXT_DATA__ tag'); + + assert.strictEqual( + nextDataTagValue.props.pageProps._sentryTraceData.split('-')[0], + transactionEnvelope.envelopeHeader.trace.trace_id, + 'Trace id in envelope header must be the same as in trace parent data returned from getInitialProps', + ); + + assert.strictEqual( + nextDataTagValue.props.pageProps._sentryBaggage.match(/sentry-trace_id=([a-f0-9]*),/)[1], + transactionEnvelope.envelopeHeader.trace.trace_id, + 'Trace id in envelope header must be the same as in baggage returned from getInitialProps', + ); + + await expectRequestCount(requests, { transactions: 1 }); +}; diff --git a/packages/nextjs/test/integration/test/client/tracingClientGetServerSideProps.js b/packages/nextjs/test/integration/test/client/tracingClientGetServerSideProps.js new file mode 100644 index 000000000000..59c1613ff30f --- /dev/null +++ b/packages/nextjs/test/integration/test/client/tracingClientGetServerSideProps.js @@ -0,0 +1,48 @@ +const { + expectRequestCount, + isTransactionRequest, + expectTransaction, + extractEnvelopeFromRequest, +} = require('../utils/client'); +const assert = require('assert').strict; + +module.exports = async ({ page, url, requests }) => { + await page.goto(`${url}/1337/withServerSideProps`); + await page.waitForRequest(isTransactionRequest); + + const transactionEnvelope = extractEnvelopeFromRequest(requests.transactions[0]); + + expectTransaction(requests.transactions[0], { + contexts: { + trace: { + op: 'pageload', + }, + }, + }); + + const nextDataTag = await page.waitForSelector('#__NEXT_DATA__'); + const nextDataTagValue = JSON.parse(await nextDataTag.evaluate(tag => tag.innerText)); + + assert.strictEqual( + nextDataTagValue.props.pageProps.data, + '[some getServerSideProps data]', + 'Returned data must contain original data returned from getServerSideProps.', + ); + + assert.ok(nextDataTagValue.props.pageProps._sentryTraceData, '_sentryTraceData must exist in __NEXT_DATA__ tag'); + assert.ok(nextDataTagValue.props.pageProps._sentryBaggage, '_sentryBaggage must exist in __NEXT_DATA__ tag'); + + assert.strictEqual( + nextDataTagValue.props.pageProps._sentryTraceData.split('-')[0], + transactionEnvelope.envelopeHeader.trace.trace_id, + 'Trace id in envelope header must be the same as in trace parent data returned from getServerSideProps', + ); + + assert.strictEqual( + nextDataTagValue.props.pageProps._sentryBaggage.match(/sentry-trace_id=([a-f0-9]*),/)[1], + transactionEnvelope.envelopeHeader.trace.trace_id, + 'Trace id in envelope header must be the same as in baggage returned from getServerSideProps', + ); + + await expectRequestCount(requests, { transactions: 1 }); +}; diff --git a/packages/nextjs/test/integration/test/server/errorServerSideProps.js b/packages/nextjs/test/integration/test/server/errorServerSideProps.js index a6c622de5db5..ab5f743486b5 100644 --- a/packages/nextjs/test/integration/test/server/errorServerSideProps.js +++ b/packages/nextjs/test/integration/test/server/errorServerSideProps.js @@ -4,7 +4,7 @@ const { sleep } = require('../utils/common'); const { getAsync, interceptEventRequest } = require('../utils/server'); module.exports = async ({ url: urlBase, argv }) => { - const url = `${urlBase}/withServerSideProps`; + const url = `${urlBase}/withErrorServerSideProps`; const capturedRequest = interceptEventRequest( { diff --git a/packages/nextjs/test/integration/test/server/tracingServerGetInitialProps.js b/packages/nextjs/test/integration/test/server/tracingServerGetInitialProps.js new file mode 100644 index 000000000000..368ccb26a952 --- /dev/null +++ b/packages/nextjs/test/integration/test/server/tracingServerGetInitialProps.js @@ -0,0 +1,33 @@ +const assert = require('assert'); + +const { sleep } = require('../utils/common'); +const { getAsync, interceptTracingRequest } = require('../utils/server'); + +module.exports = async ({ url: urlBase, argv }) => { + const url = `${urlBase}/239/withInitialProps`; + + const capturedRequest = interceptTracingRequest( + { + contexts: { + trace: { + op: 'nextjs.data.server', + }, + }, + transaction: '/[id]/withInitialProps', + transaction_info: { + source: 'route', + }, + type: 'transaction', + request: { + url, + }, + }, + argv, + 'tracingGetInitialProps', + ); + + await getAsync(url); + await sleep(250); + + assert.ok(capturedRequest.isDone(), 'Did not intercept expected request'); +}; diff --git a/packages/nextjs/test/integration/test/server/tracingServerGetServerSideProps.js b/packages/nextjs/test/integration/test/server/tracingServerGetServerSideProps.js new file mode 100644 index 000000000000..8f195d337168 --- /dev/null +++ b/packages/nextjs/test/integration/test/server/tracingServerGetServerSideProps.js @@ -0,0 +1,33 @@ +const assert = require('assert'); + +const { sleep } = require('../utils/common'); +const { getAsync, interceptTracingRequest } = require('../utils/server'); + +module.exports = async ({ url: urlBase, argv }) => { + const url = `${urlBase}/193/withServerSideProps`; + + const capturedRequest = interceptTracingRequest( + { + contexts: { + trace: { + op: 'nextjs.data.server', + }, + }, + transaction: '/[id]/withServerSideProps', + transaction_info: { + source: 'route', + }, + type: 'transaction', + request: { + url, + }, + }, + argv, + 'tracingServerGetServerSideProps', + ); + + await getAsync(url); + await sleep(250); + + assert.ok(capturedRequest.isDone(), 'Did not intercept expected request'); +}; diff --git a/packages/nextjs/test/performance/client.test.ts b/packages/nextjs/test/performance/client.test.ts index 2a90b7db45a1..279c39ed90fc 100644 --- a/packages/nextjs/test/performance/client.test.ts +++ b/packages/nextjs/test/performance/client.test.ts @@ -88,41 +88,15 @@ describe('client', () => { 'https://example.com/lforst/posts/1337?q=42', '/[user]/posts/[id]', { user: 'lforst', id: '1337', q: '42' }, - { - _sentryGetInitialPropsTraceData: 'c82b8554881b4d28ad977de04a4fb40a-a755953cd3394d5f-1', - _sentryGetInitialPropsBaggage: - 'other=vendor,foo=bar,third=party,last=item,sentry-release=2.1.0,sentry-environment=myEnv', - }, - true, - { - name: '/[user]/posts/[id]', - op: 'pageload', - tags: { - 'routing.instrumentation': 'next-router', - }, - metadata: { - source: 'route', - baggage: [{ environment: 'myEnv', release: '2.1.0' }, '', true], - }, - traceId: 'c82b8554881b4d28ad977de04a4fb40a', - parentSpanId: 'a755953cd3394d5f', - parentSampled: true, - }, - ], - [ - 'https://example.com/dynamic', - '/dynamic', - {}, { pageProps: { - _sentryGetServerSidePropsTraceData: 'c82b8554881b4d28ad977de04a4fb40a-a755953cd3394d5f-1', - _sentryGetServerSidePropsBaggage: - 'other=vendor,foo=bar,third=party,last=item,sentry-release=2.1.0,sentry-environment=myEnv', + _sentryTraceData: 'c82b8554881b4d28ad977de04a4fb40a-a755953cd3394d5f-1', + _sentryBaggage: 'other=vendor,foo=bar,third=party,last=item,sentry-release=2.1.0,sentry-environment=myEnv', }, }, true, { - name: '/dynamic', + name: '/[user]/posts/[id]', op: 'pageload', tags: { 'routing.instrumentation': 'next-router', @@ -137,19 +111,18 @@ describe('client', () => { }, ], [ - 'https://example.com/static', - '/static', + 'https://example.com/some-page', + '/some-page', {}, { pageProps: { - _sentryGetStaticPropsTraceData: 'c82b8554881b4d28ad977de04a4fb40a-a755953cd3394d5f-1', - _sentryGetStaticPropsBaggage: - 'other=vendor,foo=bar,third=party,last=item,sentry-release=2.1.0,sentry-environment=myEnv', + _sentryTraceData: 'c82b8554881b4d28ad977de04a4fb40a-a755953cd3394d5f-1', + _sentryBaggage: 'other=vendor,foo=bar,third=party,last=item,sentry-release=2.1.0,sentry-environment=myEnv', }, }, true, { - name: '/static', + name: '/some-page', op: 'pageload', tags: { 'routing.instrumentation': 'next-router',