From f4f9ec1339dbbff5e6dba20dded5af401844312a Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Thu, 18 Apr 2024 15:50:06 +0200 Subject: [PATCH 1/3] feat(nextjs): Instrument outgoing http requests --- packages/nextjs/package.json | 1 + packages/nextjs/src/server/httpIntegration.ts | 48 +++++++++++++++++++ packages/node/src/integrations/http.ts | 6 ++- 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 packages/nextjs/src/server/httpIntegration.ts 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/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); From b09f6d6942020c76912688efc192df051988184b Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 19 Apr 2024 10:20:28 +0200 Subject: [PATCH 2/3] use httpIntegration by default --- packages/nextjs/src/server/index.ts | 4 ++++ 1 file changed, 4 insertions(+) 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 From fdfe8c1f4f72a5c5251fe6a6af320e2c2e2fe0a7 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 19 Apr 2024 10:27:20 +0200 Subject: [PATCH 3/3] bump size limit --- .size-limit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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', }, ];