Relevant source files
The following files were used as context for generating this wiki page:
- packages/next/src/telemetry/storage.ts
- packages/next/src/build/index.ts
- packages/next/src/server/dev/hot-reloader-turbopack.ts
- packages/next/src/telemetry/flush-telemetry.ts
- packages/next/src/next-devtools/userspace/app/forward-logs.ts
- packages/next/src/trace/report/to-telemetry.ts
- packages/next/src/server/dev/hot-reloader-webpack.ts
- packages/next/src/server/mcp/mcp-telemetry-tracker.ts
- packages/next/src/telemetry/detached-flush.ts
- packages/next/src/telemetry/post-telemetry-payload.ts
- packages/next/src/server/lib/trace/request-insights.ts
- packages/next/src/telemetry/events/swc-load-failure.ts
- packages/next/src/cli/next-telemetry.ts
- packages/next/src/next-devtools/dev-overlay.browser.tsx
- packages/next/src/build/webpack/plugins/telemetry-plugin/telemetry-plugin.ts
- packages/devlow-bench/src/interfaces/snowflake.ts
- packages/next/src/next-devtools/dev-overlay/utils/save-devtools-config.ts
- packages/next/src/trace/trace-uploader.ts
- packages/next/src/telemetry/events/index.ts
- packages/next/src/trace/report/index.ts
- packages/next/src/server/client-component-renderer-logger.ts
- packages/next/src/client/dev/hot-reloader/runtime-error-state.ts
- packages/next/src/telemetry/events/agent-upgrade.ts
- packages/next/src/telemetry/events/build.ts
- packages/next/src/server/dev/browser-logs/file-logger.ts
- packages/next/src/server/lib/trace/span-store.ts
- packages/next/src/trace/report/to-json.ts
- packages/next/src/next-devtools/userspace/app/terminal-logging-config.ts
- packages/next/src/telemetry/events/session-stopped.ts
- packages/next/src/telemetry/anonymous-meta.ts
Overview
Next.js telemetry provides an anonymous data collection system designed to capture usage patterns, feature adoption, compilation results, and performance diagnostics, helping shape product roadmaps and prioritize features. Operating via persistent local configuration stores and secure identity hashing, the telemetry subsystem records events locally, buffers payloads in memory, and dispatches them securely over HTTPS to upstream collection endpoints while preserving end-user privacy. Sources: packages/next/src/telemetry/storage.ts:14-30, packages/next/src/telemetry/storage.ts:346-360, packages/next/src/telemetry/post-telemetry-payload.ts:25-29
The infrastructure integrates deeply across the build pipeline, compiler plugins, CLI execution lifecycle, and OpenTelemetry tracing bridges to gather granular insights. It incorporates robust opt-out mechanisms, background flushing workers for process lifecycles, and environment variable controls to ensure telemetry collection remains transparent and fully controllable. Sources: packages/next/src/build/index.ts:4902-4911, packages/next/src/telemetry/detached-flush.ts:51-53, packages/next/src/cli/next-telemetry.ts:14-22
Telemetry storage architecture and identity hashing
Core Telemetry Class and Configuration Storage
The Telemetry class coordinates local event recording, persistent configuration management, and payload dispatch. During instantiation, it reads environment variables (NEXT_TELEMETRY_DISABLED and NEXT_TELEMETRY_DEBUG) to determine operating modes and initializes a storage directory using getStorageDirectory. Ephemeral environments such as continuous integration servers or Docker containers resolve storage to path.join(distDir, 'cache'), while standard developer workstations omit a dedicated local directory override. The class wraps Next.js's compiled Conf library under the project name nextjs, catching permission initialization errors to prevent process crashes. A cryptographically secure session identifier (sessionId) is generated using 32 random bytes converted to a hex string, while an in-memory queue (Set<Promise<RecordObject>>) tracks active submission promises.
Sources: packages/next/src/telemetry/storage.ts:40-89
| Constant Name | Value / Key | Purpose |
|---|---|---|
TELEMETRY_KEY_ENABLED | telemetry.enabled | Stores whether telemetry collection is enabled or disabled. |
TELEMETRY_KEY_NOTIFY_DATE | telemetry.notifiedAt | Specifies when the user was first informed about anonymous telemetry. |
TELEMETRY_KEY_ID | telemetry.anonymousId | Quasi-persistent identifier used to deduplicate recurring events anonymously. |
TELEMETRY_KEY_SALT | telemetry.salt | Cryptographic salt included within hashed values to prevent dictionary lookups. |
Sources: packages/next/src/telemetry/storage.ts:14-30
Note
The Conf persistence layer handles initialization permission errors via a try-catch block, falling back to a null configuration reference to prevent unexpected application crashes during startup.
Sources: packages/next/src/telemetry/storage.ts:75-82
Anonymous Identity and Hashing Mechanics
Persistent user identification relies on the anonymousId getter, which checks the local configuration store for telemetry.anonymousId. If absent, it generates a new 32-byte random hex string and caches it in the configuration store. Project identification utilizes getRawProjectId, pairing local configuration contexts with raw project identifiers to scope events. To maintain user privacy, all sensitive payloads pass through irreversible hashing mechanisms incorporating a per-installation cryptographic salt (telemetry.salt). This salt value is never transmitted upstream, neutralizing pre-computed dictionary attacks and ensuring that submitted telemetry data remains strictly non-identifiable.
Sources: packages/next/src/telemetry/storage.ts:11-29, packages/next/src/telemetry/storage.ts:120-131
Anonymous system metadata collection
Host Platform and Machine Indicators
The getAnonymousMeta function gathers execution context and environment metadata by inspecting host operating system interfaces and environment variables. Host metrics include the platform type, release version, system architecture, CPU core count, primary CPU model, and CPU speed via the Node.js os module. Memory capacity is calculated by converting total system memory from bytes to megabytes and rounding to the nearest integer.
Sources: packages/next/src/telemetry/anonymous-meta.ts:3-43
| Trait Property | Source / Expression | Description |
|---|---|---|
systemPlatform | os.platform() | Operating system platform identifier. |
systemRelease | os.release() | Operating system release version. |
systemArchitecture | os.arch() | System CPU architecture. |
cpuCount | cpus.length | Total number of available CPU cores. |
cpuModel | cpus.length ? cpus[0].model : null | Model designation of the primary CPU. |
cpuSpeed | cpus.length ? cpus[0].speed : null | Clock speed of the primary CPU in megahertz. |
memoryInMb | Math.trunc(os.totalmem() / Math.pow(1024, 2)) | Total system memory truncated to megabytes. |
Sources: packages/next/src/telemetry/anonymous-meta.ts:35-43
Note
Metadata payloads are cached in the module-scoped traits variable upon initial resolution, ensuring that subsequent telemetry submissions reuse identical payload metadata without re-querying system interfaces.
Sources: packages/next/src/telemetry/anonymous-meta.ts:25-30
Environment and Container Detection
Containerization, continuous integration services, and developer tooling indicators are collected alongside core machine statistics. Container presence is evaluated using is-docker, Windows Subsystem for Linux detection relies on is-wsl, and local development environments verify whether NOW_REGION is set to 'dev1'. Continuous integration status and vendor identity are resolved via ../server/ci-info, while package manager or execution agent names are populated asynchronously using getAgentName(). The active framework release is captured directly from process.env.__NEXT_VERSION.
Sources: packages/next/src/telemetry/anonymous-meta.ts:1-22, packages/next/src/telemetry/anonymous-meta.ts:44-52
Event queuing and synchronous flush
In-Memory Buffering and Queue State
The Telemetry class maintains an in-memory event queue represented as a Set of promises tracking asynchronous record operations (this.queue = new Set()). When telemetry is recorded, single events or arrays of events are normalized into a TelemetryEvent array. If debugging is enabled via NEXT_TELEMETRY_DEBUG, events bypass network dispatch and instead log the serialized payload alongside resolved anonymous metadata to standard error after a short timeout.
Sources: packages/next/src/telemetry/storage.ts:31-38, packages/next/src/telemetry/storage.ts:59-60, packages/next/src/telemetry/storage.ts:306-338
Warning
Telemetry recording returns an immediate resolved promise if the feature is globally disabled via this.isDisabled or if an empty event array is provided, preventing unnecessary allocations or execution overhead.
Sources: packages/next/src/telemetry/storage.ts:316-318, packages/next/src/telemetry/storage.ts:341-343
HTTPS Payload Dispatch and Retry Policies
Payload submission is handled by postNextTelemetryPayload, which dispatches HTTPS requests to the telemetry collection endpoint via the Fetch API using next/dist/compiled/async-retry. If an incoming AbortSignal is absent and AbortSignal.timeout is supported, a default 5000-millisecond timeout signal is instantiated. The request configuration options and retry policies are structured explicitly to constrain network latency during dispatch operations.
Sources: packages/next/src/telemetry/post-telemetry-payload.ts:1-38, packages/next/src/telemetry/storage.ts:345-365
| Parameter | Value | Description |
|---|---|---|
| Endpoint URL | https://telemetry.nextjs.org/api/v1/record | Destination HTTPS endpoint for telemetry payloads. |
| HTTP Method | POST | HTTP method used for dispatching event data. |
| Content-Type | application/json | Request header specifying JSON payload formatting. |
minTimeout | 500 | Initial retry delay in milliseconds. |
retries | 1 | Maximum number of retry attempts upon failure. |
factor | 1 | Exponential backoff factor applied between retries. |
Sources: packages/next/src/telemetry/post-telemetry-payload.ts:23-38
Note
Network errors and non-OK HTTP responses are caught and swallowed silently to guarantee that telemetry failures never disrupt or crash the main application process. Sources: packages/next/src/telemetry/post-telemetry-payload.ts:30-47
Global telemetry flushing is coordinated across modules through flushTelemetry, which retrieves the active telemetry instance from traceGlobals and awaits its flush routine.
Sources: packages/next/src/telemetry/flush-telemetry.ts:1-9
Detached background telemetry flushing
Argument Parsing and Execution Validation
The detached background telemetry flushing mechanism is implemented as an immediately invoked asynchronous function in packages/next/src/telemetry/detached-flush.ts. It expects process arguments to follow a precise ordering: mode, project directory, an optional events filename, and an optional resolved distribution directory.
Sources: packages/next/src/telemetry/detached-flush.ts:9-15
if (!dir || mode !== 'dev') {
throw new Error(
`Invalid flags should be run as node detached-flush dev ./path-to/project [eventsFile] [distDir]`
)
}Sources: packages/next/src/telemetry/detached-flush.ts:18-22
dir = getProjectDir(dir)Sources: packages/next/src/telemetry/detached-flush.ts:23-23
Warning
If inputDir is omitted or mode is not 'dev', the script throws an error enforcing the exact node invocation syntax.
Sources: packages/next/src/telemetry/detached-flush.ts:18-22
Distribution Directory and Event Batch Resolution
To avoid reloading phase-specific configuration when build nudges pass their resolved output directories, the script conditionally resolves distDir. Named batches live in the .next/cache directory to prevent build cleanup from removing them prior to submission, while callers without an events filename fall back to the legacy root _events.json location.
Sources: packages/next/src/telemetry/detached-flush.ts:25-38
// Build nudges pass their resolved output directory to avoid loading dev config again.
const distDir = suppliedDistDir
? path.resolve(suppliedDistDir)
: path.join(
dir,
(await loadConfig(PHASE_DEVELOPMENT_SERVER, dir)).distDir || '.next'
)
// Named batches live in cache so build cleanup cannot remove them before submission.
// Retain the legacy root path for callers without an events filename.
const eventsPath =
eventsFile && !eventsFile.includes('/')
? path.join(distDir, 'cache', eventsFile)
: path.join(distDir, '_events.json')Sources: packages/next/src/telemetry/detached-flush.ts:25-38
Note
If the events file does not exist (ENOENT), the worker catches the error code and exits immediately with code 0 since there are no events to process.
Sources: packages/next/src/telemetry/detached-flush.ts:41-49
Event Recording, Flushing, and Cleanup
Once the event payload is read and parsed as JSON, a new Telemetry instance is initialized using the resolved distDir. The worker records the events, initiates a flush, unlinks the events file upon completion, and allows Node.js to exit naturally.
Sources: packages/next/src/telemetry/detached-flush.ts:51-59
const telemetry = new Telemetry({ distDir })
await telemetry.record(events)
await telemetry.flush()
// finished flushing events clean-up
fs.unlinkSync(eventsPath)
// Don't call process.exit() here - let Node.js exit naturally after
// all pending work completes (e.g., setTimeout in debug telemetry)
})()Sources: packages/next/src/telemetry/detached-flush.ts:51-59
Compilation and feature adoption events
Build Outcomes and Optimization Telemetry
Next.js records comprehensive telemetry during compilation phases via functions defined in packages/next/src/telemetry/events/build.ts. The eventBuildCompleted and eventBuildOptimize factories evaluate page paths against regex rules to categorize project layout traits, specifically identifying test files or directories via /[\\/]__(tests|mocks)__[\\/]/i or /\.(?:spec|test)\.[^.]+$/i, and dunder directories via /[\\/]__[^\\/]+(?<![\\/]__(?:tests|mocks))__[\\/]/i.
Sources: packages/next/src/telemetry/events/build.ts:5-108, packages/next/src/telemetry/events/build.ts:151-178
const EVENT_BUILD_COMPLETED = 'NEXT_BUILD_COMPLETED'
type EventBuildCompleted = {
bundler: 'webpack' | 'rspack' | 'turbopack'
durationInSeconds: number
totalPageCount: number
hasDunderPages: boolean
hasTestPages: boolean
totalAppPagesCount?: number
}Sources: packages/next/src/telemetry/events/build.ts:76-84
Note
NEXT_BUILD_OPTIMIZED aggregates detailed route metrics including static, server, SSR, and edge runtime counts for both pages and App Router directories, alongside route matcher counts like headersCount, rewritesCount, and redirectsCount.
Sources: packages/next/src/telemetry/events/build.ts:124-149
SWC Feature Detection and Compiler Failures
The TelemetryPlugin inspects the Webpack ModuleGraph during compilation by tapping into module finish hooks to resolve incoming connections and count unique origin imports for specific features. When next-swc native binding loads fail, eventSwcLoadFailure gathers host runtime diagnostics including glibcVersion from process.report?.getReport().header.glibcVersionRuntime and installed optional SWC package versions.
Sources: packages/next/src/build/webpack/plugins/telemetry-plugin/telemetry-plugin.ts:168-235, packages/next/src/telemetry/events/swc-load-failure.ts:21-55
telemetry.record({
eventName: EVENT_PLUGIN_PRESENT,
payload: {
nextVersion: process.env.__NEXT_VERSION as string,
glibcVersion,
installedSwcPackages,
arch: process.arch,
platform: process.platform,
nodeVersion: process.versions.node,
wasm: event?.wasm,
nativeBindingsErrorCode: event?.nativeBindingsErrorCode,
},
})
// ensure this event is flushed before process exits
await telemetry.flush()Sources: packages/next/src/telemetry/events/swc-load-failure.ts:57-71
| Feature Name | Module Path or Regexp Identifier | Category |
|---|---|---|
next/image | /next/image.js | Built-in Component |
next/future/image | /next/future/image.js | Built-in Component |
next/legacy/image | /next/legacy/image.js | Built-in Component |
next/script | /next/script.js | Built-in Component |
next/dynamic | /next/dynamic.js | Built-in Component |
@next/font/google | /\/@next\/font\/google\/target\.css\?.+$/ | Font Optimization |
@next/font/local | /\/@next\/font\/local\/target\.css\?.+$/ | Font Optimization |
next/font/google | /\/next\/font\/google\/target\.css\?.+$/ | Font Optimization |
next/font/local | /\/next\/font\/local\/target\.css\?.+$/ | Font Optimization |
Sources: packages/next/src/build/webpack/plugins/telemetry-plugin/telemetry-plugin.ts:65-77
Warning
eventErrorThrown intentionally strips out error message contents to protect user privacy, recording solely the error class name (errorCode) and any anonymized internal source tree location path.
Sources: packages/next/src/telemetry/events/build.ts:311-326
CLI lifecycle and upgrade telemetry
Session Stop Events and MCP Tool Tracking
Next.js monitors CLI usage by capturing session termination via eventCliSessionStopped, which records environment identifiers and directory structures under NEXT_CLI_SESSION_STOPPED. Version checking relies strictly on process.env.__NEXT_VERSION; if unpopulated, payload emission is aborted. Alongside core CLI metrics, McpTelemetryTracker records tool invocation counts for Model Context Protocol features.
Sources: packages/next/src/server/mcp/mcp-telemetry-tracker.ts:1-47, packages/next/src/telemetry/events/session-stopped.ts:1-39
const payload: EventCliSessionStopped = {
nextVersion: process.env.__NEXT_VERSION,
nodeVersion: process.version,
cliCommand: event.cliCommand,
durationMilliseconds: event.durationMilliseconds,
...(typeof event.turboFlag !== 'undefined'
? {
turboFlag: !!event.turboFlag,
}
: {}),
pagesDir: event.pagesDir,
appDir: event.appDir,
isRspack: process.env.NEXT_RSPACK !== undefined,
}Sources: packages/next/src/telemetry/events/session-stopped.ts:25-38
| Payload Field | Type | Description |
|---|---|---|
cliCommand | string | The invoked CLI command name |
nextVersion | string | Resolved from process.env.__NEXT_VERSION |
nodeVersion | string | Resolved from process.version |
turboFlag | boolean | null | Indicates if Turbopack was requested |
durationMilliseconds | number | null | Total session execution duration |
pagesDir | boolean | Presence of a pages directory |
appDir | boolean | Presence of an app directory |
isRspack | boolean | Resolved from process.env.NEXT_RSPACK !== undefined |
Sources: packages/next/src/telemetry/events/session-stopped.ts:3-12, packages/next/src/telemetry/events/session-stopped.ts:25-38
Warning
eventCliSessionStopped acts as an invariant check for build tooling: if process.env.__NEXT_VERSION is not a string, the event array is dropped entirely rather than emitting incomplete version metadata.
Sources: packages/next/src/telemetry/events/session-stopped.ts:20-23
Agent Upgrade and Nudge Telemetry
The telemetry infrastructure tracks automated upgrade policies, nudge prompts, and agent execution results using structured event factories. Every generated event wraps its payload with schemaVersion: 1. Policy detection, user prompt interactions, run initiations, and CLI results are emitted via distinct event channels to separate configuration reach from operational outcomes.
Sources: packages/next/src/telemetry/events/agent-upgrade.ts:28-97
| Type Identifier | Allowed Values / Structure | Purpose |
|---|---|---|
AgentUpgradePolicy | 'security', 'latest', 'experimental-future' | Defines upgrade strictness |
AgentUpgradeOrigin | 'human_manual', 'human_nudge', 'agent_manual', 'agent_nudge' | Tracks how an upgrade run started |
AgentUpgradeHandoffMethod | 'existing_agent', 'launched_agent', 'copied_prompt', 'printed_prompt' | Describes prompt delivery mechanism |
AgentUpgradeCLIResult | 'no_update_needed', 'no_safe_target', 'metadata_failure', 'guide_failure', 'cancelled', 'handoff_issued', 'handoff_failed', 'cli_failure' | Reports CLI preparation and delivery result |
Sources: packages/next/src/telemetry/events/agent-upgrade.ts:4-27
Tip
eventAgentUpgradeCLIResult captures the CLI's preparation and prompt delivery stages, whereas eventAgentUpgradeAgentResult separately records the agent's verified execution outcome (success or failure) and its resulting version.
Sources: packages/next/src/telemetry/events/agent-upgrade.ts:78-97
Trace span telemetry reporting
Next.js bridges internal Trace Profiling spans into the telemetry dispatcher via reportToTelemetry within Trace Profiling. The bridge maps incoming trace event names against a fixed access list, filtering out unlisted metrics before querying global telemetry state. When an active telemetry instance is retrieved from traceGlobals, matching trace events are recorded with their execution duration converted into microseconds.
Sources: packages/next/src/trace/report/to-telemetry.ts:5-27
| Trace Event Name | Mapped Telemetry Event Name | Payload Structure |
|---|---|---|
webpack-invalidated | WEBPACK_INVALIDATED | { durationInMicroseconds: number } |
Sources: packages/next/src/trace/report/to-telemetry.ts:5-9, packages/next/src/trace/report/to-telemetry.ts:21-26
Note
TRACE_EVENT_ACCESSLIST acts as an explicit allowlist: any trace event whose name is absent from the lookup map is silently ignored, preventing arbitrary internal build spans from leaking into telemetry payloads.
Sources: packages/next/src/trace/report/to-telemetry.ts:5-15
To coordinate multiple diagnostic sinks alongside telemetry routing, MultiReporter implements the Reporter interface. It broadcasts trace events, flush operations, and close requests concurrently to an underlying array of reporters, which includes JSON diagnostics, build JSON reports, and telemetry dispatchers.
Sources: packages/next/src/trace/report/index.ts:7-32
User consent and CLI configuration
Next.js provides global consent controls through configuration storage and environment overrides. The Telemetry class constructor inspects process.env.NEXT_TELEMETRY_DISABLED to determine whether collection is blocked.
Sources: packages/next/src/telemetry/storage.ts:56-70
Persistent user preferences are maintained using a configuration store initialized under the project distribution directory or cache storage. Sources: packages/next/src/telemetry/storage.ts:40-82
The next telemetry command interface handles user prompt interactions and preference modifications directly from the command line.
Sources: packages/next/src/cli/next-telemetry.ts:14-51
| Command Option / Flag | Action Performed | Result Output Message |
|---|---|---|
enable | Sets internal telemetry preference to enabled via telemetry.setEnabled(true) | Success! followed by status confirmation |
disable | Sets internal telemetry preference to disabled via telemetry.setEnabled(false) | Your preference has been saved to [path]. or notice that collection is already disabled |
| none | Displays current telemetry configuration status and explanation | Next.js Telemetry title followed by current status block |
Sources: packages/next/src/cli/next-telemetry.ts:14-51
Note
When telemetry is newly initialized without a stored notification date, the Telemetry constructor triggers this.notify() to print an informational attention message to standard output unless skipNotify is set to true.
Sources: packages/next/src/telemetry/storage.ts:61-118
Related
On This Page
OverviewTelemetry storage architecture and identity hashingCore Telemetry Class and Configuration StorageAnonymous Identity and Hashing MechanicsAnonymous system metadata collectionHost Platform and Machine IndicatorsEnvironment and Container DetectionEvent queuing and synchronous flushIn-Memory Buffering and Queue StateHTTPS Payload Dispatch and Retry PoliciesDetached background telemetry flushingArgument Parsing and Execution ValidationDistribution Directory and Event Batch ResolutionEvent Recording, Flushing, and CleanupCompilation and feature adoption eventsBuild Outcomes and Optimization TelemetrySWC Feature Detection and Compiler FailuresCLI lifecycle and upgrade telemetrySession Stop Events and MCP Tool TrackingAgent Upgrade and Nudge TelemetryTrace span telemetry reportingUser consent and CLI configurationRelated