Skip to content

Commit f4c0ebd

Browse files
committed
Merge branch 'master' of https://github.com/getsentry/sentry-javascript into onur/test-bundles
2 parents 2d00b51 + 6809dd2 commit f4c0ebd

File tree

27 files changed

+598
-367
lines changed

27 files changed

+598
-367
lines changed

.github/ISSUE_TEMPLATE/bug.yml

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
name: 🐞 Bug Report
2+
description: Tell us about something that's not working the way we (probably) intend.
3+
labels: 'Type: Bug'
4+
body:
5+
- type: checkboxes
6+
attributes:
7+
label: Is there an existing issue for this?
8+
description: Please search to see if an issue already exists for the bug you encountered.
9+
options:
10+
- label: I have checked for existing issues https://github.com/getsentry/sentry-javascript/issues
11+
required: true
12+
- label: I have reviewed the documentation https://docs.sentry.io/
13+
required: true
14+
- label: I am using the latest SDK release https://github.com/getsentry/sentry-javascript/releases
15+
required: true
16+
- type: dropdown
17+
id: type
18+
attributes:
19+
label: How do you use Sentry?
20+
options:
21+
- Sentry Saas (sentry.io)
22+
- Self-hosted/on-premise
23+
validations:
24+
required: true
25+
- type: dropdown
26+
id: package
27+
attributes:
28+
label: Which package are you using?
29+
options:
30+
- "@sentry/angular"
31+
- "@sentry/browser"
32+
- "@sentry/ember"
33+
- "@sentry/gatsby"
34+
- "@sentry/nextjs"
35+
- "@sentry/node"
36+
- "@sentry/react"
37+
- "@sentry/serverless"
38+
- "@sentry/vue"
39+
- "@sentry/wasm"
40+
validations:
41+
required: true
42+
- type: input
43+
id: sdk-version
44+
attributes:
45+
label: SDK Version
46+
description: What version of the SDK are you using?
47+
placeholder: ex. 1.5.2
48+
validations:
49+
required: true
50+
- type: input
51+
id: framework-version
52+
attributes:
53+
label: Framework Version
54+
description: If you're using one of our framework-specific SDKs (`@sentry/react`, for example), what version of the framework are you using?
55+
placeholder: ex. 1.5.2
56+
- type: input
57+
id: link-to-sentry
58+
attributes:
59+
label: Link to Sentry event
60+
description: If applicable, provide a link to the affected event from your Sentry account. The event will only be viewable by Sentry staff.
61+
placeholder: https://sentry.io/organizations/<org-slug>/issues/<issue-id>/events/<event-id>/?project=<project-id>
62+
- type: textarea
63+
id: repro
64+
attributes:
65+
label: Steps to Reproduce
66+
description: How can we see what you're seeing? Specific is terrific.
67+
placeholder: |-
68+
1. What
69+
2. you
70+
3. did.
71+
validations:
72+
required: true
73+
- type: textarea
74+
id: expected
75+
attributes:
76+
label: Expected Result
77+
validations:
78+
required: true
79+
- type: textarea
80+
id: actual
81+
attributes:
82+
label: Actual Result
83+
description: Logs? Screenshots? Yes, please.
84+
validations:
85+
required: true
86+
- type: markdown
87+
attributes:
88+
value: |-
89+
## Thanks 🙏
90+
validations:
91+
required: false

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 0 additions & 33 deletions
This file was deleted.

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
blank_issues_enabled: false
2+
contact_links:
3+
- name: Ask a question
4+
url: https://github.com/getsentry/sentry-javascript/issues
5+
about: Ask questions and discuss with other community members

.github/ISSUE_TEMPLATE/feature.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: 💡 Feature Request
2+
description: Create a feature request for a sentry-javascript SDK.
3+
labels: 'Type: Improvement'
4+
body:
5+
- type: markdown
6+
attributes:
7+
value: Thanks for taking the time to file a feature request! Please fill out this form as completely as possible.
8+
- type: textarea
9+
id: problem
10+
attributes:
11+
label: Problem Statement
12+
description: A clear and concise description of what you want and what your use case is.
13+
placeholder: |-
14+
I want to make whirled peas, but Sentry doesn't blend.
15+
validations:
16+
required: true
17+
- type: textarea
18+
id: expected
19+
attributes:
20+
label: Solution Brainstorm
21+
description: We know you have bright ideas to share ... share away, friend.
22+
placeholder: |-
23+
Add a blender to Sentry.
24+
validations:
25+
required: true
26+
- type: markdown
27+
attributes:
28+
value: |-
29+
## Thanks 🙏
30+
Check our [triage docs](https://open.sentry.io/triage/) for what to expect next.

MIGRATION.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ We deprecated the `Status` enum in `@sentry/tracing` and it will be removed in t
7272

7373
```js
7474
// New in 6.17.0:
75-
import { spanStatusfromHttpCode } from '@sentry/utils';
75+
import { spanStatusfromHttpCode } from '@sentry/tracing';
7676

7777
const status = spanStatusfromHttpCode(403);
7878

packages/browser/src/eventbuilder.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,10 @@ export function eventFromString(
143143
};
144144

145145
if (options.attachStacktrace && syntheticException) {
146-
event.stacktrace = {
147-
frames: parseStackFrames(syntheticException),
148-
};
146+
const frames = parseStackFrames(syntheticException);
147+
if (frames.length) {
148+
event.stacktrace = { frames };
149+
}
149150
}
150151

151152
return event;

packages/browser/src/parsers.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,10 @@ export function eventFromPlainObject(
5454
};
5555

5656
if (syntheticException) {
57-
event.stacktrace = {
58-
frames: parseStackFrames(syntheticException),
59-
};
57+
const frames = parseStackFrames(syntheticException);
58+
if (frames.length) {
59+
event.stacktrace = { frames };
60+
}
6061
}
6162

6263
return event;

packages/nextjs/src/index.server.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,20 @@ const domain = domainModule as typeof domainModule & { active: (domainModule.Dom
2323
// During build, the main process is invoked by
2424
// `node next build`
2525
// and child processes are invoked as
26-
// `node <path>/node_modules/jest-worker/build/workers/processChild.js`,
27-
// whereas at runtime the process is invoked as
26+
// `node <path>/node_modules/.../jest-worker/processChild.js`.
27+
// The former is (obviously) easy to recognize, but the latter could happen at runtime as well. Fortunately, the main
28+
// process hits this file before any of the child processes do, so we're able to set an env variable which the child
29+
// processes can then check. During runtime, the main process is invoked as
2830
// `node next start`
2931
// or
30-
// `node /var/runtime/index.js`.
31-
const isBuild = new RegExp('build').test(process.argv.toString());
32+
// `node /var/runtime/index.js`,
33+
// so we never drop into the `if` in the first place.
34+
let isBuild = false;
35+
if (process.argv.includes('build') || process.env.SENTRY_BUILD_PHASE) {
36+
process.env.SENTRY_BUILD_PHASE = 'true';
37+
isBuild = true;
38+
}
39+
3240
const isVercel = !!process.env.VERCEL;
3341

3442
/** Inits the Sentry NextJS SDK on node. */

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { BaseClient } from '@sentry/core';
21
import { RewriteFrames } from '@sentry/integrations';
32
import * as SentryNode from '@sentry/node';
43
import { getCurrentHub, NodeClient } from '@sentry/node';
@@ -17,7 +16,6 @@ const global = getGlobalObject();
1716
(global as typeof global & { __rewriteFramesDistDir__: string }).__rewriteFramesDistDir__ = '.next';
1817

1918
const nodeInit = jest.spyOn(SentryNode, 'init');
20-
const captureEvent = jest.spyOn(BaseClient.prototype, 'captureEvent');
2119
const logError = jest.spyOn(logger, 'error');
2220

2321
describe('Server init()', () => {
@@ -91,7 +89,7 @@ describe('Server init()', () => {
9189
expect(currentScope._tags.vercel).toBeUndefined();
9290
});
9391

94-
it('adds 404 transaction filter', () => {
92+
it('adds 404 transaction filter', async () => {
9593
init({
9694
dsn: 'https://[email protected]/12312012',
9795
tracesSampleRate: 1.0,
@@ -102,8 +100,10 @@ describe('Server init()', () => {
102100
const transaction = hub.startTransaction({ name: '/404' });
103101
transaction.finish();
104102

103+
// We need to flush because the event processor pipeline is async whereas transaction.finish() is sync.
104+
await SentryNode.flush();
105+
105106
expect(sendEvent).not.toHaveBeenCalled();
106-
expect(captureEvent.mock.results[0].value).toBeUndefined();
107107
expect(logError).toHaveBeenCalledWith(new SentryError('An event processor returned null, will not send event.'));
108108
});
109109

packages/nextjs/test/integration/test/server/tracingHttp.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ module.exports = async ({ url: urlBase, argv }) => {
4040
'tracingHttp',
4141
);
4242

43-
await getAsync(url);
43+
// The `true` causes `getAsync` to rewrap `http.get` in next 12, since it will have been overwritten by the import of
44+
// `nock` above. See https://github.com/getsentry/sentry-javascript/pull/4619.
45+
// TODO: see note in `getAsync` about removing the boolean
46+
await getAsync(url, true);
4447
await sleep(250);
4548

4649
assert.ok(capturedRequest.isDone(), 'Did not intercept expected request');

packages/nextjs/test/integration/test/utils/server.js

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
const { get } = require('http');
22
const nock = require('nock');
3+
const nodeSDK = require('@sentry/node');
34
const { logIf, parseEnvelope } = require('./common');
45

56
Error.stackTraceLimit = Infinity;
67

7-
const getAsync = url => {
8+
const getAsync = (url, rewrap = false) => {
9+
// Depending on what version of Nextjs we're testing, the wrapping which happens in the `Http` integration may have
10+
// happened too early and gotten overwritten by `nock`. This redoes the wrapping if so.
11+
//
12+
// TODO: This works but is pretty hacky in that it has the potential to wrap things multiple times, more even than the
13+
// double-wrapping which is discussed at length in the comment in `ensureWrappedGet` below, which is why we need
14+
// `rewrap`. Once we fix `fill` to not wrap things twice, we should be able to take this out and just always call
15+
// `ensureWrappedGet`.
16+
const wrappedGet = rewrap ? ensureWrappedGet(get, url) : get;
17+
818
return new Promise((resolve, reject) => {
9-
get(url, res => {
19+
wrappedGet(url, res => {
1020
res.setEncoding('utf8');
1121
let rawData = '';
1222
res.on('data', chunk => {
@@ -99,6 +109,60 @@ const objectMatches = (actual, expected) => {
99109
return true;
100110
};
101111

112+
/**
113+
* Rewrap `http.get` if the wrapped version has been overridden by `nock`.
114+
*
115+
* This is only relevant for Nextjs >= 12.1, which changed when `_app` is initialized, which in turn changed the order
116+
* in which our SDK and `nock` wrap `http.get`. See https://github.com/getsentry/sentry-javascript/pull/4619.
117+
*
118+
* TODO: We'll have to do this for `ClientRequest` also if we decide to start wrapping that.
119+
* TODO: Can we fix the wrapping-things-twice problem discussed in the comment below?
120+
*/
121+
function ensureWrappedGet(importedGet, url) {
122+
// we always test against the latest minor for any given Nextjs major version, so if we're testing Next 12, it's
123+
// definitely at least 12.1, making this check against the major version sufficient
124+
if (Number(process.env.NEXTJS_VERSION) < 12) {
125+
return importedGet;
126+
}
127+
128+
// As of Next 12.1, creating a `NextServer` instance (which we do immediately upon starting this test runner) loads
129+
// `_app`, which has the effect of initializing the SDK. So, unless something's gone wrong, we should always be able
130+
// to find the integration
131+
let httpIntegration;
132+
try {
133+
httpIntegration = nodeSDK.getCurrentHub().getClient().getIntegration(nodeSDK.Integrations.Http);
134+
} catch (err) {
135+
console.warn(`Warning: Sentry SDK not set up at \`NextServer\` initialization. Request URL: ${url}`);
136+
return importedGet;
137+
}
138+
139+
// This rewraps `http.get` and `http.request`, which, at this point, look like `nockWrapper(sentryWrapper(get))` and
140+
// `nockWrapper(sentryWrapper(request))`. By the time we're done with this function, they'll look like
141+
// `sentryWrapper(nockWrapper(sentryWrapper(get)))` and `sentryWrapper(nockWrapper(sentryWrapper(request)))`,
142+
// respectively. Though this seems less than ideal, we don't have to worry about our instrumentation being
143+
// (meaningfully) called twice because:
144+
//
145+
// 1) As long as we set up a `nock` interceptor for any outgoing http request, `nock`'s wrapper will call a replacement
146+
// function for that request rather than call the function it's wrapping (in other words, it will look more like
147+
// `sentryWrapper(nockWrapper(getSubstitute))` than `sentryWrapper(nockWrapper(sentryWrapper(get)))`), which means
148+
// our code is only called once.
149+
// 2) In cases where we don't set up an interceptor (such as for the `wrappedGet` call in `getAsync` above), it's true
150+
// that we can end up with `sentryWrapper(nockWrapper(sentryWrapper(get)))`, meaning our wrapper code will run
151+
// twice. For now that's okay because in those cases we're not in the middle of a transactoin and therefore
152+
// the two wrappers' respective attempts to start spans will both no-op.
153+
//
154+
// TL; DR - if the double-wrapping means you're seeing two spans where you really only want one, set up a nock
155+
// interceptor for the request.
156+
//
157+
// TODO: add in a "don't do this twice" check (in `fill`, maybe moved from `wrap`), so that we don't wrap the outer
158+
// wrapper with a third wrapper
159+
httpIntegration.setupOnce();
160+
161+
// now that we've rewrapped it, grab the correct version of `get` for use in our tests
162+
const httpModule = require('http');
163+
return httpModule.get;
164+
}
165+
102166
module.exports = {
103167
getAsync,
104168
interceptEventRequest,

packages/nextjs/test/run-integration-tests.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ mv next.config.js next.config.js.bak
3232

3333
for NEXTJS_VERSION in 10 11 12; do
3434

35+
# export this to the env so that we can behave differently depending on which version of next we're testing, without
36+
# having to pass this value from function to function to function to the one spot, deep in some callstack, where we
37+
# actually need it
38+
export NEXTJS_VERSION=$NEXTJS_VERSION
39+
3540
# Next 10 requires at least Node v10
3641
if [ "$NODE_MAJOR" -lt "10" ]; then
3742
echo "[nextjs] Next.js is not compatible with versions of Node older than v10. Current version $NODE_VERSION"

packages/node/src/backend.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export class NodeBackend extends BaseBackend<NodeOptions> {
1616
*/
1717
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
1818
public eventFromException(exception: any, hint?: EventHint): PromiseLike<Event> {
19-
return eventFromException(this._options, exception, hint);
19+
return eventFromException(exception, hint);
2020
}
2121

2222
/**

0 commit comments

Comments
 (0)