smithery/agentient

nextjs-opentelemetry-instrumentation

OpenTelemetry distributed tracing for Next.js 14+

Installation

$ npx skills add smithery/agentient --skill nextjs-opentelemetry-instrumentation

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/agentient · top by installs.

npx skills add smithery/agentient

Browse all from smithery/agentient

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Skill metadata

Parsed from SKILL.md frontmatter.

Version1.0.0

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 7,413 B
  • docs SUMMARY.md 93 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

Next.js OpenTelemetry Instrumentation

Implement distributed tracing for Next.js 14+ App Router applications using OpenTelemetry to track requests from browser through Server Components to API routes.

Setup

1. Install Dependencies

npm install @opentelemetry/sdk-node @opentelemetry/api \
  @opentelemetry/auto-instrumentations-node \
  @opentelemetry/exporter-trace-otlp-http \
  @vercel/otel

2. Create instrumentation.ts

Next.js calls this file on server startup before handling requests:

// instrumentation.ts (root of project)
export async function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    await import('./instrumentation.node')
  }
}
// instrumentation.node.ts
import { NodeSDK } from '@opentelemetry/sdk-node'
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { Resource } from '@opentelemetry/resources'
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'

const sdk = new NodeSDK({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: 'nextjs-app',
    [SemanticResourceAttributes.SERVICE_VERSION]: '1.0.0',
    [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]:
      process.env.NODE_ENV || 'development',
  }),

  traceExporter: new OTLPTraceExporter({
    url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4318/v1/traces',
  }),

  instrumentations: [
    getNodeAutoInstrumentations({
      '@opentelemetry/instrumentation-fs': { enabled: false },
    }),
  ],
})

sdk.start()

3. Enable in next.config.js

// next.config.js
module.exports = {
  experimental: {
    instrumentationHook: true,
  },
}

Automatic Spans

Next.js automatically creates spans for:

  • BaseServer.handleRequest: Root span for incoming request
  • render route (app): Server Component rendering
  • fetch [METHOD] [URL]: Data fetching operations

These appear automatically once instrumentation is enabled.

Manual Spans (Custom Instrumentation)

For business logic not automatically traced:

// app/api/users/route.ts
import { trace } from '@opentelemetry/api'

const tracer = trace.getTracer('my-app')

export async function GET() {
  // Create custom span
  return await tracer.startActiveSpan('fetch-users', async (span) => {
    try {
      const users = await db.query('SELECT * FROM users')

      // Add attributes to span
      span.setAttribute('user.count', users.length)
      span.setAttribute('db.system', 'postgresql')

      return Response.json(users)
    } catch (error) {
      // Record exception
      span.recordException(error as Error)
      span.setStatus({ code: 2 }) // Error status
      throw error
    } finally {
      span.end()
    }
  })
}

Server Component Example

// app/products/[id]/page.tsx
import { trace } from '@opentelemetry/api'

const tracer = trace.getTracer('my-app')

async function getProduct(id: string) {
  return await tracer.startActiveSpan('get-product', async (span) => {
    span.setAttribute('product.id', id)

    const product = await fetch(`https://api.example.com/products/${id}`)
    const data = await product.json()

    span.setAttribute('product.name', data.name)
    span.end()

    return data
  })
}

export default async function ProductPage({ params }: { params: { id: string } }) {
  const product = await getProduct(params.id)
  return <ProductDetails product={product} />
}

Client-Side Tracing

Track browser-side activity:

// app/providers.tsx
'use client'

import { useEffect } from 'react'
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'
import { registerInstrumentations } from '@opentelemetry/instrumentation'
import { FetchInstrumentor } from '@opentelemetry/instrumentation-fetch'
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'

export function TracingProvider({ children }: { children: React.ReactNode }) {
  useEffect(() => {
    const provider = new WebTracerProvider({
      resource: new Resource({
        'service.name': 'nextjs-browser',
      }),
    })

    provider.addSpanProcessor(
      new BatchSpanProcessor(
        new OTLPTraceExporter({
          url: '/api/traces', // Proxy to avoid CORS
        })
      )
    )

    provider.register()

    // Auto-instrument fetch calls
    registerInstrumentations({
      instrumentations: [new FetchInstrumentor()],
    })
  }, [])

  return <>{children}</>
}

Vercel Integration

Use @vercel/otel for simplified setup on Vercel:

// instrumentation.ts
import { registerOTel } from '@vercel/otel'

export function register() {
  registerOTel({
    serviceName: 'nextjs-app',
  })
}

Vercel automatically exports to their observability platform.

Environment Variables

# Service identification
OTEL_SERVICE_NAME=nextjs-app
OTEL_SERVICE_VERSION=1.0.0

# Exporter endpoint
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318

# Google Cloud Trace
GOOGLE_CLOUD_PROJECT=your-project-id

# Sampling (optional)
OTEL_TRACES_SAMPLER=traceidratio
OTEL_TRACES_SAMPLER_ARG=0.1  # Sample 10%

Integration with Logging

Link traces and logs via trace_id:

import { trace, context } from '@opentelemetry/api'
import logger from '@/lib/logger'

const span = trace.getSpan(context.active())
const spanContext = span?.spanContext()

logger.info('Processing request', {
  trace_id: spanContext?.traceId,
  span_id: spanContext?.spanId,
})

Best Practices

  1. Set resource attributes: service.name, service.version, environment
  2. Use automatic instrumentation: Don't reinvent the wheel
  3. Create custom spans for business logic: Database queries, external APIs
  4. Add meaningful attributes: userid, orderid, etc.
  5. Record exceptions: span.recordException(error)
  6. Set span status: Indicate success or failure
  7. End spans: Always call span.end() (use try/finally)

Verification

  1. Start Next.js: npm run dev
  2. Make request: curl http://localhost:3000/api/test
  3. Check trace backend (Cloud Trace, Jaeger, etc.)
  4. Verify spans appear with correct hierarchy

Common Issues

Issue: Spans not appearing Solution: Check OTELEXPORTEROTLP_ENDPOINT is correct

Issue: instrumentation.ts not called Solution: Verify experimental.instrumentationHook: true in next.config.js

Issue: Trace disconnected across services Solution: Check context propagation (traceparent header)