diff --git a/packages/node/src/integrations/http/SentryHttpInstrumentationBeforeOtel.ts b/packages/node/src/integrations/http/SentryHttpInstrumentationBeforeOtel.ts index e98256211f84..e15419fce2b8 100644 --- a/packages/node/src/integrations/http/SentryHttpInstrumentationBeforeOtel.ts +++ b/packages/node/src/integrations/http/SentryHttpInstrumentationBeforeOtel.ts @@ -9,6 +9,17 @@ import { stealthWrap } from './utils'; type Http = typeof http; type Https = typeof https; +// The reason this "before OTEL" integration even exists is due to timing reasons. We need to be able to register a +// `res.on('close')` handler **after** OTEL registers its own handler (which it uses to end spans), so that we can do +// something (ie. flush) after OTEL has ended a span for a request. If you think about it like an onion: +// +// (Sentry after OTEL instrumentation +// (OTEL instrumentation +// (Sentry before OTEL instrumentation +// (orig HTTP request handler)))) +// +// registering an instrumentation before OTEL allows us to do this for incoming requests. + /** * A Sentry specific http instrumentation that is applied before the otel instrumentation. */ diff --git a/packages/node/src/integrations/http/index.ts b/packages/node/src/integrations/http/index.ts index 0df3fc56b480..c8ba11655dcb 100644 --- a/packages/node/src/integrations/http/index.ts +++ b/packages/node/src/integrations/http/index.ts @@ -142,7 +142,17 @@ export const httpIntegration = defineIntegration((options: HttpOptions = {}) => return { name: INTEGRATION_NAME, setupOnce() { - instrumentSentryHttpBeforeOtel(); + // Below, we instrument the Node.js HTTP API three times. 2 times Sentry-specific, 1 time OTEL specific. + // Due to timing reasons, we sometimes need to apply Sentry instrumentation _before_ we apply the OTEL + // instrumentation (e.g. to flush on serverless platforms), and sometimes we need to apply Sentry instrumentation + // _after_ we apply OTEL instrumentation (e.g. for isolation scope handling and breadcrumbs). + + // This is Sentry-specific instrumentation that is applied _before_ any OTEL instrumentation. + if (process.env.VERCEL) { + // Currently this instrumentation only does something when deployed on Vercel, so to save some overhead, we short circuit adding it here only for Vercel. + // If it's functionality is extended in the future, feel free to remove the if statement and this comment. + instrumentSentryHttpBeforeOtel(); + } const instrumentSpans = _shouldInstrumentSpans(options, getClient()?.getOptions()); @@ -152,9 +162,7 @@ export const httpIntegration = defineIntegration((options: HttpOptions = {}) => instrumentOtelHttp(instrumentationConfig); } - // This is the Sentry-specific instrumentation that isolates requests & creates breadcrumbs - // Note that this _has_ to be wrapped after the OTEL instrumentation, - // otherwise the isolation will not work correctly + // This is Sentry-specific instrumentation that is applied _after_ any OTEL instrumentation. instrumentSentryHttp({ ...options, // If spans are not instrumented, it means the HttpInstrumentation has not been added