Инструментирование Next.js приложения с помощью OpenTelemetry
Наблюдаемость (Observability) крайне важна для понимания и оптимизации поведения и производительности вашего Next.js приложения.
По мере усложнения приложений становится все труднее выявлять и диагностировать возникающие проблемы. Используя инструменты наблюдаемости, такие как логирование и метрики, разработчики могут получить представление о поведении приложения и определить области для оптимизации. С наблюдаемостью разработчики могут заранее устранять проблемы до того, как они станут серьезными, и обеспечивать лучший пользовательский опыт. Поэтому настоятельно рекомендуется использовать наблюдаемость в ваших Next.js приложениях для повышения производительности, оптимизации ресурсов и улучшения пользовательского опыта.
Мы рекомендуем использовать OpenTelemetry для инструментирования ваших приложений. Это платформо-независимый способ инструментирования приложений, который позволяет вам менять провайдера наблюдаемости без изменения кода. Подробнее о OpenTelemetry и принципах его работы читайте в официальной документации OpenTelemetry.
В этой документации используются термины, такие как Span (Спан), Trace (Трейс) или Exporter (Экспортер), все они описаны в OpenTelemetry Observability Primer.
Next.js поддерживает инструментирование OpenTelemetry "из коробки", что означает, что мы уже инструментировали сам Next.js.
При включении OpenTelemetry мы автоматически оборачиваем весь ваш код, например
getStaticProps
, в спаны с полезными атрибутами.
Начало работы
OpenTelemetry расширяем, но его настройка может быть довольно многословной.
Поэтому мы подготовили пакет @vercel/otel
, который поможет вам быстро начать работу.
Использование @vercel/otel
Для начала установите следующие пакеты:
npm install @vercel/otel @opentelemetry/sdk-logs @opentelemetry/api-logs @opentelemetry/instrumentation
Затем создайте файл instrumentation.ts
(или .js
) в корневой директории проекта (или внутри папки src
, если она используется):
import { registerOTel } from '@vercel/otel'
export function register() {
registerOTel({ serviceName: 'next-app' })
}
import { registerOTel } from '@vercel/otel'
export function register() {
registerOTel({ serviceName: 'next-app' })
}
Дополнительные параметры конфигурации смотрите в документации @vercel/otel
.
Полезно знать:
- Файл
instrumentation
должен находиться в корне вашего проекта, а не внутри папокapp
илиpages
. Если вы используете папкуsrc
, поместите файл внутрьsrc
рядом сpages
иapp
.- Если вы используете опцию конфигурации
pageExtensions
для добавления суффикса, вам также потребуется обновить имя файлаinstrumentation
, чтобы оно соответствовало.- Мы создали базовый пример with-opentelemetry, который вы можете использовать.
Ручная настройка OpenTelemetry
Пакет @vercel/otel
предоставляет множество параметров конфигурации и должен покрывать большинство распространенных случаев использования. Но если он не подходит для ваших нужд, вы можете настроить OpenTelemetry вручную.
Сначала вам нужно установить пакеты OpenTelemetry:
npm install @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/semantic-conventions @opentelemetry/sdk-trace-node @opentelemetry/exporter-trace-otlp-http
Теперь вы можете инициализировать NodeSDK
в вашем instrumentation.ts
.
В отличие от @vercel/otel
, NodeSDK
не совместим с edge-рантаймом, поэтому вам нужно убедиться, что вы импортируете их только когда process.env.NEXT_RUNTIME === 'nodejs'
. Мы рекомендуем создать новый файл instrumentation.node.ts
, который вы условно импортируете только при использовании 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 { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { Resource } 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: new Resource({
[ATTR_SERVICE_NAME]: 'next-app',
}),
spanProcessor: new SimpleSpanProcessor(new OTLPTraceExporter()),
})
sdk.start()
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { Resource } 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: new Resource({
[ATTR_SERVICE_NAME]: 'next-app',
}),
spanProcessor: new SimpleSpanProcessor(new OTLPTraceExporter()),
})
sdk.start()
Это эквивалентно использованию @vercel/otel
, но позволяет модифицировать и расширять некоторые функции, которые не предоставляются @vercel/otel
. Если необходима поддержка edge-рантайма, вам придется использовать @vercel/otel
.
Тестирование инструментирования
Для тестирования трейсов OpenTelemetry локально вам понадобится OpenTelemetry collector с совместимым бэкендом. Мы рекомендуем использовать нашу OpenTelemetry dev environment.
Если все работает правильно, вы должны увидеть корневой серверный спан с меткой GET /requested/pathname
.
Все остальные спаны из этого конкретного трейса будут вложены под ним.
Next.js трейсит больше спанов, чем экспортируется по умолчанию.
Чтобы увидеть больше спанов, вы должны установить NEXT_OTEL_VERBOSE=1
.
Развертывание
Использование OpenTelemetry Collector
При развертывании с OpenTelemetry Collector вы можете использовать @vercel/otel
.
Это будет работать как на Vercel, так и при самостоятельном хостинге.
Развертывание на Vercel
Мы позаботились о том, чтобы OpenTelemetry работал "из коробки" на Vercel.
Следуйте документации Vercel, чтобы подключить ваш проект к провайдеру наблюдаемости.
Самостоятельный хостинг
Развертывание на других платформах также просто. Вам нужно будет запустить свой собственный OpenTelemetry Collector для приема и обработки данных телеметрии из вашего Next.js приложения.
Для этого следуйте руководству по началу работы с OpenTelemetry Collector, которое проведет вас через настройку коллектора и его конфигурацию для приема данных из вашего Next.js приложения.
После запуска коллектора вы можете развернуть ваше Next.js приложение на выбранной платформе, следуя соответствующим руководствам по развертыванию.
Пользовательские экспортеры
OpenTelemetry Collector не является обязательным. Вы можете использовать пользовательский экспортер OpenTelemetry с @vercel/otel
или ручной настройкой OpenTelemetry.
Пользовательские спаны
Вы можете добавить пользовательский спан с помощью API OpenTelemetry.
npm install @opentelemetry/api
Следующий пример демонстрирует функцию, которая получает звезды GitHub и добавляет пользовательский спан fetchGithubStars
для отслеживания результата запроса:
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()
}
})
}
Функция register
будет выполнена перед запуском вашего кода в новой среде.
Вы можете начать создавать новые спаны, и они должны быть корректно добавлены в экспортируемый трейс.
Стандартные спаны в Next.js
Next.js автоматически инструментирует несколько спанов, чтобы предоставить полезную информацию о производительности вашего приложения.
Атрибуты спанов следуют семантическим соглашениям OpenTelemetry. Мы также добавляем некоторые пользовательские атрибуты в пространстве имен next
:
next.span_name
- дублирует имя спанаnext.span_type
- каждый тип спана имеет уникальный идентификаторnext.route
- Шаблон маршрута запроса (например,/[param]/user
).next.rsc
(true/false) - Является ли запрос RSC-запросом, например, предварительной загрузкой.next.page
- Это внутреннее значение, используемое маршрутизатором app.
- Вы можете думать об этом как о маршруте к специальному файлу (например,
page.ts
,layout.ts
,loading.ts
и другие) - Может использоваться как уникальный идентификатор только в сочетании с
next.route
, так как/layout
может использоваться для идентификации как/(groupA)/layout.ts
, так и/(groupB)/layout.ts
[http.method] [next.route]
next.span_type
:BaseServer.handleRequest
Этот спан представляет корневой спан для каждого входящего запроса к вашему Next.js приложению. Он отслеживает HTTP-метод, маршрут, цель и статус-код запроса.
Атрибуты:
- Общие HTTP-атрибуты
http.method
http.status_code
- HTTP-атрибуты сервера
http.route
http.target
next.span_name
next.span_type
next.route
render route (app) [next.route]
next.span_type
:AppRender.getBodyResult
.
Этот спан представляет процесс рендеринга маршрута в маршрутизаторе app.
Атрибуты:
next.span_name
next.span_type
next.route
fetch [http.method] [http.url]
next.span_type
:AppRender.fetch
.
Этот спан представляет запрос fetch, выполненный в вашем коде.
Атрибуты:
- Общие HTTP-атрибуты
http.method
- HTTP-атрибуты клиента
http.url
net.peer.name
net.peer.port
(только если указан)
next.span_name
next.span_type
Этот спан можно отключить, установив NEXT_OTEL_FETCH_DISABLED=1
в вашем окружении. Это полезно, когда вы хотите использовать пользовательскую библиотеку инструментирования fetch.
executing api route (app) [next.route]
next.span_type
:AppRouteRouteHandlers.runHandler
.
Этот спан представляет выполнение обработчика API Route в маршрутизаторе app.
Атрибуты:
next.span_name
next.span_type
next.route
getServerSideProps [next.route]
next.span_type
:Render.getServerSideProps
.
Этот спан представляет выполнение getServerSideProps
для конкретного маршрута.
Атрибуты:
next.span_name
next.span_type
next.route
getStaticProps [next.route]
next.span_type
:Render.getStaticProps
.
Этот спан представляет выполнение getStaticProps
для конкретного маршрута.
Атрибуты:
next.span_name
next.span_type
next.route
render route (pages) [next.route]
next.span_type
:Render.renderDocument
.
Этот спан представляет процесс рендеринга документа для конкретного маршрута.
Атрибуты:
next.span_name
next.span_type
next.route
generateMetadata [next.page]
next.span_type
:ResolveMetadata.generateMetadata
.
Этот спан представляет процесс генерации метаданных для конкретной страницы (один маршрут может иметь несколько таких спанов).
Атрибуты:
next.span_name
next.span_type
next.page
resolve page components
next.span_type
:NextNodeServer.findPageComponents
.
Этот спан представляет процесс разрешения компонентов страницы для конкретной страницы.
Атрибуты:
next.span_name
next.span_type
next.route
resolve segment modules
next.span_type
:NextNodeServer.getLayoutOrPageModule
.
Этот спан представляет загрузку модулей кода для layout или страницы.
Атрибуты:
next.span_name
next.span_type
next.segment
start response
next.span_type
:NextNodeServer.startResponse
.
Этот спан нулевой длительности представляет момент, когда был отправлен первый байт ответа.