install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/claude-code/otel-nextjs-integration" ~/.claude/skills/intense-visions-harness-engineering-otel-nextjs-integration-f96faf && rm -rf "$T"
manifest:
agents/skills/claude-code/otel-nextjs-integration/SKILL.mdsource content
OpenTelemetry Next.js Integration
Add OpenTelemetry tracing to Next.js with the instrumentation hook for Server Components, API routes, and middleware
When to Use
- Adding distributed tracing to a Next.js application
- Tracing Server Component rendering, API routes, and server actions
- Debugging slow page loads across data fetching and rendering
- Connecting Next.js traces to backend microservice traces
Instructions
- Create
(orinstrumentation.ts
) in the project root — Next.js loads it automatically.instrumentation.node.ts - Export a
function that initializes the OpenTelemetry SDK. This runs once on server startup.register - Enable the
ininstrumentationHook
(Next.js 13-14). In Next.js 15+, it is enabled by default.next.config.js - Use
for simplified setup on Vercel, or configure the standard OTel SDK for any platform.@vercel/otel - Next.js auto-creates spans for rendering, data fetching, and route resolution. Add custom spans for business logic.
// instrumentation.ts (project root) export async function register() { if (process.env.NEXT_RUNTIME === 'nodejs') { // Only initialize on the Node.js runtime, not edge const { NodeSDK } = await import('@opentelemetry/sdk-node'); const { getNodeAutoInstrumentations } = await import('@opentelemetry/auto-instrumentations-node'); const { OTLPTraceExporter } = await import('@opentelemetry/exporter-trace-otlp-http'); const { Resource } = await import('@opentelemetry/resources'); const { ATTR_SERVICE_NAME } = await import('@opentelemetry/semantic-conventions'); const sdk = new NodeSDK({ resource: new Resource({ [ATTR_SERVICE_NAME]: process.env.OTEL_SERVICE_NAME || 'nextjs-app', }), traceExporter: new OTLPTraceExporter({ url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4318/v1/traces', }), instrumentations: [ getNodeAutoInstrumentations({ '@opentelemetry/instrumentation-fs': { enabled: false }, }), ], }); sdk.start(); } }
// next.config.js (Next.js 13-14, not needed in 15+) /** @type {import('next').NextConfig} */ const nextConfig = { experimental: { instrumentationHook: true, }, }; module.exports = nextConfig;
// Simplified setup with @vercel/otel // instrumentation.ts import { registerOTel } from '@vercel/otel'; export function register() { registerOTel({ serviceName: 'nextjs-app', // Automatically configured for Vercel's tracing infrastructure }); }
Details
Next.js automatic spans: With OpenTelemetry configured, Next.js creates spans for:
— route matching and resolutionnext.route
— Server Component renderingnext.render
— data fetching (Pages Router)next.getServerSideProps
— server-side fetch() callsnext.fetch
— middleware executionnext.middleware
Custom spans in Server Components:
// app/dashboard/page.tsx import { trace } from '@opentelemetry/api'; const tracer = trace.getTracer('dashboard'); export default async function DashboardPage() { const data = await tracer.startActiveSpan('dashboard.loadData', async (span) => { try { const [users, metrics] = await Promise.all([ fetchUsers(), fetchMetrics(), ]); span.setAttribute('users.count', users.length); return { users, metrics }; } finally { span.end(); } }); return <Dashboard data={data} />; }
Custom spans in Route Handlers:
// app/api/orders/route.ts import { trace, SpanStatusCode } from '@opentelemetry/api'; const tracer = trace.getTracer('orders-api'); export async function POST(request: Request) { return tracer.startActiveSpan('orders.create', async (span) => { try { const body = await request.json(); span.setAttribute('order.items', body.items.length); const order = await createOrder(body); span.setAttribute('order.id', order.id); return Response.json(order, { status: 201 }); } catch (error) { span.setStatus({ code: SpanStatusCode.ERROR }); span.recordException(error as Error); return Response.json({ error: 'Failed' }, { status: 500 }); } finally { span.end(); } }); }
Edge runtime limitations: The Edge runtime does not support the full Node.js OTel SDK. For edge middleware and edge routes, use
@vercel/otel which provides a lightweight implementation, or check for NEXT_RUNTIME === 'edge' and use a compatible exporter.
Vercel integration: On Vercel, use
@vercel/otel for zero-config tracing. Traces are sent to Vercel's observability dashboard and can be forwarded to external backends (Datadog, Honeycomb, Axiom).
Source
https://nextjs.org/docs/app/building-your-application/optimizing/open-telemetry
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.