Capítulo 64 de 456
How to instrument a Next.js app with OpenTelemetry for observability (tracing), either via the @vercel/otel helper package or manual SDK configuration, plus reference for the spans Next.js emits by default.
@vercel/otel: official helper package that wraps OpenTelemetry setup; recommended entry point, registered inside instrumentation.ts's register() function.instrumentation.ts/.js: special file at the project root (or inside src/) where OpenTelemetry (or other instrumentation) is registered; must match pageExtensions config if customized.NodeSDK configuration: alternative to @vercel/otel for advanced cases; not edge-runtime compatible, so it must be conditionally imported only when process.env.NEXT_RUNTIME === 'nodejs'.NEXT_OTEL_VERBOSE=1: env var that surfaces additional spans Next.js traces internally but doesn't emit by default.NEXT_OTEL_FETCH_DISABLED=1: env var to turn off the automatic fetch span, useful when using a custom fetch instrumentation library.@opentelemetry/api's trace.getTracer(name).startActiveSpan(...), callable from any code that runs after register().next.* custom span attributes: next.span_name, next.span_type, next.route, next.rsc, next.page — the framework's semantic-convention extensions.// instrumentation.ts — quick setup with @vercel/otel
import { registerOTel } from '@vercel/otel'
export function register() {
registerOTel({ serviceName: 'next-app' })
}
@vercel/otel.// instrumentation.ts — conditional import for manual Node-only SDK
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
await import('./instrumentation.node.ts')
}
}
// instrumentation.node.ts — manual NodeSDK configuration
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { resourceFromAttributes } from '@opentelemetry/resources'
import { NodeSDK } from '@opentelemetry/sdk-node'
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node'
import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions'
const sdk = new NodeSDK({
resource: resourceFromAttributes({
[ATTR_SERVICE_NAME]: 'next-app',
}),
spanProcessor: new SimpleSpanProcessor(new OTLPTraceExporter()),
})
sdk.start()
NodeSDK não roda em edge runtime, por isso precisa de import condicional isolado num arquivo separado.// Custom span example
import { trace } from '@opentelemetry/api'
export async function fetchGithubStars() {
return await trace
.getTracer('nextjs-example')
.startActiveSpan('fetchGithubStars', async (span) => {
try {
return await getValue()
} finally {
span.end()
}
})
}
span.end() no finally.| Span name | next.span_type | Descrição |
|---|---|---|
[http.method] [next.route] | BaseServer.handleRequest | Span raiz de cada request recebido |
render route (app) [next.route] | AppRender.getBodyResult | Renderização de uma rota no app router |
fetch [http.method] [http.url] | AppRender.fetch | Requisição fetch executada no código (desativável com NEXT_OTEL_FETCH_DISABLED=1) |
executing api route (app) [next.route] | AppRouteRouteHandlers.runHandler | Execução de um Route Handler no app router |
getServerSideProps [next.route] | Render.getServerSideProps | Execução de getServerSideProps (pages router) |
getStaticProps [next.route] | Render.getStaticProps | Execução de getStaticProps (pages router) |
render route (pages) [next.route] | Render.renderDocument | Renderização do documento (pages router) |
generateMetadata [next.page] | ResolveMetadata.generateMetadata | Geração de metadata (pode ocorrer múltiplas vezes por rota) |
resolve page components | NextNodeServer.findPageComponents | Resolução de componentes de página |
resolve segment modules | NextNodeServer.getLayoutOrPageModule | Carregamento de módulos de layout/página |
start response | NextNodeServer.startResponse | Span de tamanho zero, marca o primeiro byte enviado |
instrumentation.ts dentro de app/ ou pages/: precisa estar na raiz do projeto (ou em src/ se usado).NodeSDK incondicionalmente: quebra em edge runtime; sempre isolar atrás de NEXT_RUNTIME === 'nodejs'.NodeSDK quando precisa de suporte a edge runtime: NodeSDK não é compatível; @vercel/otel é obrigatório nesse caso.instrumentation.ts.@vercel/otel cobre a maioria dos casos e funciona tanto na Vercel quanto self-hosted; configuração manual só é necessária para customizações não expostas pelo pacote.NEXT_OTEL_VERBOSE=1 revela spans adicionais não emitidos por padrão; útil para debug fino.@opentelemetry/api), sem necessidade de pacote proprietário da Vercel.instrumentation.ts é o mesmo arquivo usado para outros hooks de inicialização do servidor, não exclusivo de OpenTelemetry.