Repository navigation
Add instrumentationInject for adapter-supplied instrumentation - #100658
Conversation
Failing test suitesCommit: 3eca83f | About building and testing Next.js
Expand output● inject-instrumentation › injected package resolution › runs injected packages and filesystem entries ● inject-instrumentation › injected package resolution › keeps the opted-out implementation out of emitted server bundles ● inject-instrumentation › injected package resolution › traces the external instrumentation package ● inject-instrumentation › instrumentationInject without a project file › runs every injected register in Node.js, in order ● inject-instrumentation › instrumentationInject without a project file › runs every injected register in the Edge runtime, in order ● inject-instrumentation › a project instrumentation file chains after the inject › runs the injected registers, then the project register ● inject-instrumentation › a project instrumentation file with a compound page extension › chains instrumentation.page.ts after the inject ● inject-instrumentation › no inject and no project file › runs no instrumentation
Expand output● inject-instrumentation › injected package resolution › runs injected packages and filesystem entries ● inject-instrumentation › injected package resolution › keeps the opted-out implementation out of emitted server bundles ● inject-instrumentation › injected package resolution › traces the external instrumentation package ● inject-instrumentation › instrumentationInject without a project file › runs every injected register in Node.js, in order ● inject-instrumentation › instrumentationInject without a project file › runs every injected register in the Edge runtime, in order ● inject-instrumentation › a project instrumentation file chains after the inject › runs the injected registers, then the project register ● inject-instrumentation › a project instrumentation file with a compound page extension › chains instrumentation.page.ts after the inject ● inject-instrumentation › no inject and no project file › runs no instrumentation
Expand output● inject-instrumentation › injected package resolution › runs injected packages and filesystem entries ● inject-instrumentation › injected package resolution › keeps the opted-out implementation out of emitted server bundles ● inject-instrumentation › injected package resolution › traces the external instrumentation package ● inject-instrumentation › instrumentationInject without a project file › runs every injected register in Node.js, in order ● inject-instrumentation › instrumentationInject without a project file › runs every injected register in the Edge runtime, in order ● inject-instrumentation › a project instrumentation file chains after the inject › runs the injected registers, then the project register ● inject-instrumentation › a project instrumentation file with a compound page extension › chains instrumentation.page.ts after the inject ● inject-instrumentation › no inject and no project file › runs no instrumentation |
injectInstrumentation: default tracing without an instrumentation.ts
b3db38b to
f0581c7
Compare
injectInstrumentation: default tracing without an instrumentation.ts1c1fb13 to
9762bd2
Compare
9762bd2 to
bdb7103
Compare
bdb7103 to
1e57c97
Compare
1e57c97 to
a7f6d03
Compare
What does that mean? It shoudl execute all of them. No file "wins"?
Anyone can set them. |
858ddec to
f6c0652
Compare
Right, nothing wins anymore. Since d09b2e7 every entry runs in array order, then the project's own
Agreed. The description now says anyone can set it, an adapter in |
Every module is required before any hook runs, so a later async module could reject while an earlier hook was awaiting, and Node would report an unhandled rejection. Attach a no-op handler when combining; callEach still awaits each promise and collects its error.
NextConfigComplete types the field as string[], so modifyConfig can push to it without a guard, matching instrumentationClientInject.
The startup check resolves entries with Node's rules, which don't try .ts or .tsx, so an import-style path that both bundlers accept was reported as not resolving.
Both lists reach webpack only through loader options and the client entry, which the persistent cache doesn't key on. With a warm .next/cache, adding or removing an entry reused the previously generated instrumentation module.
The config merge copied the array references, so a push in modifyConfig changed Next's shared default (or a cached user config), and a later load in the same process saw the entry twice.
A hook that throws, or a module with top-level await that fails to load, is collected. A module without top-level await that throws while loading stops startup before combineInstrumentation runs, as a single broken file does today.
35dabcf to
6dc245e
Compare
vercel/next.js#100658 renamed the config option to match instrumentationClientInject.
lucleray
left a comment
There was a problem hiding this comment.
looks good, thanks! a few nits inline 👍
They ran one after another, so an injected handler that hung kept the project's own onRequestError from ever running, and the request path awaits it. Run them together and collect failures the same way. register keeps its array order.
Resolve conflicts with #100658 (instrumentationInject). Keep both sides: this branch's route-types bootstrap and Turbopack route publisher, and canary's webpack-only instrumentation hook setup. Combine the Rust filesystem imports.
What?
Adds an
instrumentationInject?: string[]config option. Next.js compiles one module that runs every entry'sregisterandonRequestError, then the project's owninstrumentation.{ts,js}if it has one. This works likeinstrumentationClientInjectdoes for the client.Why?
Hosting platforms want to give every app default instrumentation, such as OpenTelemetry tracing. Today that means asking users to add an
instrumentation.tsand a dependency to their project. This hook lets an adapter or a config wrapper supply the module at build time instead. The user's files,package.jsonand lockfile stay untouched, and Next.js installs nothing.How?
modifyConfig, or a config wrapper such aswithSentry. Whoever sets it decides whether to inject when the project already has its own instrumentation file.combineInstrumentation(next/dist/lib/combine-instrumentation).next-instrumentation-inject-loader. Turbopack uses a virtualinstrumentation.inject.cjsat the project root, so atype: modulepackage.json can't change how it parses.registerhooks run in array order.onRequestErrorhooks run concurrently, so a slow or hanging handler can't stop the project's own from running. A hook that throws, or a module with top-level await that fails to load, doesn't stop the others. The error is rethrown after all have run, as anAggregateErrorwhen more than one fails, so Next.js surfaces it as it does for a single file. A module without top-level await that throws while loading stops startup, as a single broken file does today.awaitcome back fromrequire()as promises.combineInstrumentationawaits each one, and marks them handled up front so a later rejection isn't reported as unhandled while an earlier hook runs.serverExternalPackagesand the built-in externals list apply, and an external entry is still included in the server trace. Absolute paths are rewritten to project-relative ones for Turbopack, which resolves a leading/from the project root.instrumentationInjectdefaults to[], likeinstrumentationClientInject, somodifyConfigcan push to it.pageExtensionsextension, in config order, at the project root and then insrc/. Turbopack uses its usualinstrumentationlookup, which also checks parent folders up tooutputFileTracingRootand tries longer extensions first. With more than one candidate file, the two can pick different ones.Tests
test/e2e/app-dir/inject-instrumentationcovers five cases with webpack and Turbopack:register()calls run, in order.register()calls run, then the project's.pageExtensions: ['page.ts', …], the project'sinstrumentation.page.tsis chained.serverExternalPackagesstays out of the Node server bundles and appears ininstrumentation.js.nft.json, and a bundled one is compiled in. The bundle check reads build output, so it's gated to!deploy, and the trace check tostart.The fixtures cover a relative and an absolute entry, an injected entry and a project file that both use top-level
await, and an injected module that imports a sibling file.lib/combine-instrumentation.test.tscoversregisterorder, error isolation,onRequestErrorarguments, concurrentonRequestErrorhandlers (a hanging one doesn't block the rest), promise modules, and that a later rejection isn't left unhandled.Related
nextjs/adapter-vercel#148