ThisCalculator

Everyday

Next.js 16 Instrumentation, OpenTelemetry and Custom Spans

Updated · Standard formula, shown below

Next.js 16 Instrumentation, OpenTelemetry and Custom Spans

Yes: Next.js 16 supports application instrumentation through an instrumentation.ts file, and OpenTelemetry can capture traces that include custom spans. Put shared setup in the instrumentation hook, create spans where you need extra detail, and export telemetry through an OpenTelemetry-compatible SDK or collector; the exact setup depends on your runtime and exporter.

What instrumentation.ts does in Next.js 16

The instrumentation.ts file is a framework-recognized entry point for registering code that should run as the Next.js server starts. It is intended for observability setup such as initializing tracing, monitoring, or logging integrations, rather than for handling an individual page request. Place it in the project root, or under src if the application uses that directory convention, alongside the relevant Next.js project structure.

Export an asynchronous register function. Next.js invokes it during server startup, and it can dynamically import runtime-specific setup. This matters because a Next.js application may run in a Node.js environment or an Edge environment, whose available APIs and compatible instrumentation libraries differ. Avoid assuming that Node-only packages work in every runtime.

Instrumentation is not the same as a complete telemetry pipeline. The file provides a place to initialize instrumentation; an SDK or supported integration creates and manages telemetry, and an exporter or collector sends it somewhere. You still need to configure where data goes, which signals to collect, and how the production deployment supplies that configuration.

How OpenTelemetry fits into Next.js

OpenTelemetry is a set of APIs, SDKs, and conventions for collecting traces, metrics, and logs. In tracing, a trace represents a request or operation across components. A span is one timed operation within that trace; parent and child spans show how work is related. Automatic instrumentation can cover supported frameworks and libraries, while manual spans add context specific to your application.

Next.js can expose tracing through its OpenTelemetry integration, but automatic coverage is not a promise that every operation or dependency will be recorded. Coverage depends on the framework version, runtime, instrumentation package, and libraries involved. Follow the current Next.js and OpenTelemetry guidance for the version you deploy, and verify what your chosen exporter actually receives.

A typical setup has three parts:

  • Registration: instrumentation.ts loads the appropriate initialization code when the server starts.
  • Instrumentation: framework integrations and libraries create spans automatically; your code can add manual spans.
  • Export: an SDK sends completed telemetry to a collector or backend, often using OTLP or another supported exporter.

Register instrumentation safely

A minimal pattern is to branch on the runtime and dynamically load a separate module. The exact import names and SDK setup depend on the packages selected, so this sketch shows the structure rather than a copy-and-paste exporter configuration:

export async function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    await import('./instrumentation.node')
  }

  if (process.env.NEXT_RUNTIME === 'edge') {
    await import('./instrumentation.edge')
  }
}

Keep initialization out of request handlers and make it safe to execute in the deployment model you use. Do not start a second SDK or register duplicate global providers on every request. In serverless environments, instances may start and stop independently, so initialization should tolerate repeated process starts across instances without assuming a single permanent server.

Store exporter endpoints and credentials in deployment environment variables, not source code. Use only the variables required by your exporter, and avoid logging secret values. When testing locally, confirm that the collector or backend is reachable and that the SDK is configured to export rather than merely create spans in memory.

Adding custom spans

Manual spans are useful for meaningful application operations that automatic instrumentation cannot explain—for example, a database lookup, a call to an internal service, or a multi-step business operation. Use the OpenTelemetry API and the active context so the span becomes a child of the current request trace when context propagation is available.

Conceptually, create a span, perform the operation, record relevant attributes, and end the span even when the operation fails. With an OpenTelemetry tracer, a common pattern is startActiveSpan and a try/finally block:

await tracer.startActiveSpan('catalog.lookup', async (span) => {
  try {
    span.setAttribute('catalog.source', 'primary')
    return await loadCatalogItem(itemId)
  } catch (error) {
    span.recordException(error)
    span.setStatus({ code: SpanStatusCode.ERROR })
    throw error
  } finally {
    span.end()
  }
})

This example assumes the tracer and status type have been imported from a configured OpenTelemetry API and that the surrounding runtime supports the operation. Follow the API version used by your dependencies. If asynchronous work loses context, inspect context propagation and instrumentation compatibility rather than assuming that naming a span automatically attaches it to the right trace.

Choose span names that describe stable operations, not individual values. Put useful, low-cardinality details in attributes, such as an operation type or dependency name. Avoid recording passwords, access tokens, request bodies, personal data, or identifiers that create unbounded numbers of unique values. Telemetry can be stored and searched outside the application, so treat it as production data.

Common setup problems and limits

  • No traces appear: Check that registration runs, the SDK has a provider and exporter, the endpoint is reachable, and sampling has not filtered the request. Also check collector and backend filters.
  • Only some operations appear: Automatic instrumentation is library- and runtime-dependent. Add manual spans around important work or use a compatible instrumentation package.
  • Spans are disconnected: Context propagation may not cross an unsupported library, worker boundary, or outbound request. Check propagation headers and runtime compatibility.
  • Duplicate or excessive spans: Avoid repeated SDK initialization and unnecessary nested spans. High-volume tracing can add processing, network, and storage overhead.
  • Node setup breaks on Edge: Separate runtime-specific modules and avoid importing Node-only dependencies into code that may be bundled for Edge.

Instrumentation also has practical costs: added CPU and memory use, exporter traffic, and backend storage. Sampling can reduce volume, but it changes which traces are retained. Decide how to handle errors, slow requests, and routine traffic, then validate the policy under realistic load. Do not treat telemetry as a substitute for application error handling or service health checks.

Frequently Asked Questions

Does Next.js 16 support OpenTelemetry?
Yes. Next.js provides an instrumentation entry point and OpenTelemetry support, but exported data depends on your SDK, integrations, runtime, and exporter configuration.

Where should instrumentation.ts go?
Put it at the project root, or in the src directory when that is where the application’s Next.js code is organized. Follow the current Next.js file-convention guidance for your project layout.

Can I create custom spans in a Server Component?
Custom spans can be created in server-side code when an OpenTelemetry tracer and active context are available. Keep instrumentation compatible with the code’s runtime and avoid assuming that client-side rendering automatically shares server trace context.

Does instrumentation.ts run for every request?
It is a server startup registration hook, not a per-request handler. Use request-level framework hooks or spans inside the relevant operation for work that must be measured on each request.

Can I use the same instrumentation for Node.js and Edge?
Only if every imported dependency and API supports both runtimes. Separate runtime-specific setup when necessary, and test each deployment target independently.

Why are my custom spans missing from traces?
Check that the span is ended, the provider and exporter are configured, the active context is propagated, and sampling or backend filters are not dropping it. Confirm that the code path actually runs in the expected runtime.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.