Śledzenie OpenTelemetry z instrumentation.ts
Rejestruj OpenTelemetry za pomocą hooka instrumentation, aby śledzić żądania serwerowe i spany.
Śledzenie OpenTelemetry z instrumentation.ts to bezpłatna lekcja Next.js 15 Fullstack (App Router + Server Actions) na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Next.js 15 Fullstack (App Router + Server Actions), a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Next.js 15 Fullstack (App Router + Server Actions) zawiera 4 lekcji w sumie.
Części tej lekcji nie zostały jeszcze przetłumaczone i są wyświetlane po angielsku.
What Is OpenTelemetry and Why Next.js Needs It
OpenTelemetry (OTel) is a vendor-neutral observability framework that produces traces, metrics, and logs from your application. A trace records the full journey of a single request — from the edge, through Server Components, Server Actions, and database calls — as a tree of spans.
Next.js 15 has built-in, first-class support for OTel via the instrumentation.ts hook. This means you get automatic span creation for:
- App Router page and layout renders
- Route Handler HTTP requests
- Server Actions invocations
- Fetch calls made inside server code
Without tracing you can only guess where latency hides. With tracing you see the exact span — and its duration — that is slow.
Enabling the Instrumentation Hook in next.config.ts
Before instrumentation.ts is picked up, you must opt in. In Next.js 15 the flag is stable, but the config key is still required for older 14-compat setups. Add it to your config:
The file next.config.ts is the TypeScript-native config introduced in Next.js 15. The experimental.instrumentationHook key tells the framework to import instrumentation.ts once when the Node.js server boots — before any request is handled.
After adding this flag, create instrumentation.ts in the project root (same level as app/, not inside it).
// next.config.ts
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
experimental: {
// Required in Next.js 14; stable & default-true in Next.js 15
// but explicit opt-in avoids version ambiguity
instrumentationHook: true,
},
};
export default nextConfig;Installing OpenTelemetry Packages
The OTel ecosystem is split into many small packages. For a Next.js setup you need:
@opentelemetry/sdk-node— the Node.js SDK that wires everything together@opentelemetry/auto-instrumentations-node— auto-instrumentshttp,fetch,dns, and popular libraries without manual code@opentelemetry/exporter-trace-otlp-http— exports spans over OTLP/HTTP to a collector (Jaeger, Grafana Tempo, Honeycomb, Datadog, etc.)@opentelemetry/resourcesand@opentelemetry/semantic-conventions— describe your service with standard attribute names
Install them all as production dependencies:
// Terminal — install once
// npm install @opentelemetry/sdk-node \
// @opentelemetry/auto-instrumentations-node \
// @opentelemetry/exporter-trace-otlp-http \
// @opentelemetry/resources \
// @opentelemetry/semantic-conventions
// No code to run — this is a shell command reference.
// After install, package.json will contain these entries:
const expectedDeps = [
'@opentelemetry/sdk-node',
'@opentelemetry/auto-instrumentations-node',
'@opentelemetry/exporter-trace-otlp-http',
'@opentelemetry/resources',
'@opentelemetry/semantic-conventions',
];
console.log('Required OTel packages:', expectedDeps);The register() Export — Entry Point for Instrumentation
instrumentation.ts must export a named async function called register(). Next.js calls it exactly once when the server process starts.
Key rules:
- The file runs in the Node.js runtime only — not in the Edge runtime or the browser.
- Use
process.env.NEXT_RUNTIME === 'nodejs'to guard Node-only imports, because Next.js may import the file in other runtimes during analysis. - SDK initialisation must complete synchronously or before the first request —
register()is awaited by the framework.
// instrumentation.ts (project root)
export async function register() {
// Guard: only run the heavy Node.js OTel SDK in the Node runtime.
// Edge runtime does not support 'async_hooks' which OTel depends on.
if (process.env.NEXT_RUNTIME === 'nodejs') {
// Dynamically import so the module is never bundled into Edge chunks.
await import('./instrumentation.node');
}
}Creating instrumentation.node.ts — SDK Initialisation
By convention, the heavy setup lives in a separate file instrumentation.node.ts that is only ever imported inside the Node runtime guard. This keeps your bundle clean.
The NodeSDK class from @opentelemetry/sdk-node is the main entry point. You pass it:
- A resource — metadata about your service (name, version, environment)
- A trace exporter — where spans are sent
- Optional instrumentations — auto-instrument libraries automatically
Call sdk.start() to activate. Register a SIGTERM handler to flush in-flight spans before the process exits.
// instrumentation.node.ts (project root)
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { Resource } from '@opentelemetry/resources';
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
const sdk = new NodeSDK({
resource: new Resource({
[ATTR_SERVICE_NAME]: process.env.OTEL_SERVICE_NAME ?? 'my-nextjs-app',
[ATTR_SERVICE_VERSION]: process.env.npm_package_version ?? '0.0.0',
}),
traceExporter: new OTLPTraceExporter({
// Default: http://localhost:4318/v1/traces
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? 'http://localhost:4318/v1/traces',
}),
instrumentations: [
getNodeAutoInstrumentations({
// Disable noisy filesystem instrumentation in Next.js
'@opentelemetry/instrumentation-fs': { enabled: false },
}),
],
});
sdk.start();
// Flush spans before the process exits (e.g. during Vercel cold-start teardown)
process.on('SIGTERM', () => {
sdk.shutdown().finally(() => process.exit(0));
});What Spans Next.js Creates Automatically
Once the SDK is running, Next.js 15 emits spans automatically for every server-side operation. You do not write any tracing code in your components. The built-in spans include:
BaseServer.handleRequest— top-level span for every HTTP requestNextNodeServer.findPageComponents— span for resolving which page/layout to renderAppRender.getBodyResult— span covering React server renderingAppRouteRouteHandler.runHandler— span wrapping a Route Handler- Nested fetch spans — each
fetch()call in server code becomes a child span with URL, method, and status
These spans are automatically linked as a parent-child tree so you can see the full call graph in your tracing UI (Jaeger, Grafana Tempo, Honeycomb, etc.).
Adding Custom Spans with @opentelemetry/api
Auto-instrumentation covers HTTP and fetch. For your own business logic — such as a slow database query helper or a complex computation — you create custom spans using @opentelemetry/api.
The pattern is always:
- Get the global tracer:
trace.getTracer('your-scope-name') - Call
tracer.startActiveSpan('span-name', async (span) => { ... }) - Set attributes on the span for searchable metadata
- Always call
span.end()in afinallyblock
Custom spans automatically become children of the currently active span, so they nest correctly in the trace tree.
// lib/db.ts — wrapping a Postgres query with a custom span
import { trace, SpanStatusCode } from '@opentelemetry/api';
import { sql } from '@vercel/postgres'; // or any pg client
const tracer = trace.getTracer('my-nextjs-app/db');
export async function getUserById(id: string) {
return tracer.startActiveSpan('db.getUserById', async (span) => {
span.setAttributes({
'db.system': 'postgresql',
'db.operation': 'SELECT',
'app.user.id': id,
});
try {
const result = await sql`SELECT * FROM users WHERE id = ${id} LIMIT 1`;
span.setStatus({ code: SpanStatusCode.OK });
return result.rows[0] ?? null;
} catch (err) {
span.recordException(err as Error);
span.setStatus({ code: SpanStatusCode.ERROR, message: (err as Error).message });
throw err;
} finally {
span.end();
}
});
}Tracing Inside a Server Action
Server Actions run on the server and are wrapped by Next.js in their own HTTP lifecycle. You can add custom spans inside a Server Action just as you would in any server function — the active span context is propagated automatically.
This example creates a span around the database write so you can see separately how long validation took versus the actual INSERT:
// app/actions/create-post.ts
'use server';
import { trace, SpanStatusCode } from '@opentelemetry/api';
import { revalidatePath } from 'next/cache';
import { z } from 'zod';
const tracer = trace.getTracer('my-nextjs-app/actions');
const CreatePostSchema = z.object({
title: z.string().min(1).max(200),
body: z.string().min(1),
});
export async function createPost(formData: FormData) {
return tracer.startActiveSpan('action.createPost', async (span) => {
try {
// Validation span — nested child
const parsed = CreatePostSchema.safeParse({
title: formData.get('title'),
body: formData.get('body'),
});
if (!parsed.success) {
span.setStatus({ code: SpanStatusCode.ERROR, message: 'Validation failed' });
return { error: parsed.error.flatten() };
}
// Simulate DB insert (replace with real client)
// await db.posts.create({ data: parsed.data });
span.setAttributes({ 'post.title': parsed.data.title });
span.setStatus({ code: SpanStatusCode.OK });
revalidatePath('/posts');
return { success: true };
} finally {
span.end();
}
});
}Propagating Trace Context Across fetch() Calls
When your Next.js server calls an external microservice with fetch(), OTel should inject W3C Trace Context headers (traceparent, tracestate) so the downstream service's spans appear as children in the same trace.
With getNodeAutoInstrumentations() enabled, the @opentelemetry/instrumentation-undici or @opentelemetry/instrumentation-http package handles this automatically for fetch and http calls. You do not need to manually add headers.
To verify propagation is working, inspect the outgoing request headers — you should see:
traceparent: 00-<traceId>-<spanId>-01- The receiving service must also run OTel and read these headers (W3C standard).
// app/api/summary/route.ts — fetch with automatic context propagation
import { NextResponse } from 'next/server';
import { trace } from '@opentelemetry/api';
const tracer = trace.getTracer('my-nextjs-app/api');
export async function GET() {
return tracer.startActiveSpan('api.getSummary', async (span) => {
try {
// OTel auto-instrumentation injects 'traceparent' header automatically.
// The downstream service will see this request as a child span.
const response = await fetch('https://internal-api.example.com/data', {
next: { revalidate: 60 }, // Next.js cache hint
});
if (!response.ok) {
span.setStatus({ code: 2, message: `Upstream ${response.status}` });
return NextResponse.json({ error: 'upstream failed' }, { status: 502 });
}
const data = await response.json();
span.setStatus({ code: 1 });
return NextResponse.json(data);
} finally {
span.end();
}
});
}Environment Variables and Collector Configuration
The OTel SDK respects the standard OTel environment variables, which means you rarely need to hard-code endpoints in code. Set these in .env.local (development) and your hosting platform's env config (production):
OTEL_SERVICE_NAME— identifies your service in the tracing UIOTEL_EXPORTER_OTLP_ENDPOINT— the collector URL (e.g.http://localhost:4318for a local Jaeger all-in-one)OTEL_TRACES_SAMPLER— e.g.parentbased_traceidratioto sample only a fraction of traces in productionOTEL_TRACES_SAMPLER_ARG— e.g.0.1to sample 10% of traces
For Vercel, install the Vercel OTel integration or use @vercel/otel which wraps the SDK and reads these same env vars from the Vercel dashboard.
# .env.local — development with a local Jaeger all-in-one container
# docker run -d --name jaeger -p 16686:16686 -p 4318:4318 jaegertracing/all-in-one
OTEL_SERVICE_NAME=my-nextjs-app
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
# Sample every request in dev, 10% in production
# (set OTEL_TRACES_SAMPLER_ARG=0.1 in prod env)
OTEL_TRACES_SAMPLER=always_on
OTEL_TRACES_SAMPLER_ARG=1Using @vercel/otel for Simplified Setup
Vercel provides @vercel/otel, a thin wrapper around the OTel SDK optimised for Next.js deployments. It reduces boilerplate to a single registerOTel() call and handles Edge runtime compatibility automatically.
When deploying to Vercel, this is the recommended approach. For self-hosted or Docker deployments, the manual NodeSDK setup from earlier scenes gives you more control.
Both approaches produce identical spans — the difference is only in setup complexity and Edge support.
// instrumentation.ts — simplified with @vercel/otel
// npm install @vercel/otel
import { registerOTel } from '@vercel/otel';
export function register() {
// Works in both Node.js and Edge runtimes.
// Reads OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_ENDPOINT from env automatically.
registerOTel({
serviceName: process.env.OTEL_SERVICE_NAME ?? 'my-nextjs-app',
// Optionally add custom attributes visible on every span:
attributes: {
'deployment.environment': process.env.VERCEL_ENV ?? 'development',
'deployment.region': process.env.VERCEL_REGION ?? 'local',
},
});
}Knowledge Check: Where Does SDK Initialisation Belong?
You are setting up OpenTelemetry tracing for a Next.js 15 App Router project deployed on a self-hosted Node.js server. Which approach correctly initialises the OTel NodeSDK?
Recap: OpenTelemetry Tracing with instrumentation.ts
In this lesson you learned how to bring production-grade distributed tracing to a Next.js 15 App Router application:
- Enable the hook — set
experimental.instrumentationHook: trueinnext.config.ts(stable default in Next.js 15). - Create instrumentation.ts — export a
register()function at the project root; use it as the single entry point for all server-side initialisation. - Guard the runtime — wrap Node.js-only imports with
process.env.NEXT_RUNTIME === 'nodejs'and use dynamicimport()to prevent Edge bundling. - Initialise NodeSDK in a separate
instrumentation.node.ts— provide aResource, anOTLPTraceExporter, and auto-instrumentations; callsdk.start()once. - Automatic spans — Next.js emits spans for page renders, Route Handlers, Server Actions, and fetch calls with no extra code.
- Custom spans — use
trace.getTracer()andstartActiveSpan()from@opentelemetry/apito instrument your own business logic. - Context propagation — W3C
traceparentheaders are injected automatically into outgoing fetch calls by auto-instrumentation. - Configuration — standard
OTEL_*env vars drive the SDK; no need to hard-code collector URLs in source code.
Często zadawane pytania
Czy lekcja „Śledzenie OpenTelemetry z instrumentation.ts” jest bezpłatna?
Tak — pełny tekst „Śledzenie OpenTelemetry z instrumentation.ts” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Next.js 15 Fullstack (App Router + Server Actions), przejdź na CoddyKit PRO. Kurs Next.js 15 Fullstack (App Router + Server Actions) zawiera 4 lekcji w sumie.
Co nauczysz się w „Śledzenie OpenTelemetry z instrumentation.ts”?
Rejestruj OpenTelemetry za pomocą hooka instrumentation, aby śledzić żądania serwerowe i spany. Ćwiczysz Next.js 15 Fullstack (App Router + Server Actions) z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Next.js 15 Fullstack (App Router + Server Actions)?
Nie wymagamy żadnego doświadczenia. Next.js 15 Fullstack (App Router + Server Actions) w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „Śledzenie OpenTelemetry z instrumentation.ts”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Next.js 15 Fullstack (App Router + Server Actions)?
Tak. Każda lekcja Next.js 15 Fullstack (App Router + Server Actions) zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Śledzenie OpenTelemetry z instrumentation.ts
- Szczegółowe granice error.tsx i global-error
- Ustrukturyzowane logowanie na serwerze i Edge
- Przechwytywanie błędów Server Actions i telemetrii