OpenTelemetry

Es bueno saberlo: Esta característica es experimental, debes activarla explícitamente proporcionando experimental.instrumentationHook = true; en tu next.config.js.

La observabilidad es crucial para entender y optimizar el comportamiento y rendimiento de tu aplicación Next.js.

A medida que las aplicaciones se vuelven más complejas, resulta cada vez más difícil identificar y diagnosticar problemas que puedan surgir. Al aprovechar herramientas de observabilidad, como registros y métricas, los desarrolladores pueden obtener información sobre el comportamiento de su aplicación e identificar áreas para optimización. Con la observabilidad, los desarrolladores pueden abordar problemas proactivamente antes de que se conviertan en problemas mayores y proporcionar una mejor experiencia de usuario. Por lo tanto, se recomienda encarecidamente usar observabilidad en tus aplicaciones Next.js para mejorar el rendimiento, optimizar recursos y mejorar la experiencia de usuario.

Recomendamos usar OpenTelemetry para instrumentar tus aplicaciones. Es una forma agnóstica de plataforma para instrumentar aplicaciones que te permite cambiar tu proveedor de observabilidad sin modificar tu código. Lee la documentación oficial de OpenTelemetry para más información sobre OpenTelemetry y cómo funciona.

Esta documentación utiliza términos como Span (Intervalo), Trace (Traza) o Exporter (Exportador) a lo largo del documento, todos los cuales puedes encontrar en el manual de observabilidad de OpenTelemetry.

Next.js soporta instrumentación con OpenTelemetry de forma nativa, lo que significa que ya hemos instrumentado Next.js mismo. Cuando activas OpenTelemetry, automáticamente envolvemos todo tu código como getStaticProps en spans con atributos útiles.

Primeros pasos

OpenTelemetry es extensible pero configurarlo correctamente puede ser bastante verboso. Por eso preparamos un paquete @vercel/otel que te ayuda a comenzar rápidamente.

Usando @vercel/otel

Para comenzar, debes instalar @vercel/otel:

Terminal
npm install @vercel/otel

A continuación, crea un archivo personalizado instrumentation.ts (o .js) en el directorio raíz del proyecto (o dentro de la carpeta src si usas una):

import { registerOTel } from '@vercel/otel'

export function register() {
  registerOTel({ serviceName: 'next-app' })
}
import { registerOTel } from '@vercel/otel'

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

Consulta la documentación de @vercel/otel para opciones de configuración adicionales.

Es bueno saberlo

  • El archivo instrumentation debe estar en la raíz de tu proyecto y no dentro de los directorios app o pages. Si usas la carpeta src, coloca el archivo dentro de src junto a pages y app.
  • Si usas la opción de configuración pageExtensions para añadir un sufijo, también necesitarás actualizar el nombre del archivo instrumentation para que coincida.
  • Hemos creado un ejemplo básico with-opentelemetry que puedes usar.

Configuración manual de OpenTelemetry

El paquete @vercel/otel proporciona muchas opciones de configuración y debería cubrir la mayoría de los casos de uso comunes. Pero si no se ajusta a tus necesidades, puedes configurar OpenTelemetry manualmente.

Primero necesitas instalar los paquetes de OpenTelemetry:

Terminal
npm install @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/semantic-conventions @opentelemetry/sdk-trace-node @opentelemetry/exporter-trace-otlp-http

Ahora puedes inicializar NodeSDK en tu instrumentation.ts. A diferencia de @vercel/otel, NodeSDK no es compatible con el entorno de ejecución edge, por lo que debes asegurarte de que solo los importas cuando process.env.NEXT_RUNTIME === 'nodejs'. Recomendamos crear un nuevo archivo instrumentation.node.ts que importes condicionalmente solo cuando uses node:

export async function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    await import('./instrumentation.node.ts')
  }
}
export async function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    await import('./instrumentation.node.js')
  }
}
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'

const sdk = new NodeSDK({
  resource: new Resource({
    [SEMRESATTRS_SERVICE_NAME]: 'next-app',
  }),
  spanProcessor: new SimpleSpanProcessor(new OTLPTraceExporter()),
})
sdk.start()
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'

const sdk = new NodeSDK({
  resource: new Resource({
    [SEMRESATTRS_SERVICE_NAME]: 'next-app',
  }),
  spanProcessor: new SimpleSpanProcessor(new OTLPTraceExporter()),
})
sdk.start()

Hacer esto es equivalente a usar @vercel/otel, pero es posible modificar y extender algunas características que no están expuestas por @vercel/otel. Si necesitas soporte para el entorno de ejecución edge, tendrás que usar @vercel/otel.

Probando tu instrumentación

Necesitas un recolector OpenTelemetry con un backend compatible para probar las trazas de OpenTelemetry localmente. Recomendamos usar nuestro entorno de desarrollo OpenTelemetry.

Si todo funciona correctamente, deberías poder ver el span raíz del servidor etiquetado como GET /ruta/solicitada. Todos los demás spans de esa traza particular estarán anidados debajo de él.

Next.js rastrea más spans de los que se emiten por defecto. Para ver más spans, debes establecer NEXT_OTEL_VERBOSE=1.

Despliegue

Usando OpenTelemetry Collector

Cuando te despliegas con OpenTelemetry Collector, puedes usar @vercel/otel. Funcionará tanto en Vercel como en autoalojamiento.

Desplegando en Vercel

Nos aseguramos de que OpenTelemetry funcione de forma nativa en Vercel.

Sigue la documentación de Vercel para conectar tu proyecto a un proveedor de observabilidad.

Autoalojamiento

Desplegar en otras plataformas también es sencillo. Necesitarás iniciar tu propio OpenTelemetry Collector para recibir y procesar los datos de telemetría de tu aplicación Next.js.

Para hacer esto, sigue la guía de inicio de OpenTelemetry Collector, que te guiará a través de la configuración del recolector para recibir datos de tu aplicación Next.js.

Una vez que tengas tu recolector funcionando, puedes desplegar tu aplicación Next.js en la plataforma de tu elección siguiendo sus respectivas guías de despliegue.

Exportadores personalizados

OpenTelemetry Collector no es necesario. Puedes usar un exportador OpenTelemetry personalizado con @vercel/otel o configuración manual de OpenTelemetry.

Spans personalizados

Puedes añadir un span personalizado con las APIs de OpenTelemetry.

Terminal
npm install @opentelemetry/api

El siguiente ejemplo demuestra una función que obtiene estrellas de GitHub y añade un span personalizado fetchGithubStars para rastrear el resultado de la solicitud:

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()
      }
    })
}

La función register se ejecutará antes de que tu código se ejecute en un nuevo entorno. Puedes comenzar a crear nuevos spans, y deberían añadirse correctamente a la traza exportada.

Spans predeterminados en Next.js

Next.js instrumenta automáticamente varios spans para proporcionarte información útil sobre el rendimiento de tu aplicación.

Los atributos en los spans siguen las convenciones semánticas de OpenTelemetry. También añadimos algunos atributos personalizados bajo el espacio de nombres next:

  • next.span_name - duplica el nombre del span
  • next.span_type - cada tipo de span tiene un identificador único
  • next.route - El patrón de ruta de la solicitud (ej. /[param]/user).
  • next.rsc (verdadero/falso) - Indica si la solicitud es una solicitud RSC, como prefetch.
  • next.page
    • Este es un valor interno usado por el enrutador de app.
    • Puedes pensar en él como una ruta a un archivo especial (como page.ts, layout.ts, loading.ts y otros)
    • Puede usarse como identificador único solo cuando se combina con next.route porque /layout puede usarse para identificar tanto /(groupA)/layout.ts como /(groupB)/layout.ts

[http.method] [next.route]

  • next.span_type: BaseServer.handleRequest

Este span representa el span raíz para cada solicitud entrante a tu aplicación Next.js. Rastrea el método HTTP, ruta, objetivo y código de estado de la solicitud.

Atributos:

render route (app) [next.route]

  • next.span_type: AppRender.getBodyResult.

Este span representa el proceso de renderizar una ruta en el enrutador de app.

Atributos:

  • next.span_name
  • next.span_type
  • next.route

fetch [http.method] [http.url]

  • next.span_type: AppRender.fetch.

Este span representa la solicitud fetch ejecutada en tu código.

Atributos:

Este span puede desactivarse estableciendo NEXT_OTEL_FETCH_DISABLED=1 en tu entorno. Esto es útil cuando quieres usar una biblioteca de instrumentación fetch personalizada.

executing api route (app) [next.route]

  • next.span_type: AppRouteRouteHandlers.runHandler.

Este span representa la ejecución de un manejador de ruta API en el enrutador de app.

Atributos:

  • next.span_name
  • next.span_type
  • next.route

getServerSideProps [next.route]

  • next.span_type: Render.getServerSideProps.

Este span representa la ejecución de getServerSideProps para una ruta específica.

Atributos:

  • next.span_name
  • next.span_type
  • next.route

getStaticProps [next.route]

  • next.span_type: Render.getStaticProps.

Este span representa la ejecución de getStaticProps para una ruta específica.

Atributos:

  • next.span_name
  • next.span_type
  • next.route

render route (pages) [next.route]

  • next.span_type: Render.renderDocument.

Este span representa el proceso de renderizar el documento para una ruta específica.

Atributos:

  • next.span_name
  • next.span_type
  • next.route

generateMetadata [next.page]

  • next.span_type: ResolveMetadata.generateMetadata.

Este span representa el proceso de generar metadatos para una página específica (una sola ruta puede tener varios de estos spans).

Atributos:

  • next.span_name
  • next.span_type
  • next.page

resolve page components

  • next.span_type: NextNodeServer.findPageComponents.

Este span representa el proceso de resolver componentes de página para una página específica.

Atributos:

  • next.span_name
  • next.span_type
  • next.route

resolve segment modules

  • next.span_type: NextNodeServer.getLayoutOrPageModule.

Este span representa la carga de módulos de código para un layout o una página.

Atributos:

  • next.span_name
  • next.span_type
  • next.segment

start response

  • next.span_type: NextNodeServer.startResponse.

Este span de duración cero representa el momento en que se envía el primer byte en la respuesta.