diff --git a/packages/nextjs/src/client/index.ts b/packages/nextjs/src/client/index.ts index 07d1ee5c4e84..d7a2478987ae 100644 --- a/packages/nextjs/src/client/index.ts +++ b/packages/nextjs/src/client/index.ts @@ -5,6 +5,7 @@ import type { Client, EventProcessor, Integration } from '@sentry/core'; import { addEventProcessor, applySdkMetadata, consoleSandbox, getGlobalScope, GLOBAL_OBJ } from '@sentry/core'; import type { BrowserOptions } from '@sentry/react'; import { getDefaultIntegrations as getReactDefaultIntegrations, init as reactInit } from '@sentry/react'; +import { DEBUG_BUILD } from '../common/debug-build'; import { devErrorSymbolicationEventProcessor } from '../common/devErrorSymbolicationEventProcessor'; import { getVercelEnv } from '../common/getVercelEnv'; import { isRedirectNavigationError } from '../common/nextNavigationErrorUtils'; @@ -48,6 +49,15 @@ export function init(options: BrowserOptions): Client | undefined { } clientIsInitialized = true; + if (!DEBUG_BUILD && options.debug) { + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn( + '[@sentry/nextjs] You have enabled `debug: true`, but Sentry debug logging was removed from your bundle (likely via `withSentryConfig({ disableLogger: true })` / `webpack.treeshake.removeDebugLogging: true`). Set that option to `false` to see Sentry debug output.', + ); + }); + } + // Remove cached trace meta tags for ISR/SSG pages before initializing // This prevents the browser tracing integration from using stale trace IDs if (typeof __SENTRY_TRACING__ === 'undefined' || __SENTRY_TRACING__) { diff --git a/packages/nextjs/src/edge/index.ts b/packages/nextjs/src/edge/index.ts index fcaad178b9fa..9fa05c94e978 100644 --- a/packages/nextjs/src/edge/index.ts +++ b/packages/nextjs/src/edge/index.ts @@ -22,6 +22,7 @@ import { import { getScopesFromContext } from '@sentry/opentelemetry'; import type { VercelEdgeOptions } from '@sentry/vercel-edge'; import { getDefaultIntegrations, init as vercelEdgeInit } from '@sentry/vercel-edge'; +import { DEBUG_BUILD } from '../common/debug-build'; import { ATTR_NEXT_SPAN_TYPE } from '../common/nextSpanAttributes'; import { TRANSACTION_ATTR_SHOULD_DROP_TRANSACTION } from '../common/span-attributes-with-logic-attached'; import { addHeadersAsAttributes } from '../common/utils/addHeadersAsAttributes'; @@ -55,6 +56,13 @@ export function init(options: VercelEdgeOptions = {}): void { return; } + if (!DEBUG_BUILD && options.debug) { + // eslint-disable-next-line no-console + console.warn( + '[@sentry/nextjs] You have enabled `debug: true`, but Sentry debug logging was removed from your bundle (likely via `withSentryConfig({ disableLogger: true })` / `webpack.treeshake.removeDebugLogging: true`). Set that option to `false` to see Sentry debug output.', + ); + } + const customDefaultIntegrations = getDefaultIntegrations(options); // This value is injected at build time, based on the output directory specified in the build config. Though a default diff --git a/packages/nextjs/src/server/index.ts b/packages/nextjs/src/server/index.ts index 7dc533e171b1..18f3db003177 100644 --- a/packages/nextjs/src/server/index.ts +++ b/packages/nextjs/src/server/index.ts @@ -96,6 +96,13 @@ export function init(options: NodeOptions): NodeClient | undefined { return; } + if (!DEBUG_BUILD && options.debug) { + // eslint-disable-next-line no-console + console.warn( + '[@sentry/nextjs] You have enabled `debug: true`, but Sentry debug logging was removed from your bundle (likely via `withSentryConfig({ disableLogger: true })` / `webpack.treeshake.removeDebugLogging: true`). Set that option to `false` to see Sentry debug output.', + ); + } + const customDefaultIntegrations = getDefaultIntegrations(options) .filter(integration => integration.name !== 'Http') .concat( diff --git a/packages/nextjs/test/config/conflictingDebugOptions.test.ts b/packages/nextjs/test/config/conflictingDebugOptions.test.ts new file mode 100644 index 000000000000..8c0920382c4a --- /dev/null +++ b/packages/nextjs/test/config/conflictingDebugOptions.test.ts @@ -0,0 +1,82 @@ +import { JSDOM } from 'jsdom'; +import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest'; + +const TEST_DSN = 'https://public@dsn.ingest.sentry.io/1337'; + +function didWarnAboutDebugRemoved(warnSpy: ReturnType): boolean { + return warnSpy.mock.calls.some(call => + call.some( + arg => + typeof arg === 'string' && + arg.includes('You have enabled `debug: true`') && + arg.includes('debug logging was removed from your bundle'), + ), + ); +} + +describe('debug: true + removeDebugLogging warning', () => { + let dom: JSDOM; + let originalDocument: unknown; + let originalLocation: unknown; + let originalAddEventListener: unknown; + + beforeAll(() => { + dom = new JSDOM('', { url: 'https://example.com/' }); + + originalDocument = (globalThis as any).document; + originalLocation = (globalThis as any).location; + originalAddEventListener = (globalThis as any).addEventListener; + + Object.defineProperty(globalThis, 'document', { value: dom.window.document, writable: true }); + Object.defineProperty(globalThis, 'location', { value: dom.window.location, writable: true }); + Object.defineProperty(globalThis, 'addEventListener', { value: () => undefined, writable: true }); + }); + + afterAll(() => { + Object.defineProperty(globalThis, 'document', { value: originalDocument, writable: true }); + Object.defineProperty(globalThis, 'location', { value: originalLocation, writable: true }); + Object.defineProperty(globalThis, 'addEventListener', { value: originalAddEventListener, writable: true }); + }); + + afterEach(() => { + vi.restoreAllMocks(); + vi.resetModules(); + vi.unmock('../../src/common/debug-build.js'); + delete process.env.NEXT_OTEL_FETCH_DISABLED; + delete process.env.NEXT_PHASE; + }); + + it('warns on client/server/edge when debug is true but DEBUG_BUILD is false', async () => { + vi.doMock('../../src/common/debug-build.js', () => ({ DEBUG_BUILD: false })); + + const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); + + const client = await import('../../src/client/index.js'); + client.init({ dsn: TEST_DSN, debug: true } as any); + + const server = await import('../../src/server/index.js'); + server.init({ dsn: TEST_DSN, debug: true } as any); + + const edge = await import('../../src/edge/index.js'); + edge.init({ dsn: TEST_DSN, debug: true } as any); + + expect(didWarnAboutDebugRemoved(warnSpy)).toBe(true); + }); + + it('does not emit that warning when DEBUG_BUILD is true', async () => { + vi.doMock('../../src/common/debug-build.js', () => ({ DEBUG_BUILD: true })); + + const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); + + const client = await import('../../src/client/index.js'); + client.init({ dsn: TEST_DSN, debug: true } as any); + + const server = await import('../../src/server/index.js'); + server.init({ dsn: TEST_DSN, debug: true } as any); + + const edge = await import('../../src/edge/index.js'); + edge.init({ dsn: TEST_DSN, debug: true } as any); + + expect(didWarnAboutDebugRemoved(warnSpy)).toBe(false); + }); +});