diff --git a/packages/node/src/integrations/http/index.ts b/packages/node/src/integrations/http/index.ts index 72326e21e6f1..5ef5c8bed007 100644 --- a/packages/node/src/integrations/http/index.ts +++ b/packages/node/src/integrations/http/index.ts @@ -3,7 +3,8 @@ import { diag } from '@opentelemetry/api'; import type { HttpInstrumentationConfig } from '@opentelemetry/instrumentation-http'; import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'; import type { Span } from '@sentry/core'; -import { defineIntegration, getClient } from '@sentry/core'; +import { defineIntegration, getClient, hasSpansEnabled } from '@sentry/core'; +import { NODE_VERSION } from '../../nodeVersion'; import { generateInstrumentOnce } from '../../otel/instrument'; import type { NodeClient } from '../../sdk/client'; import type { HTTPModuleRequestIncomingMessage } from '../../transports/http-module'; @@ -159,8 +160,22 @@ export const instrumentOtelHttp = generateInstrumentOnce = {}): boolean { // If `spans` is passed in, it takes precedence - // Else, we by default emit spans, unless `skipOpenTelemetrySetup` is set to `true` - return typeof options.spans === 'boolean' ? options.spans : !clientOptions.skipOpenTelemetrySetup; + // Else, we by default emit spans, unless `skipOpenTelemetrySetup` is set to `true` or spans are not enabled + if (typeof options.spans === 'boolean') { + return options.spans; + } + + if (clientOptions.skipOpenTelemetrySetup) { + return false; + } + + // IMPORTANT: We only disable span instrumentation when spans are not enabled _and_ we are on Node 22+, + // as otherwise the necessary diagnostics channel is not available yet + if (!hasSpansEnabled(clientOptions) && NODE_VERSION.major >= 22) { + return false; + } + + return true; } /** diff --git a/packages/node/src/integrations/node-fetch/index.ts b/packages/node/src/integrations/node-fetch/index.ts index d62d1e7c88c2..e85ce34dfb35 100644 --- a/packages/node/src/integrations/node-fetch/index.ts +++ b/packages/node/src/integrations/node-fetch/index.ts @@ -1,7 +1,7 @@ import type { UndiciInstrumentationConfig } from '@opentelemetry/instrumentation-undici'; import { UndiciInstrumentation } from '@opentelemetry/instrumentation-undici'; import type { IntegrationFn } from '@sentry/core'; -import { defineIntegration, getClient, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; +import { defineIntegration, getClient, hasSpansEnabled, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; import { generateInstrumentOnce } from '../../otel/instrument'; import type { NodeClient } from '../../sdk/client'; import type { NodeClientOptions } from '../../types'; @@ -86,8 +86,10 @@ function getAbsoluteUrl(origin: string, path: string = '/'): string { function _shouldInstrumentSpans(options: NodeFetchOptions, clientOptions: Partial = {}): boolean { // If `spans` is passed in, it takes precedence - // Else, we by default emit spans, unless `skipOpenTelemetrySetup` is set to `true` - return typeof options.spans === 'boolean' ? options.spans : !clientOptions.skipOpenTelemetrySetup; + // Else, we by default emit spans, unless `skipOpenTelemetrySetup` is set to `true` or spans are not enabled + return typeof options.spans === 'boolean' + ? options.spans + : !clientOptions.skipOpenTelemetrySetup && hasSpansEnabled(clientOptions); } function getConfigWithDefaults(options: Partial = {}): UndiciInstrumentationConfig { diff --git a/packages/node/test/integrations/http.test.ts b/packages/node/test/integrations/http.test.ts index 6d4bea24661f..89052a348ea4 100644 --- a/packages/node/test/integrations/http.test.ts +++ b/packages/node/test/integrations/http.test.ts @@ -1,19 +1,30 @@ import { describe, expect, it } from 'vitest'; import { _shouldInstrumentSpans } from '../../src/integrations/http'; +import { conditionalTest } from '../helpers/conditional'; describe('httpIntegration', () => { describe('_shouldInstrumentSpans', () => { it.each([ - [{}, {}, true], [{ spans: true }, {}, true], [{ spans: false }, {}, false], [{ spans: true }, { skipOpenTelemetrySetup: true }, true], [{ spans: false }, { skipOpenTelemetrySetup: true }, false], [{}, { skipOpenTelemetrySetup: true }, false], - [{}, { skipOpenTelemetrySetup: false }, true], + [{}, { tracesSampleRate: 0, skipOpenTelemetrySetup: true }, false], + [{}, { tracesSampleRate: 0 }, true], ])('returns the correct value for options=%j and clientOptions=%j', (options, clientOptions, expected) => { const actual = _shouldInstrumentSpans(options, clientOptions); expect(actual).toBe(expected); }); + + conditionalTest({ min: 22 })('returns false without tracesSampleRate on Node >=22', () => { + const actual = _shouldInstrumentSpans({}, {}); + expect(actual).toBe(false); + }); + + conditionalTest({ max: 21 })('returns true without tracesSampleRate on Node <22', () => { + const actual = _shouldInstrumentSpans({}, {}); + expect(actual).toBe(true); + }); }); });