OpenTelemetry using Next.js
This guide demonstrates how to configure OpenTelemetry in a Next.js app to send telemetry data to Axiom.
OpenTelemetry provides a standardized way to collect and export telemetry data from your Next.js apps. This guide walks you through the process of configuring OpenTelemetry in a Next.js app to send traces to Axiom using the OpenTelemetry SDK.
Prerequisites
- Create an Axiom account.
- Create a dataset in Axiom where you send your data.
- Create an API token in Axiom with permissions to create, read, update, and delete datasets.
- Install Node.js version 14 or newer.
- An existing Next.js app. Alternatively, use the provided example project.
Initial setup
For initial setup, choose one of the following options:
- Use the
@vercel/otel
package for easier setup. - Set up your app without the
@vercel/otel
package.
Initial setup with @vercel/otel
To use the @vercel/otel
package for easier setup, run the following command to install the dependencies:
npm install @vercel/otel @opentelemetry/exporter-trace-otlp-http @opentelemetry/sdk-trace-node
Create an instrumentation.ts
file in the root of your project with the following content:
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node';
import { registerOTel } from '@vercel/otel';
export function register() {
registerOTel({
serviceName: 'nextjs-app',
spanProcessors: [
new SimpleSpanProcessor(
new OTLPTraceExporter({
url: 'https://api.axiom.co/v1/traces',
headers: {
Authorization: `Bearer ${process.env.API_TOKEN}`,
'X-Axiom-Dataset': `${process.env.DATASET_NAME}`,
},
})
),
],
});
}
Add the API_TOKEN
and DATASET_NAME
environment variables to your .env
file. For example:
API_TOKEN=xaat-123
DATASET_NAME=my-dataset
Initial setup without @vercel/otel
To set up your app without the @vercel/otel
package, run the following command to install the dependencies:
npm install @opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-http @opentelemetry/resources @opentelemetry/semantic-conventions @opentelemetry/sdk-trace-node
Create an instrumentation.ts
file in the root of your project with the following content:
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { Resource } from '@opentelemetry/resources';
import { SEMRESATTRS_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node';
export function register() {
const sdk = new NodeSDK({
resource: new Resource({
[SEMRESATTRS_SERVICE_NAME]: 'nextjs-app',
}),
spanProcessor: new SimpleSpanProcessor(
new OTLPTraceExporter({
url: 'https://api.axiom.co/v1/traces',
headers: {
Authorization: `Bearer ${process.env.API_TOKEN}`,
'X-Axiom-Dataset': process.env.DATASET_NAME,
},
})
),
});
sdk.start();
}
Add the API_TOKEN
and DATASET_NAME
environment variables to your .env
file. For example:
API_TOKEN=xaat-123
DATASET_NAME=my-dataset
Set up the Next.js environment
layout.tsx
In the src/app/layout.tsx
file, import and call the register
function from the instrumentation
module:
import { register } from '../../instrumentation';
register();
export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
This file sets up the root layout for your Next.js app and initializes the OpenTelemetry instrumentation by calling the register
function.
route.ts
Create a route.ts
file in src/app/api/rolldice/
to handle HTTP GET requests to the /rolldice
API endpoint:
// src/app/api/rolldice/route.ts
import { NextResponse } from 'next/server';
function getRandomNumber(min: number, max: number): number {
return Math.floor(Math.random() * (max - min) + min);
}
export async function GET() {
const diceRoll = getRandomNumber(1, 6);
return NextResponse.json(diceRoll.toString());
}
This file defines a route handler for the /rolldice
endpoint, which returns a random number between 1 and 6.
next.config.js
Configure the next.config.js
file to enable instrumentation and resolve the tls
module:
module.exports = {
experimental: {
// Enable the instrumentation hook for collecting telemetry data
instrumentationHook: true,
},
webpack: (config, { isServer }) => {
if (!isServer) {
config.resolve.fallback = {
// Disable the 'tls' module on the client side
tls: false,
};
}
return config;
},
};
This configuration enables the instrumentation hook and resolves the tls
module for the client-side build.
tsconfig.json
Add the following options to your tsconfig.json
file to ensure compatibility with OpenTelemetry and Next.js:
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
This file configures the TypeScript compiler options for your Next.js app.
Project structure
After completing the steps above, the project structure of your Next.js app is the following:
my-nextjs-app/
├── src/
│ ├── app/
│ │ ├── api/
│ │ │ └── rolldice/
│ │ │ └── route.ts
│ │ ├── page.tsx
│ │ └── layout.tsx
│ └── ...
├── instrumentation.ts
├── next.config.js
├── tsconfig.json
└── ...
Run the app and observe traces in Axiom
Use the following command to run your Next.js app with OpenTelemetry instrumentation in development mode:
npm run dev
This command starts the Next.js development server, and the OpenTelemetry instrumentation automatically collects traces. As you interact with your app, traces are sent to Axiom where you can monitor and analyze your app’s performance and behavior.
In Axiom, go to the Stream tab and click your dataset. This page displays the traces sent to Axiom and lets you monitor and analyze your app’s performance and behavior.
Go to the Dashboards tab and click OpenTelemetry Traces. This pre-built traces dashboard provides further insights into the performance and behavior of your app.
Send data from an existing Next.js project
Manual instrumentation
Manual instrumentation allows you to create, configure, and manage spans and traces, providing detailed control over telemetry data collection at specific points within the app.
- Set up and retrieve a tracer from the OpenTelemetry API. This tracer starts and manages spans within your app components or API routes.
import { trace } from '@opentelemetry/api';
const tracer = trace.getTracer('nextjs-app');
- Manually start a span at the beginning of significant operations or transactions within your Next.js app and ensure you end it appropriately. This approach is for tracing specific custom events or operations not automatically captured by instrumentations.
const span = tracer.startSpan('operationName');
try {
// Perform your operation here
} finally {
span.end();
}
- Enhance the span with additional information such as user details or operation outcomes, which can provide deeper insights when analyzing telemetry data.
span.setAttribute('user_id', userId);
span.setAttribute('operation_status', 'success');
Automatic instrumentation
Automatic instrumentation uses the capabilities of OpenTelemetry to automatically capture telemetry data for standard operations such as HTTP requests and responses.
- Use the OpenTelemetry Node SDK to configure your app to automatically instrument supported libraries and frameworks. Set up
NodeSDK
in aninstrumentation.ts
file in your project.
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
export function register() {
const sdk = new NodeSDK({
resource: new Resource({ [SEM_RESOURCE_ATTRIBUTES.SERVICE_NAME]: 'nextjs-app' }),
spanProcessor: new BatchSpanProcessor(
new OTLPTraceExporter({
url: 'https://api.axiom.co/v1/traces',
headers: {
Authorization: `Bearer ${process.env.API_TOKEN}`,
'X-Axiom-Dataset': `${process.env.DATASET_NAME_NAME}`,
},
})
),
});
sdk.start();
}
-
Include necessary OpenTelemetry instrumentation packages to automatically capture telemetry from Node.js libraries like HTTP and any other middlewares used by Next.js.
-
Call the
register
function from theinstrumentation.ts
within your app startup file or before your app starts handling traffic to initialize the OpenTelemetry instrumentation.
// In pages/_app.js or an equivalent entry point
import { register } from '../instrumentation';
register();
Reference
List of OpenTelemetry trace fields
Field Category | Field Name | Description |
---|---|---|
General Trace Information | ||
_rowId | Unique identifier for each row in the trace data. | |
_sysTime | System timestamp when the trace data was recorded. | |
_time | Timestamp when the actual event being traced occurred. | |
trace_id | Unique identifier for the entire trace. | |
span_id | Unique identifier for the span within the trace. | |
parent_span_id | Unique identifier for the parent span within the trace. | |
HTTP Attributes | ||
attributes.http.method | HTTP method used for the request. | |
attributes.http.status_code | HTTP status code returned in response. | |
attributes.http.route | Route accessed during the HTTP request. | |
attributes.http.target | Specific target of the HTTP request. | |
Custom Attributes | ||
attributes.custom[“next.route”] | Custom attribute defining the Next.js route. | |
attributes.custom[“next.rsc”] | Indicates if React Server Components are used. | |
attributes.custom[“next.span_name”] | Custom name of the span within Next.js context. | |
attributes.custom[“next.span_type”] | Type of the Next.js span, describing the operation context. | |
Resource Process Attributes | ||
resource.process.pid | Process ID of the Node.js app. | |
resource.process.runtime.description | Description of the runtime environment. For example, Node.js. | |
resource.process.runtime.name | Name of the runtime environment. For example, nodejs. | |
resource.process.runtime.version | Version of the runtime environment For example, 18.17.0. | |
resource.process.executable.name | Executable name running the process. For example, next-server. | |
Resource Host Attributes | ||
resource.host.arch | Architecture of the host machine. For example, arm64. | |
resource.host.name | Name of the host machine. For example, MacBook-Pro.local. | |
Operational Details | ||
duration | Time taken for the operation. | |
kind | Type of span (for example, server, internal). | |
name | Name of the span, often a high-level title for the operation. | |
Scope Attributes | ||
scope.name | Name of the scope for the operation. For example, next.js. | |
scope.version | Version of the scope. For example, 0.0.1. | |
Service Attributes | ||
service.name | Name of the service generating the trace. For example, nextjs-app. | |
Telemetry SDK Attributes | ||
telemetry.sdk.language | Language of the telemetry SDK. For example, nodejs. | |
telemetry.sdk.name | Name of the telemetry SDK. For example, opentelemetry. | |
telemetry.sdk.version | Version of the telemetry SDK. For example, 1.23.0. |
s
List of imported libraries
@opentelemetry/api
The core API for OpenTelemetry in JavaScript, providing the necessary interfaces and utilities for tracing, metrics, and context propagation. In the context of Next.js, it allows developers to manually instrument custom spans, manipulate context, and access the active span if needed.
@opentelemetry/exporter-trace-otlp-http
This exporter enables your Next.js app to send trace data over HTTP to any backend that supports the OTLP (OpenTelemetry Protocol), such as Axiom. Using OTLP ensures compatibility with a wide range of observability tools and standardizes the data export process.
@opentelemetry/resources
This defines the Resource which represents the entity producing telemetry. In Next.js, Resources can be used to describe the app (for example, service name, version) and are attached to all exported telemetry, aiding in identifying data in backend systems.
@opentelemetry/sdk-node
The OpenTelemetry SDK for Node.js which provides a comprehensive set of tools for instrumenting Node.js apps. It includes automatic instrumentation for popular libraries and frameworks, as well as APIs for manual instrumentation. In the Next.js setup, it’s used to configure and initialize the OpenTelemetry SDK.
@opentelemetry/semantic-conventions
A set of standard attributes and conventions for describing resources, spans, and metrics in OpenTelemetry. By adhering to these conventions, your Next.js app’s telemetry data becomes more consistent and interoperable with other OpenTelemetry-compatible tools and systems.
@vercel/otel
A package provided by Vercel that simplifies the setup and configuration of OpenTelemetry for Next.js apps deployed on the Vercel platform. It abstracts away some of the boilerplate code and provides a more streamlined integration experience.
Was this page helpful?