Skip to content

Commit 0e1463f

Browse files
authored
Telemetry: track failed builds and build bundler usage (#77943)
This: - Tracks build failures as a separate event, `NEXT_BUILD_FAILED` - Added `bundler` property to both `NEXT_BUILD_COMPLETED` and `NEXT_BUILD_FAILED` - Requires a server-side update Alternatives considered: Considered adding a `success: boolean` property to the existing `EventBuildCompleted`. There are a few issues with this: - Expands the semantics of the existing event. This may have unintended consequences if we don’t change how we report on these events to filter success state - This event requires page paths and timing information from the compiler, neither of which are guaranteed to exist on failures Test Plan: - Added a syntax error to a module and built with `NEXT_TELEMETRY_DEBUG=1`. Verified the failure event is sent along with the bundler value - Verified bundler values are now sent for webpack, rspack, and Turbopack on build successes
1 parent 733c570 commit 0e1463f

File tree

2 files changed

+63
-0
lines changed

2 files changed

+63
-0
lines changed

packages/next/src/build/index.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ import {
107107
EVENT_BUILD_FEATURE_USAGE,
108108
eventPackageUsedInGetServerSideProps,
109109
eventBuildCompleted,
110+
eventBuildFailed,
110111
} from '../telemetry/events'
111112
import type { EventBuildFeatureUsage } from '../telemetry/events'
112113
import { Telemetry } from '../telemetry/storage'
@@ -210,6 +211,8 @@ import { isPersistentCachingEnabled } from '../shared/lib/turbopack/utils'
210211
import { inlineStaticEnv } from '../lib/inline-static-env'
211212
import { populateStaticEnv } from '../lib/static-env'
212213
import { durationToString } from './duration-to-string'
214+
import { traceGlobals } from '../trace/shared'
215+
import { extractNextErrorCode } from '../lib/error-telemetry-utils'
213216

214217
type Fallback = null | boolean | string
215218

@@ -826,6 +829,7 @@ export default async function build(
826829
const isCompileMode = experimentalBuildMode === 'compile'
827830
const isGenerateMode = experimentalBuildMode === 'generate'
828831
NextBuildContext.isCompileMode = isCompileMode
832+
const buildStartTime = Date.now()
829833

830834
let loadedConfig: NextConfigComplete | undefined
831835
try {
@@ -1471,6 +1475,7 @@ export default async function build(
14711475

14721476
telemetry.record(
14731477
eventBuildCompleted(pagesPaths, {
1478+
bundler: 'turbopack',
14741479
durationInSeconds: Math.round(compilerDuration),
14751480
totalAppPagesCount,
14761481
})
@@ -1558,6 +1563,7 @@ export default async function build(
15581563

15591564
telemetry.record(
15601565
eventBuildCompleted(pagesPaths, {
1566+
bundler: getBundlerForTelemetry(isTurbopack),
15611567
durationInSeconds,
15621568
totalAppPagesCount,
15631569
})
@@ -1573,6 +1579,7 @@ export default async function build(
15731579

15741580
telemetry.record(
15751581
eventBuildCompleted(pagesPaths, {
1582+
bundler: getBundlerForTelemetry(isTurbopack),
15761583
durationInSeconds: compilerDuration,
15771584
totalAppPagesCount,
15781585
})
@@ -3705,6 +3712,18 @@ export default async function build(
37053712

37063713
await shutdownPromise
37073714
})
3715+
} catch (e) {
3716+
const telemetry: Telemetry | undefined = traceGlobals.get('telemetry')
3717+
if (telemetry) {
3718+
telemetry.record(
3719+
eventBuildFailed({
3720+
bundler: getBundlerForTelemetry(isTurbopack),
3721+
errorCode: getErrorCodeForTelemetry(e),
3722+
durationInSeconds: Math.floor((Date.now() - buildStartTime) / 1000),
3723+
})
3724+
)
3725+
}
3726+
throw e
37083727
} finally {
37093728
// Ensure we wait for lockfile patching if present
37103729
await lockfilePatchPromise.cur
@@ -3751,3 +3770,32 @@ function warnAboutTurbopackBuilds(config?: NextConfigComplete) {
37513770

37523771
Log.warn(warningStr)
37533772
}
3773+
3774+
function getBundlerForTelemetry(isTurbopack: boolean) {
3775+
if (isTurbopack) {
3776+
return 'turbopack'
3777+
}
3778+
3779+
if (process.env.NEXT_RSPACK) {
3780+
return 'rspack'
3781+
}
3782+
3783+
return 'webpack'
3784+
}
3785+
3786+
function getErrorCodeForTelemetry(err: unknown) {
3787+
const code = extractNextErrorCode(err)
3788+
if (code != null) {
3789+
return code
3790+
}
3791+
3792+
if (err instanceof Error && 'code' in err && typeof err.code === 'string') {
3793+
return err.code
3794+
}
3795+
3796+
if (err instanceof Error) {
3797+
return err.name
3798+
}
3799+
3800+
return 'Unknown'
3801+
}

packages/next/src/telemetry/events/build.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export function eventLintCheckCompleted(event: EventLintCheckCompleted): {
5454

5555
const EVENT_BUILD_COMPLETED = 'NEXT_BUILD_COMPLETED'
5656
type EventBuildCompleted = {
57+
bundler: 'webpack' | 'rspack' | 'turbopack'
5758
durationInSeconds: number
5859
totalPageCount: number
5960
hasDunderPages: boolean
@@ -85,6 +86,20 @@ export function eventBuildCompleted(
8586
}
8687
}
8788

89+
const EVENT_BUILD_FAILED = 'NEXT_BUILD_FAILED'
90+
type EventBuildFailed = {
91+
bundler: 'webpack' | 'rspack' | 'turbopack'
92+
errorCode: string
93+
durationInSeconds: number
94+
}
95+
96+
export function eventBuildFailed(event: EventBuildFailed) {
97+
return {
98+
eventName: EVENT_BUILD_FAILED,
99+
payload: event,
100+
}
101+
}
102+
88103
const EVENT_BUILD_OPTIMIZED = 'NEXT_BUILD_OPTIMIZED'
89104
type EventBuildOptimized = {
90105
durationInSeconds: number

0 commit comments

Comments
 (0)