diff --git a/.size-limit.js b/.size-limit.js index 3b91cb51a3c5..db032dc4a3ac 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -208,7 +208,7 @@ module.exports = [ 'tls', ], gzip: true, - limit: '160 KB', + limit: '180 KB', }, ]; diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index ca3204c7e942..fccab85ea60c 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -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", diff --git a/packages/nextjs/src/server/httpIntegration.ts b/packages/nextjs/src/server/httpIntegration.ts new file mode 100644 index 000000000000..4fdc615deb92 --- /dev/null +++ b/packages/nextjs/src/server/httpIntegration.ts @@ -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; diff --git a/packages/nextjs/src/server/index.ts b/packages/nextjs/src/server/index.ts index 6895f1457727..0251f5782bcb 100644 --- a/packages/nextjs/src/server/index.ts +++ b/packages/nextjs/src/server/index.ts @@ -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'; @@ -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 diff --git a/packages/node/src/integrations/http.ts b/packages/node/src/integrations/http.ts index d2d6b2149f9e..ce6916da4dcd 100644 --- a/packages/node/src/integrations/http.ts +++ b/packages/node/src/integrations/http.ts @@ -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);