Skip to content

Commit ad75f14

Browse files
author
Luca Forstner
authored
Merge pull request #8076 from getsentry/prepare-release/7.51.2
2 parents cea8b47 + bfb44fe commit ad75f14

File tree

11 files changed

+65
-27
lines changed

11 files changed

+65
-27
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,7 @@ jobs:
808808
job_node_integration_tests,
809809
job_browser_playwright_tests,
810810
job_browser_integration_tests,
811+
job_browser_loader_tests,
811812
job_remix_integration_tests,
812813
job_e2e_tests,
813814
]

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
66

7+
## 7.51.2
8+
9+
- fix(nextjs): Continue traces in data fetchers when there is an already active transaction on the hub (#8073)
10+
- fix(sveltekit): Avoid creating the Sentry Vite plugin in dev mode (#8065)
11+
712
## 7.51.1
813

914
- feat(replay): Add event to capture options on checkouts (#8011)

packages/e2e-tests/test-applications/create-react-app/test-recipe.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,13 @@
22
"$schema": "../../test-recipe-schema.json",
33
"testApplicationName": "create-react-app",
44
"buildCommand": "pnpm install && pnpm build",
5-
"tests": []
5+
"tests": [],
6+
"canaryVersions": [
7+
{
8+
"dependencyOverrides": {
9+
"react": "canary",
10+
"react-dom": "canary"
11+
}
12+
}
13+
]
614
}

packages/nextjs/src/server/utils/wrapperUtils.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
runWithAsyncContext,
66
startTransaction,
77
} from '@sentry/core';
8-
import type { Transaction } from '@sentry/types';
8+
import type { Span, Transaction } from '@sentry/types';
99
import { baggageHeaderToDynamicSamplingContext, extractTraceparentData } from '@sentry/utils';
1010
import type { IncomingMessage, ServerResponse } from 'http';
1111

@@ -82,7 +82,8 @@ export function withTracedServerSideDataFetcher<F extends (...args: any[]) => Pr
8282
return async function (this: unknown, ...args: Parameters<F>): Promise<ReturnType<F>> {
8383
return runWithAsyncContext(async () => {
8484
const hub = getCurrentHub();
85-
let requestTransaction: Transaction | undefined = getTransactionFromRequest(req);
85+
const scope = hub.getScope();
86+
const previousSpan: Span | undefined = getTransactionFromRequest(req) ?? scope.getSpan();
8687
let dataFetcherSpan;
8788

8889
const sentryTraceHeader = req.headers['sentry-trace'];
@@ -93,7 +94,8 @@ export function withTracedServerSideDataFetcher<F extends (...args: any[]) => Pr
9394
const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(rawBaggageString);
9495

9596
if (platformSupportsStreaming()) {
96-
if (requestTransaction === undefined) {
97+
let spanToContinue: Span;
98+
if (previousSpan === undefined) {
9799
const newTransaction = startTransaction(
98100
{
99101
op: 'http.server',
@@ -109,8 +111,6 @@ export function withTracedServerSideDataFetcher<F extends (...args: any[]) => Pr
109111
{ request: req },
110112
);
111113

112-
requestTransaction = newTransaction;
113-
114114
if (platformSupportsStreaming()) {
115115
// On platforms that don't support streaming, doing things after res.end() is unreliable.
116116
autoEndTransactionOnResponseEnd(newTransaction, res);
@@ -119,9 +119,12 @@ export function withTracedServerSideDataFetcher<F extends (...args: any[]) => Pr
119119
// Link the transaction and the request together, so that when we would normally only have access to one, it's
120120
// still possible to grab the other.
121121
setTransactionOnRequest(newTransaction, req);
122+
spanToContinue = newTransaction;
123+
} else {
124+
spanToContinue = previousSpan;
122125
}
123126

124-
dataFetcherSpan = requestTransaction.startChild({
127+
dataFetcherSpan = spanToContinue.startChild({
125128
op: 'function.nextjs',
126129
description: `${options.dataFetchingMethodName} (${options.dataFetcherRouteName})`,
127130
status: 'ok',
@@ -140,22 +143,20 @@ export function withTracedServerSideDataFetcher<F extends (...args: any[]) => Pr
140143
});
141144
}
142145

143-
const currentScope = hub.getScope();
144-
if (currentScope) {
145-
currentScope.setSpan(dataFetcherSpan);
146-
currentScope.setSDKProcessingMetadata({ request: req });
147-
}
146+
scope.setSpan(dataFetcherSpan);
147+
scope.setSDKProcessingMetadata({ request: req });
148148

149149
try {
150150
return await origDataFetcher.apply(this, args);
151151
} catch (e) {
152152
// Since we finish the span before the error can bubble up and trigger the handlers in `registerErrorInstrumentation`
153153
// that set the transaction status, we need to manually set the status of the span & transaction
154154
dataFetcherSpan.setStatus('internal_error');
155-
requestTransaction?.setStatus('internal_error');
155+
previousSpan?.setStatus('internal_error');
156156
throw e;
157157
} finally {
158158
dataFetcherSpan.finish();
159+
scope.setSpan(previousSpan);
159160
if (!platformSupportsStreaming()) {
160161
await flushQueue();
161162
}

packages/nextjs/src/server/wrapAppGetInitialPropsWithSentry.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ export function wrapAppGetInitialPropsWithSentry(origAppGetInitialProps: AppGetI
3131
const { req, res } = context.ctx;
3232

3333
const errorWrappedAppGetInitialProps = withErrorInstrumentation(wrappingTarget);
34-
const options = getCurrentHub().getClient()?.getOptions();
34+
const hub = getCurrentHub();
35+
const options = hub.getClient()?.getOptions();
3536

3637
// Generally we can assume that `req` and `res` are always defined on the server:
3738
// https://nextjs.org/docs/api-reference/data-fetching/get-initial-props#context-object
@@ -51,7 +52,7 @@ export function wrapAppGetInitialPropsWithSentry(origAppGetInitialProps: AppGetI
5152
};
5253
} = await tracedGetInitialProps.apply(thisArg, args);
5354

54-
const requestTransaction = getTransactionFromRequest(req);
55+
const requestTransaction = getTransactionFromRequest(req) ?? hub.getScope().getTransaction();
5556

5657
// Per definition, `pageProps` is not optional, however an increased amount of users doesn't seem to call
5758
// `App.getInitialProps(appContext)` in their custom `_app` pages which is required as per

packages/nextjs/src/server/wrapErrorGetInitialPropsWithSentry.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ export function wrapErrorGetInitialPropsWithSentry(
3434
const { req, res } = context;
3535

3636
const errorWrappedGetInitialProps = withErrorInstrumentation(wrappingTarget);
37-
const options = getCurrentHub().getClient()?.getOptions();
37+
const hub = getCurrentHub();
38+
const options = hub.getClient()?.getOptions();
3839

3940
// Generally we can assume that `req` and `res` are always defined on the server:
4041
// https://nextjs.org/docs/api-reference/data-fetching/get-initial-props#context-object
@@ -52,7 +53,7 @@ export function wrapErrorGetInitialPropsWithSentry(
5253
_sentryBaggage?: string;
5354
} = await tracedGetInitialProps.apply(thisArg, args);
5455

55-
const requestTransaction = getTransactionFromRequest(req);
56+
const requestTransaction = getTransactionFromRequest(req) ?? hub.getScope().getTransaction();
5657
if (requestTransaction) {
5758
errorGetInitialProps._sentryTraceData = requestTransaction.toTraceparent();
5859

packages/nextjs/src/server/wrapGetInitialPropsWithSentry.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ export function wrapGetInitialPropsWithSentry(origGetInitialProps: GetInitialPro
3030
const { req, res } = context;
3131

3232
const errorWrappedGetInitialProps = withErrorInstrumentation(wrappingTarget);
33-
const options = getCurrentHub().getClient()?.getOptions();
33+
const hub = getCurrentHub();
34+
const options = hub.getClient()?.getOptions();
3435

3536
// Generally we can assume that `req` and `res` are always defined on the server:
3637
// https://nextjs.org/docs/api-reference/data-fetching/get-initial-props#context-object
@@ -48,7 +49,7 @@ export function wrapGetInitialPropsWithSentry(origGetInitialProps: GetInitialPro
4849
_sentryBaggage?: string;
4950
} = await tracedGetInitialProps.apply(thisArg, args);
5051

51-
const requestTransaction = getTransactionFromRequest(req);
52+
const requestTransaction = getTransactionFromRequest(req) ?? hub.getScope().getTransaction();
5253
if (requestTransaction) {
5354
initialProps._sentryTraceData = requestTransaction.toTraceparent();
5455

packages/nextjs/src/server/wrapGetServerSidePropsWithSentry.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ export function wrapGetServerSidePropsWithSentry(
3131
const { req, res } = context;
3232

3333
const errorWrappedGetServerSideProps = withErrorInstrumentation(wrappingTarget);
34-
const options = getCurrentHub().getClient()?.getOptions();
34+
const hub = getCurrentHub();
35+
const options = hub.getClient()?.getOptions();
3536

3637
if (hasTracingEnabled() && options?.instrumenter === 'sentry') {
3738
const tracedGetServerSideProps = withTracedServerSideDataFetcher(errorWrappedGetServerSideProps, req, res, {
@@ -45,7 +46,7 @@ export function wrapGetServerSidePropsWithSentry(
4546
>);
4647

4748
if (serverSideProps && 'props' in serverSideProps) {
48-
const requestTransaction = getTransactionFromRequest(req);
49+
const requestTransaction = getTransactionFromRequest(req) ?? hub.getScope().getTransaction();
4950
if (requestTransaction) {
5051
serverSideProps.props._sentryTraceData = requestTransaction.toTraceparent();
5152

packages/nextjs/test/config/wrappers.test.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { IncomingMessage, ServerResponse } from 'http';
66
import { wrapGetInitialPropsWithSentry, wrapGetServerSidePropsWithSentry } from '../../src/server';
77

88
const startTransactionSpy = jest.spyOn(SentryCore, 'startTransaction');
9+
const originalGetCurrentHub = jest.requireActual('@sentry/node').getCurrentHub;
910

1011
// The wrap* functions require the hub to have tracing extensions. This is normally called by the NodeClient
1112
// constructor but the client isn't used in these tests.
@@ -21,13 +22,18 @@ describe('data-fetching function wrappers', () => {
2122
req = { headers: {}, url: 'http://dogs.are.great/tricks/kangaroo' } as IncomingMessage;
2223
res = { end: jest.fn() } as unknown as ServerResponse;
2324

24-
jest.spyOn(SentryCore, 'hasTracingEnabled').mockReturnValueOnce(true);
25-
jest.spyOn(SentryNode, 'getCurrentHub').mockReturnValueOnce({
26-
getClient: () =>
25+
jest.spyOn(SentryCore, 'hasTracingEnabled').mockReturnValue(true);
26+
jest.spyOn(SentryNode, 'getCurrentHub').mockImplementation(() => {
27+
const hub = originalGetCurrentHub();
28+
29+
hub.getClient = () =>
2730
({
2831
getOptions: () => ({ instrumenter: 'sentry' }),
29-
} as any),
30-
} as any);
32+
getDsn: () => {},
33+
} as any);
34+
35+
return hub;
36+
});
3137
});
3238

3339
afterEach(() => {

packages/sveltekit/src/vite/sentryVitePlugins.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export async function sentrySvelteKit(options: SentrySvelteKitPluginOptions = {}
7878
);
7979
}
8080

81-
if (mergedOptions.autoUploadSourceMaps) {
81+
if (mergedOptions.autoUploadSourceMaps && process.env.NODE_ENV !== 'development') {
8282
const pluginOptions = {
8383
...mergedOptions.sourceMapsUploadOptions,
8484
debug: mergedOptions.debug, // override the plugin's debug flag with the one from the top-level options

packages/sveltekit/test/vite/sentrySvelteKitPlugins.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,19 @@ describe('sentryVite()', () => {
3838
expect(plugins).toHaveLength(1);
3939
});
4040

41+
it("doesn't return the custom sentry source maps plugin if `NODE_ENV` is development", async () => {
42+
const previousEnv = process.env.NODE_ENV;
43+
44+
process.env.NODE_ENV = 'development';
45+
const plugins = await sentrySvelteKit({ autoUploadSourceMaps: true, autoInstrument: true });
46+
const instrumentPlugin = plugins[0];
47+
48+
expect(plugins).toHaveLength(1);
49+
expect(instrumentPlugin.name).toEqual('sentry-auto-instrumentation');
50+
51+
process.env.NODE_ENV = previousEnv;
52+
});
53+
4154
it("doesn't return the auto instrument plugin if autoInstrument is `false`", async () => {
4255
const plugins = await sentrySvelteKit({ autoInstrument: false });
4356
expect(plugins).toHaveLength(1);

0 commit comments

Comments
 (0)