Skip to content

Commit

Permalink
Tracing: allow opt-in flag to send build traces to url (#65019)
Browse files Browse the repository at this point in the history
Like #53880 did for `next dev`, this adds support to `next build` to
send a high-level subset of the next trace to a remote endpoint. This
can contain sensitive information, and it is **opt-in** and must be
provided when `next build` is invoked.
  • Loading branch information
wbinnssmith authored Apr 26, 2024
1 parent f4be4db commit 474432e
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 5 deletions.
4 changes: 4 additions & 0 deletions packages/next/src/bin/next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ program
'--experimental-debug-memory-usage',
'Enables memory profiling features to debug memory consumption.'
)
.option(
'--experimental-upload-trace, <traceUrl>',
'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
Expand Down
16 changes: 15 additions & 1 deletion packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[]
Expand Down Expand Up @@ -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<void> {
const isCompileMode = experimentalBuildMode === 'compile'
const isGenerateMode = experimentalBuildMode === 'generate'

let loadedConfig: NextConfigComplete | undefined
try {
const nextBuildSpan = trace('next-build', undefined, {
buildMode: experimentalBuildMode,
Expand Down Expand Up @@ -741,6 +744,7 @@ export default async function build(
turborepoAccessTraceResult
)
)
loadedConfig = config

process.env.NEXT_DEPLOYMENT_ID = config.deploymentId || ''
NextBuildContext.config = config
Expand Down Expand Up @@ -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,
})
}
}
}
10 changes: 9 additions & 1 deletion packages/next/src/cli/next-build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type NextBuildOptions = {
experimentalAppOnly?: boolean
experimentalTurbo?: boolean
experimentalBuildMode: 'default' | 'compile' | 'generate'
experimentalUploadTrace?: string
}

const nextBuild = (options: NextBuildOptions, directory?: string) => {
Expand All @@ -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.')
}
Expand Down Expand Up @@ -77,7 +84,8 @@ const nextBuild = (options: NextBuildOptions, directory?: string) => {
!mangling,
experimentalAppOnly,
!!process.env.TURBOPACK,
experimentalBuildMode
experimentalBuildMode,
traceUploadUrl
)
.catch((err) => {
if (experimentalDebugMemoryUsage) {
Expand Down
34 changes: 32 additions & 2 deletions packages/next/src/trace/trace-uploader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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.
Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion packages/next/src/trace/upload-trace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default function uploadTrace({
sync,
}: {
traceUploadUrl: string
mode: 'dev'
mode: 'dev' | 'build'
projectDir: string
distDir: string
sync?: boolean
Expand Down

0 comments on commit 474432e

Please sign in to comment.