Skip to content

Commit a22ddb4

Browse files
committed
feat(nextjs): Instrument outgoing http requests
1 parent b64b2ae commit a22ddb4

File tree

3 files changed

+54
-1
lines changed

3 files changed

+54
-1
lines changed

packages/nextjs/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
},
6767
"dependencies": {
6868
"@opentelemetry/api": "1.7.0",
69+
"@opentelemetry/instrumentation-http": "0.48.0",
6970
"@rollup/plugin-commonjs": "24.0.0",
7071
"@sentry/core": "8.0.0-beta.2",
7172
"@sentry/node": "8.0.0-beta.2",
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
2+
import { httpIntegration as originalHttpIntegration } from '@sentry/node';
3+
import type { IntegrationFn } from '@sentry/types';
4+
5+
/**
6+
* Next.js handles incoming requests itself,
7+
* but it does not handle outgoing requests.
8+
* Today, it is not possible to use the HttpInstrumentation for only outgoing requests -
9+
* until https://github.com/open-telemetry/opentelemetry-js/pull/4643 is merged & released.
10+
* So in the meanwhile, we extend the base HttpInstrumentation to not wrap incoming requests.
11+
*/
12+
class CustomNextjsHttpIntegration extends HttpInstrumentation {
13+
// Instead of the default behavior, we just don't do any wrapping for incoming requests
14+
protected _getPatchIncomingRequestFunction(_component: 'http' | 'https') {
15+
return (
16+
original: (event: string, ...args: unknown[]) => boolean,
17+
): ((this: unknown, event: string, ...args: unknown[]) => boolean) => {
18+
return function incomingRequest(this: unknown, event: string, ...args: unknown[]): boolean {
19+
return original.apply(this, [event, ...args]);
20+
};
21+
};
22+
}
23+
}
24+
25+
interface HttpOptions {
26+
/**
27+
* Whether breadcrumbs should be recorded for requests.
28+
* Defaults to true
29+
*/
30+
breadcrumbs?: boolean;
31+
32+
/**
33+
* Do not capture spans or breadcrumbs for outgoing HTTP requests to URLs where the given callback returns `true`.
34+
* This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.
35+
*/
36+
ignoreOutgoingRequests?: (url: string) => boolean;
37+
}
38+
39+
/**
40+
* The http integration instruments Node's internal http and https modules.
41+
* It creates breadcrumbs and spans for outgoing HTTP requests which will be attached to the currently active span.
42+
*/
43+
export const httpIntegration = ((options: HttpOptions = {}) => {
44+
return originalHttpIntegration({
45+
...options,
46+
_instrumentation: CustomNextjsHttpIntegration,
47+
});
48+
}) satisfies IntegrationFn;

packages/node/src/integrations/http.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,22 @@ interface HttpOptions {
4242
* This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.
4343
*/
4444
ignoreIncomingRequests?: (url: string) => boolean;
45+
46+
/** Allows to pass a custom version of HttpInstrumentation. We use this for Next.js. */
47+
_instrumentation?: typeof HttpInstrumentation;
4548
}
4649

4750
const _httpIntegration = ((options: HttpOptions = {}) => {
4851
const _breadcrumbs = typeof options.breadcrumbs === 'undefined' ? true : options.breadcrumbs;
4952
const _ignoreOutgoingRequests = options.ignoreOutgoingRequests;
5053
const _ignoreIncomingRequests = options.ignoreIncomingRequests;
54+
const _InstrumentationClass = options._instrumentation || HttpInstrumentation;
5155

5256
return {
5357
name: 'Http',
5458
setupOnce() {
5559
addOpenTelemetryInstrumentation(
56-
new HttpInstrumentation({
60+
new _InstrumentationClass({
5761
ignoreOutgoingRequestHook: request => {
5862
const url = getRequestUrl(request);
5963

0 commit comments

Comments
 (0)