export const metadata = { slug: "guides/tracing", title: "OpenTelemetry and Workers tracing", description: "Configure Next.js-compatible tracing with OpenTelemetry, Sentry, and Cloudflare Workers.", section: "Guides", sectionOrder: 3, order: 2, navTitle: "Tracing and observability", keywords: ["vinext tracing", "OpenTelemetry", "Sentry", "Cloudflare Workers tracing"], }; # OpenTelemetry and Workers tracing Vinext emits Next.js-compatible server traces for App Router and Pages Router requests. Existing Next.js instrumentation continues to use the standard `instrumentation.ts` or `instrumentation.js` entry point; vinext does not add a tracing API or require a vinext-specific adapter. ## Application setup Register your OpenTelemetry SDK or vendor integration exactly as you would in a Next.js application: ```ts // instrumentation.ts import { registerOTel } from "@vercel/otel"; export function register() { registerOTel({ serviceName: "my-app" }); } ``` The OpenTelemetry packages and exporter belong to the application. They are not vinext dependencies, so an application that does not install an SDK has no missing-package error and uses a cheap no-op OpenTelemetry path. Standard integrations such as `@sentry/nextjs` can register their provider through the same global OpenTelemetry API and keep their existing `register()` and `onRequestError` setup. Vinext completes `register()` before traced production user modules evaluate. In ordinary Node builds, when the application directly installs a resolvable, non-transpiled `@opentelemetry/instrumentation`, vinext also registers its ESM loader before those modules evaluate. Other instrumentation loaders remain application-owned. In Node builds, vinext also keeps every `@opentelemetry/*` package listed in the application's `package.json` external to the server bundle. Next.js does not include these packages in its default `serverExternalPackages`; vinext adds them because OpenTelemetry instrumentation depends on package boundaries. `import-in-the-middle` shares one hook registry between its ESM loader and the `Hook` class imported by `@opentelemetry/instrumentation`, so a bundled copy gets a second registry and ESM instrumentation silently stops working. Keeping the packages external also lets their transitive imports resolve from the owning package under strict pnpm installs. Cloudflare Workers builds bundle all server code, so this does not apply to them. To bundle one of these packages instead, for example in a deployment that ships no `node_modules`, list it in `transpilePackages`: ```ts // next.config.ts import type { NextConfig } from "next"; const nextConfig: NextConfig = { transpilePackages: ["@opentelemetry/api"], }; export default nextConfig; ``` A package listed in `transpilePackages` is removed from the automatic externals. Transpiling `@opentelemetry/instrumentation` also skips the automatic ESM loader registration described above, so vinext no longer instruments ESM modules automatically in that build. ## Framework spans Every framework span includes these stable Next.js attributes: - `next.span_category`: `nextjs` - `next.span_name`: the final span name - `next.span_type`: one of the types below The request root uses `BaseServer.handleRequest`. It starts with `http.method` and `http.target`, then records the parameterized `http.route` and `next.route`, `next.rsc`, `http.status_code`, and `error.type` when applicable. Its final `next.span_name` follows Next.js, for example `GET /blog/[slug]` or `RSC GET /blog/[slug]`. OpenTelemetry also updates the span's display name. Workers tracing updates the display name and error status when the runtime supports `updateName()` and `setStatus()`; older runtimes retain the initial display name and still receive the final `next.span_name` and `error.type` attributes. The stable built-in child span types are:
| App Router | Pages Router |
|---|---|
AppRender.getBodyResult
|
Render.getServerSideProps
|
AppRender.fetch
|
Render.getStaticProps
|
AppRouteRouteHandlers.runHandler
|
Render.renderDocument
|
ResolveMetadata.generateMetadata
|
Node.runHandler
|
NextNodeServer.findPageComponents
|
NextNodeServer.findPageComponents
|
NextNodeServer.getLayoutOrPageModule
|
|
NextNodeServer.createComponentTree
|
|
NextNodeServer.startResponse
|