Skip to content

Commit f29829d

Browse files
committed
only init nextjs server SDK if it hasn't already been initialized
1 parent 33ab501 commit f29829d

File tree

2 files changed

+74
-51
lines changed

2 files changed

+74
-51
lines changed

packages/nextjs/src/index.server.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { RewriteFrames } from '@sentry/integrations';
2-
import { configureScope, init as nodeInit, Integrations } from '@sentry/node';
2+
import { configureScope, getCurrentHub, init as nodeInit, Integrations } from '@sentry/node';
3+
import { logger } from '@sentry/utils';
34

45
import { instrumentServer } from './utils/instrumentServer';
56
import { MetadataBuilder } from './utils/metadataBuilder';
@@ -14,6 +15,17 @@ export { ErrorBoundary, withErrorBoundary } from '@sentry/react';
1415

1516
/** Inits the Sentry NextJS SDK on node. */
1617
export function init(options: NextjsOptions): void {
18+
if (options.debug) {
19+
logger.enable();
20+
}
21+
22+
logger.log('Initializing SDK...');
23+
24+
if (sdkAlreadyInitialized()) {
25+
logger.log('SDK already initialized');
26+
return;
27+
}
28+
1729
const metadataBuilder = new MetadataBuilder(options, ['nextjs', 'node']);
1830
metadataBuilder.addSdkMetadata();
1931
options.environment = options.environment || process.env.NODE_ENV;
@@ -26,6 +38,13 @@ export function init(options: NextjsOptions): void {
2638
configureScope(scope => {
2739
scope.setTag('runtime', 'node');
2840
});
41+
42+
logger.log('SDK successfully initialized');
43+
}
44+
45+
function sdkAlreadyInitialized(): boolean {
46+
const hub = getCurrentHub();
47+
return !!hub.getClient();
2948
}
3049

3150
const SOURCEMAP_FILENAME_REGEX = /^.*\/\.next\//;

packages/nextjs/test/index.server.test.ts

Lines changed: 54 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,52 @@
11
import { RewriteFrames } from '@sentry/integrations';
2-
import { Integrations } from '@sentry/node';
2+
import * as SentryNode from '@sentry/node';
33
import { Integration } from '@sentry/types';
4+
import { getGlobalObject } from '@sentry/utils';
45

56
import { init, Scope } from '../src/index.server';
67
import { NextjsOptions } from '../src/utils/nextjsOptions';
78

8-
const mockInit = jest.fn();
9-
let configureScopeCallback: (scope: Scope) => void = () => undefined;
9+
const { Integrations } = SentryNode;
1010

11-
jest.mock('@sentry/node', () => {
12-
const actual = jest.requireActual('@sentry/node');
13-
return {
14-
...actual,
15-
init: (options: NextjsOptions) => {
16-
mockInit(options);
17-
},
18-
configureScope: (callback: (scope: Scope) => void) => {
19-
configureScopeCallback = callback;
20-
},
21-
};
22-
});
11+
const global = getGlobalObject();
12+
13+
let configureScopeCallback: (scope: Scope) => void = () => undefined;
14+
jest.spyOn(SentryNode, 'configureScope').mockImplementation(callback => (configureScopeCallback = callback));
15+
const nodeInit = jest.spyOn(SentryNode, 'init');
2316

2417
describe('Server init()', () => {
2518
afterEach(() => {
26-
mockInit.mockClear();
19+
nodeInit.mockClear();
2720
configureScopeCallback = () => undefined;
21+
global.__SENTRY__.hub = undefined;
2822
});
2923

3024
it('inits the Node SDK', () => {
31-
expect(mockInit).toHaveBeenCalledTimes(0);
25+
expect(nodeInit).toHaveBeenCalledTimes(0);
3226
init({});
33-
expect(mockInit).toHaveBeenCalledTimes(1);
34-
expect(mockInit).toHaveBeenLastCalledWith({
35-
_metadata: {
36-
sdk: {
37-
name: 'sentry.javascript.nextjs',
38-
version: expect.any(String),
39-
packages: expect.any(Array),
27+
expect(nodeInit).toHaveBeenCalledTimes(1);
28+
expect(nodeInit).toHaveBeenLastCalledWith(
29+
expect.objectContaining({
30+
_metadata: {
31+
sdk: {
32+
name: 'sentry.javascript.nextjs',
33+
version: expect.any(String),
34+
packages: expect.any(Array),
35+
},
4036
},
41-
},
42-
autoSessionTracking: false,
43-
environment: 'test',
44-
integrations: [expect.any(RewriteFrames)],
45-
});
37+
autoSessionTracking: false,
38+
environment: 'test',
39+
integrations: [expect.any(RewriteFrames)],
40+
}),
41+
);
42+
});
43+
44+
it("doesn't reinitialize the node SDK if already initialized", () => {
45+
expect(nodeInit).toHaveBeenCalledTimes(0);
46+
init({});
47+
expect(nodeInit).toHaveBeenCalledTimes(1);
48+
init({});
49+
expect(nodeInit).toHaveBeenCalledTimes(1);
4650
});
4751

4852
it('sets runtime on scope', () => {
@@ -57,45 +61,45 @@ describe('Server init()', () => {
5761
it('adds RewriteFrames integration by default', () => {
5862
init({});
5963

60-
const reactInitOptions: NextjsOptions = mockInit.mock.calls[0][0];
61-
expect(reactInitOptions.integrations).toHaveLength(1);
62-
const integrations = reactInitOptions.integrations as Integration[];
64+
const nodeInitOptions: NextjsOptions = nodeInit.mock.calls[0][0]!;
65+
expect(nodeInitOptions.integrations).toHaveLength(1);
66+
const integrations = nodeInitOptions.integrations as Integration[];
6367
expect(integrations[0]).toEqual(expect.any(RewriteFrames));
6468
});
6569

6670
it('adds Http integration by default if tracesSampleRate is set', () => {
6771
init({ tracesSampleRate: 1.0 });
6872

69-
const reactInitOptions: NextjsOptions = mockInit.mock.calls[0][0];
70-
expect(reactInitOptions.integrations).toHaveLength(2);
71-
const integrations = reactInitOptions.integrations as Integration[];
73+
const nodeInitOptions: NextjsOptions = nodeInit.mock.calls[0][0]!;
74+
expect(nodeInitOptions.integrations).toHaveLength(2);
75+
const integrations = nodeInitOptions.integrations as Integration[];
7276
expect(integrations[1]).toEqual(expect.any(Integrations.Http));
7377
});
7478

7579
it('adds Http integration by default if tracesSampler is set', () => {
7680
init({ tracesSampler: () => true });
7781

78-
const reactInitOptions: NextjsOptions = mockInit.mock.calls[0][0];
79-
expect(reactInitOptions.integrations).toHaveLength(2);
80-
const integrations = reactInitOptions.integrations as Integration[];
82+
const nodeInitOptions: NextjsOptions = nodeInit.mock.calls[0][0]!;
83+
expect(nodeInitOptions.integrations).toHaveLength(2);
84+
const integrations = nodeInitOptions.integrations as Integration[];
8185
expect(integrations[1]).toEqual(expect.any(Integrations.Http));
8286
});
8387

8488
it('adds Http integration with tracing true', () => {
8589
init({ tracesSampleRate: 1.0 });
86-
const reactInitOptions: NextjsOptions = mockInit.mock.calls[0][0];
87-
expect(reactInitOptions.integrations).toHaveLength(2);
90+
const nodeInitOptions: NextjsOptions = nodeInit.mock.calls[0][0]!;
91+
expect(nodeInitOptions.integrations).toHaveLength(2);
8892

89-
const integrations = reactInitOptions.integrations as Integration[];
93+
const integrations = nodeInitOptions.integrations as Integration[];
9094
expect((integrations[1] as any)._tracing).toBe(true);
9195
});
9296

9397
it('supports passing integration through options', () => {
9498
init({ tracesSampleRate: 1.0, integrations: [new Integrations.Console()] });
95-
const reactInitOptions: NextjsOptions = mockInit.mock.calls[0][0];
96-
expect(reactInitOptions.integrations).toHaveLength(3);
99+
const nodeInitOptions: NextjsOptions = nodeInit.mock.calls[0][0]!;
100+
expect(nodeInitOptions.integrations).toHaveLength(3);
97101

98-
const integrations = reactInitOptions.integrations as Integration[];
102+
const integrations = nodeInitOptions.integrations as Integration[];
99103
expect(integrations).toEqual([
100104
expect.any(Integrations.Console),
101105
expect.any(RewriteFrames),
@@ -110,9 +114,9 @@ describe('Server init()', () => {
110114
integrations: [new Integrations.Http({ tracing: false })],
111115
});
112116

113-
const reactInitOptions: NextjsOptions = mockInit.mock.calls[0][0];
114-
expect(reactInitOptions.integrations).toHaveLength(2);
115-
const integrations = reactInitOptions.integrations as Integration[];
117+
const nodeInitOptions: NextjsOptions = nodeInit.mock.calls[0][0]!;
118+
expect(nodeInitOptions.integrations).toHaveLength(2);
119+
const integrations = nodeInitOptions.integrations as Integration[];
116120
expect(integrations[0] as InstanceType<typeof Integrations.Http>).toEqual(
117121
expect.objectContaining({ _breadcrumbs: true, _tracing: true, name: 'Http' }),
118122
);
@@ -124,9 +128,9 @@ describe('Server init()', () => {
124128
integrations: [new Integrations.Http({ tracing: false })],
125129
});
126130

127-
const reactInitOptions: NextjsOptions = mockInit.mock.calls[0][0];
128-
expect(reactInitOptions.integrations).toHaveLength(2);
129-
const integrations = reactInitOptions.integrations as Integration[];
131+
const nodeInitOptions: NextjsOptions = nodeInit.mock.calls[0][0]!;
132+
expect(nodeInitOptions.integrations).toHaveLength(2);
133+
const integrations = nodeInitOptions.integrations as Integration[];
130134
expect(integrations[0] as InstanceType<typeof Integrations.Http>).toEqual(
131135
expect.objectContaining({ _breadcrumbs: true, _tracing: true, name: 'Http' }),
132136
);

0 commit comments

Comments
 (0)