diff --git a/packages/node/src/types.ts b/packages/node/src/types.ts index aa9873e2da91..f6b08a394f8c 100644 --- a/packages/node/src/types.ts +++ b/packages/node/src/types.ts @@ -125,6 +125,12 @@ export interface BaseNodeOptions { */ clientReportFlushInterval?: number; + /** + * By default, the SDK will try to identify problems with your instrumentation setup and warn you about it. + * If you want to disable these warnings, set this to `true`. + */ + disableInstrumentationWarnings?: boolean; + /** Callback that is executed when a fatal global error occurs. */ onFatalError?(this: void, error: Error): void; } diff --git a/packages/node/src/utils/ensureIsWrapped.ts b/packages/node/src/utils/ensureIsWrapped.ts index 05185a293bed..a11c60f949d5 100644 --- a/packages/node/src/utils/ensureIsWrapped.ts +++ b/packages/node/src/utils/ensureIsWrapped.ts @@ -1,6 +1,7 @@ import { isWrapped } from '@opentelemetry/core'; -import { getGlobalScope, hasTracingEnabled, isEnabled } from '@sentry/core'; +import { getClient, getGlobalScope, hasTracingEnabled, isEnabled } from '@sentry/core'; import { consoleSandbox } from '@sentry/utils'; +import type { NodeClient } from '../sdk/client'; import { isCjs } from './commonjs'; import { createMissingInstrumentationContext } from './createMissingInstrumentationContext'; @@ -8,10 +9,16 @@ import { createMissingInstrumentationContext } from './createMissingInstrumentat * Checks and warns if a framework isn't wrapped by opentelemetry. */ export function ensureIsWrapped( - maybeWrappedModule: unknown, + maybeWrappedFunction: unknown, name: 'express' | 'connect' | 'fastify' | 'hapi' | 'koa', ): void { - if (!isWrapped(maybeWrappedModule) && isEnabled() && hasTracingEnabled()) { + const client = getClient(); + if ( + !client?.getOptions().disableInstrumentationWarnings && + !isWrapped(maybeWrappedFunction) && + isEnabled() && + hasTracingEnabled() + ) { consoleSandbox(() => { if (isCjs()) { // eslint-disable-next-line no-console diff --git a/packages/node/test/utils/ensureIsWrapped.test.ts b/packages/node/test/utils/ensureIsWrapped.test.ts new file mode 100644 index 000000000000..c328545315ce --- /dev/null +++ b/packages/node/test/utils/ensureIsWrapped.test.ts @@ -0,0 +1,71 @@ +import { ensureIsWrapped } from '../../src/utils/ensureIsWrapped'; +import { cleanupOtel, mockSdkInit, resetGlobals } from '../helpers/mockSdkInit'; + +const unwrappedFunction = () => {}; + +// We simulate a wrapped function +const wrappedfunction = Object.assign(() => {}, { + __wrapped: true, + __original: () => {}, + __unwrap: () => {}, +}); + +describe('ensureIsWrapped', () => { + afterEach(() => { + jest.restoreAllMocks(); + cleanupOtel(); + resetGlobals(); + }); + + it('warns when the method is unwrapped', () => { + const spyWarn = jest.spyOn(console, 'warn').mockImplementation(() => {}); + + mockSdkInit({ tracesSampleRate: 1 }); + + ensureIsWrapped(unwrappedFunction, 'express'); + + expect(spyWarn).toHaveBeenCalledTimes(1); + expect(spyWarn).toHaveBeenCalledWith( + '[Sentry] express is not instrumented. This is likely because you required/imported express before calling `Sentry.init()`.', + ); + }); + + it('does not warn when the method is wrapped', () => { + const spyWarn = jest.spyOn(console, 'warn').mockImplementation(() => {}); + + mockSdkInit({ tracesSampleRate: 1 }); + + ensureIsWrapped(wrappedfunction, 'express'); + + expect(spyWarn).toHaveBeenCalledTimes(0); + }); + + it('does not warn without a client', () => { + const spyWarn = jest.spyOn(console, 'warn').mockImplementation(() => {}); + resetGlobals(); + + ensureIsWrapped(wrappedfunction, 'express'); + + expect(spyWarn).toHaveBeenCalledTimes(0); + }); + + it('does not warn without tracing', () => { + const spyWarn = jest.spyOn(console, 'warn').mockImplementation(() => {}); + + mockSdkInit({}); + + ensureIsWrapped(unwrappedFunction, 'express'); + + expect(spyWarn).toHaveBeenCalledTimes(0); + }); + + it('does not warn if disableInstrumentationWarnings=true', () => { + const spyWarn = jest.spyOn(console, 'warn').mockImplementation(() => {}); + + mockSdkInit({ tracesSampleRate: 1, disableInstrumentationWarnings: true }); + + ensureIsWrapped(unwrappedFunction, 'express'); + + expect(spyWarn).toHaveBeenCalledTimes(0); + }); +});