Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(nextjs): Instrument outgoing http requests #11685

Merged
merged 3 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
},
"dependencies": {
"@opentelemetry/api": "1.7.0",
"@opentelemetry/instrumentation-http": "0.48.0",
"@rollup/plugin-commonjs": "24.0.0",
"@sentry/core": "8.0.0-beta.2",
"@sentry/node": "8.0.0-beta.2",
Expand Down
48 changes: 48 additions & 0 deletions packages/nextjs/src/server/httpIntegration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
import { httpIntegration as originalHttpIntegration } from '@sentry/node';
import type { IntegrationFn } from '@sentry/types';

/**
* Next.js handles incoming requests itself,
* but it does not handle outgoing requests.
* Today, it is not possible to use the HttpInstrumentation for only outgoing requests -
* until https://github.com/open-telemetry/opentelemetry-js/pull/4643 is merged & released.
* So in the meanwhile, we extend the base HttpInstrumentation to not wrap incoming requests.
*/
class CustomNextjsHttpIntegration extends HttpInstrumentation {
// Instead of the default behavior, we just don't do any wrapping for incoming requests
protected _getPatchIncomingRequestFunction(_component: 'http' | 'https') {
return (
original: (event: string, ...args: unknown[]) => boolean,
): ((this: unknown, event: string, ...args: unknown[]) => boolean) => {
return function incomingRequest(this: unknown, event: string, ...args: unknown[]): boolean {
return original.apply(this, [event, ...args]);
};
};
}
}

interface HttpOptions {
/**
* Whether breadcrumbs should be recorded for requests.
* Defaults to true
*/
breadcrumbs?: boolean;

/**
* Do not capture spans or breadcrumbs for outgoing HTTP requests to URLs where the given callback returns `true`.
* This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.
*/
ignoreOutgoingRequests?: (url: string) => boolean;
}

/**
* The http integration instruments Node's internal http and https modules.
* It creates breadcrumbs and spans for outgoing HTTP requests which will be attached to the currently active span.
*/
export const httpIntegration = ((options: HttpOptions = {}) => {
return originalHttpIntegration({
...options,
_instrumentation: CustomNextjsHttpIntegration,
});
}) satisfies IntegrationFn;
4 changes: 4 additions & 0 deletions packages/nextjs/src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import { distDirRewriteFramesIntegration } from './distDirRewriteFramesIntegrati

export * from '@sentry/node';
import type { EventProcessor } from '@sentry/types';
import { httpIntegration } from './httpIntegration';

export { httpIntegration };

export { captureUnderscoreErrorException } from '../common/_error';

Expand Down Expand Up @@ -75,6 +78,7 @@ export function init(options: NodeOptions): void {
// Next.js comes with its own Http instrumentation for OTel which would lead to double spans for route handler requests
integration.name !== 'Http',
),
httpIntegration(),
];

// This value is injected at build time, based on the output directory specified in the build config. Though a default
Expand Down
6 changes: 5 additions & 1 deletion packages/node/src/integrations/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,22 @@ interface HttpOptions {
* This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.
*/
ignoreIncomingRequests?: (url: string) => boolean;

/** Allows to pass a custom version of HttpInstrumentation. We use this for Next.js. */
_instrumentation?: typeof HttpInstrumentation;
}

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

return {
name: 'Http',
setupOnce() {
addOpenTelemetryInstrumentation(
new HttpInstrumentation({
new _InstrumentationClass({
ignoreOutgoingRequestHook: request => {
const url = getRequestUrl(request);

Expand Down
Loading