Skip to content

Commit 2e41f5e

Browse files
mydeaandreiborza
andauthored
fix(node): Avoid double-wrapping http module (#16177)
@s1gr1d and @Lms24 figured out that the duplicate spans we sometimes see are related to esm and the http module. Especially, it seems to be related to us using the `stealthWrap` function to wrap `server.emit` for request isolation purposes. While we still don't really know _why_ this is making such problems, this PR seems to fix it (at least in integration tests) by avoiding using import-in-the-middle here, and instead using diagnostics channel with good old-fashioned monkey patching on the passed-in `server` instance. Some note: We need to make sure to still call this in the otel-wrapping code of `init()`, otherwise there are weird timing issues in top-level-import scenarios 😬 Hopefully fixes #15830, and fixes #15803 --------- Co-authored-by: Andrei <[email protected]>
1 parent 0335cf7 commit 2e41f5e

File tree

7 files changed

+194
-333
lines changed

7 files changed

+194
-333
lines changed

CHANGELOG.md

+22
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,28 @@
1010

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

13+
### Important Changes
14+
15+
- **fix(node): Avoid double-wrapping http module ([#16177](https://github.com/getsentry/sentry-javascript/pull/16177))**
16+
17+
When running your application in ESM mode, there have been scenarios that resulted in the `http`/`https` emitting duplicate spans for incoming requests. This was apparently caused by us double-wrapping the modules for incoming request isolation.
18+
19+
In order to solve this problem, the modules are no longer monkey patched by us for request isolation. Instead, we register diagnostics*channel hooks to handle request isolation now.
20+
While this is generally not expected to break anything, there is one tiny change that \_may* affect you if you have been relying on very specific functionality:
21+
22+
The `ignoreOutgoingRequests` option of `httpIntegration` receives the `RequestOptions` as second argument. This type is not changed, however due to how the wrapping now works, we no longer pass through the full RequestOptions, but re-construct this partially based on the generated request. For the vast majority of cases, this should be fine, but for the sake of completeness, these are the only fields that may be available there going forward - other fields that _may_ have existed before may no longer be set:
23+
24+
```ts
25+
ignoreOutgoingRequests(url: string, {
26+
method: string;
27+
protocol: string;
28+
host: string;
29+
hostname: string; // same as host
30+
path: string;
31+
headers: OutgoingHttpHeaders;
32+
})
33+
```
34+
1335
## 9.15.0
1436

1537
### Important Changes

dev-packages/e2e-tests/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"clean": "rimraf tmp node_modules && yarn clean:test-applications && yarn clean:pnpm",
1717
"ci:build-matrix": "ts-node ./lib/getTestMatrix.ts",
1818
"ci:build-matrix-optional": "ts-node ./lib/getTestMatrix.ts --optional=true",
19-
"clean:test-applications": "rimraf --glob test-applications/**/{node_modules,dist,build,.next,.nuxt,.sveltekit,.react-router,pnpm-lock.yaml,.last-run.json,test-results}",
19+
"clean:test-applications": "rimraf --glob test-applications/**/{node_modules,dist,build,.next,.nuxt,.sveltekit,.react-router,.astro,.output,pnpm-lock.yaml,.last-run.json,test-results}",
2020
"clean:pnpm": "pnpm store prune"
2121
},
2222
"devDependencies": {

dev-packages/e2e-tests/test-applications/astro-5/tests/tracing.dynamic.test.ts

-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ test.describe('tracing in dynamically rendered (ssr) routes', () => {
6262
});
6363

6464
expect(serverPageRequestTxn).toMatchObject({
65-
breadcrumbs: expect.any(Array),
6665
contexts: {
6766
app: expect.any(Object),
6867
cloud_resource: expect.any(Object),

dev-packages/node-integration-tests/suites/express/with-http/test.ts

+24-32
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,28 @@ describe('express with http import', () => {
66
cleanupChildProcesses();
77
});
88

9-
createEsmAndCjsTests(
10-
__dirname,
11-
'scenario.mjs',
12-
'instrument.mjs',
13-
(createRunner, test) => {
14-
test('it works when importing the http module', async () => {
15-
const runner = createRunner()
16-
.expect({
17-
transaction: {
18-
transaction: 'GET /test2',
19-
},
20-
})
21-
.expect({
22-
transaction: {
23-
transaction: 'GET /test',
24-
},
25-
})
26-
.expect({
27-
transaction: {
28-
transaction: 'GET /test3',
29-
},
30-
})
31-
.start();
32-
await runner.makeRequest('get', '/test');
33-
await runner.makeRequest('get', '/test3');
34-
await runner.completed();
35-
});
36-
// TODO: This is failing on ESM because importing http is triggering the http spans twice :(
37-
// We need to fix this!
38-
},
39-
{ failsOnEsm: true },
40-
);
9+
createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createRunner, test) => {
10+
test('it works when importing the http module', async () => {
11+
const runner = createRunner()
12+
.expect({
13+
transaction: {
14+
transaction: 'GET /test2',
15+
},
16+
})
17+
.expect({
18+
transaction: {
19+
transaction: 'GET /test',
20+
},
21+
})
22+
.expect({
23+
transaction: {
24+
transaction: 'GET /test3',
25+
},
26+
})
27+
.start();
28+
await runner.makeRequest('get', '/test');
29+
await runner.makeRequest('get', '/test3');
30+
await runner.completed();
31+
});
32+
});
4133
});

0 commit comments

Comments
 (0)