diff --git a/packages/next/src/bin/next.ts b/packages/next/src/bin/next.ts index 6812b5eb99216..a9f065ed6e4f8 100755 --- a/packages/next/src/bin/next.ts +++ b/packages/next/src/bin/next.ts @@ -124,6 +124,10 @@ program '--experimental-debug-memory-usage', 'Enables memory profiling features to debug memory consumption.' ) + .option( + '--experimental-upload-trace, ', + 'Reports a subset of the debugging trace to a remote HTTP URL. Includes sensitive data.' + ) .action((directory, options) => // ensure process exits after build completes so open handles/connections // don't cause process to hang diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts index 33a1c97d27eec..f1b5a6c6805d9 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -186,6 +186,7 @@ import { createProgress } from './progress' import { traceMemoryUsage } from '../lib/memory/trace' import { generateEncryptionKeyBase64 } from '../server/app-render/encryption-utils' import type { DeepReadonly } from '../shared/lib/deep-readonly' +import uploadTrace from '../trace/upload-trace' interface ExperimentalBypassForInfo { experimentalBypassFor?: RouteHas[] @@ -703,11 +704,13 @@ export default async function build( noMangling = false, appDirOnly = false, turboNextBuild = false, - experimentalBuildMode: 'default' | 'compile' | 'generate' + experimentalBuildMode: 'default' | 'compile' | 'generate', + traceUploadUrl: string | undefined ): Promise { const isCompileMode = experimentalBuildMode === 'compile' const isGenerateMode = experimentalBuildMode === 'generate' + let loadedConfig: NextConfigComplete | undefined try { const nextBuildSpan = trace('next-build', undefined, { buildMode: experimentalBuildMode, @@ -741,6 +744,7 @@ export default async function build( turborepoAccessTraceResult ) ) + loadedConfig = config process.env.NEXT_DEPLOYMENT_ID = config.deploymentId || '' NextBuildContext.config = config @@ -3448,5 +3452,15 @@ export default async function build( await flushAllTraces() teardownTraceSubscriber() teardownHeapProfiler() + + if (traceUploadUrl && loadedConfig) { + uploadTrace({ + traceUploadUrl, + mode: 'build', + projectDir: dir, + distDir: loadedConfig.distDir, + sync: true, + }) + } } } diff --git a/packages/next/src/cli/next-build.ts b/packages/next/src/cli/next-build.ts index 2cec657720264..dbbdff5b8c5e1 100755 --- a/packages/next/src/cli/next-build.ts +++ b/packages/next/src/cli/next-build.ts @@ -20,6 +20,7 @@ type NextBuildOptions = { experimentalAppOnly?: boolean experimentalTurbo?: boolean experimentalBuildMode: 'default' | 'compile' | 'generate' + experimentalUploadTrace?: string } const nextBuild = (options: NextBuildOptions, directory?: string) => { @@ -35,8 +36,14 @@ const nextBuild = (options: NextBuildOptions, directory?: string) => { experimentalAppOnly, experimentalTurbo, experimentalBuildMode, + experimentalUploadTrace, } = options + let traceUploadUrl: string | undefined + if (experimentalUploadTrace && !process.env.NEXT_TRACE_UPLOAD_DISABLED) { + traceUploadUrl = experimentalUploadTrace + } + if (!lint) { warn('Linting is disabled.') } @@ -77,7 +84,8 @@ const nextBuild = (options: NextBuildOptions, directory?: string) => { !mangling, experimentalAppOnly, !!process.env.TURBOPACK, - experimentalBuildMode + experimentalBuildMode, + traceUploadUrl ) .catch((err) => { if (experimentalDebugMemoryUsage) { diff --git a/packages/next/src/trace/trace-uploader.ts b/packages/next/src/trace/trace-uploader.ts index a60832c667c4c..73bbc84847d12 100644 --- a/packages/next/src/trace/trace-uploader.ts +++ b/packages/next/src/trace/trace-uploader.ts @@ -9,10 +9,13 @@ import { createReadStream } from 'fs' import path from 'path' import { Telemetry } from '../telemetry/storage' +const COMMON_ALLOWED_EVENTS = ['memory-usage'] + // Predefined set of the event names to be included in the trace. // If the trace span's name matches to one of the event names in the set, // it'll up uploaded to the trace server. -const EVENT_FILTER = new Set([ +const DEV_ALLOWED_EVENTS = new Set([ + ...COMMON_ALLOWED_EVENTS, 'client-hmr-latency', 'hot-reloader', 'webpack-invalid-client', @@ -24,6 +27,31 @@ const EVENT_FILTER = new Set([ 'server-restart-close-to-memory-threshold', ]) +const BUILD_ALLOWED_EVENTS = new Set([ + ...COMMON_ALLOWED_EVENTS, + 'next-build', + 'webpack-compilation', + 'run-webpack-compiler', + 'create-entrypoints', + 'server', + 'make', + 'seal', + 'chunk-graph', + 'optimize-modules', + 'optimize-chunks', + 'optimize', + 'optimize-tree', + 'optimize-chunk-modules', + 'module-hash', + 'client', + 'static-check', + 'node-file-trace-build', + 'static-generation', + 'next-export', + 'verify-typescript-setup', + 'verify-and-lint', +]) + const { NEXT_TRACE_UPLOAD_DEBUG, // An external env to allow to upload full trace without picking up the relavant spans. @@ -107,7 +135,9 @@ interface TraceMetadata { // Always include root spans event.parentId === undefined || shouldUploadFullTrace || - EVENT_FILTER.has(event.name) + (mode === 'dev' + ? DEV_ALLOWED_EVENTS.has(event.name) + : BUILD_ALLOWED_EVENTS.has(event.name)) ) { let trace = traces.get(event.traceId) if (trace === undefined) { diff --git a/packages/next/src/trace/upload-trace.ts b/packages/next/src/trace/upload-trace.ts index 4c7528875b0f3..d93474cefed94 100644 --- a/packages/next/src/trace/upload-trace.ts +++ b/packages/next/src/trace/upload-trace.ts @@ -6,7 +6,7 @@ export default function uploadTrace({ sync, }: { traceUploadUrl: string - mode: 'dev' + mode: 'dev' | 'build' projectDir: string distDir: string sync?: boolean