OpenTelemetry
Полезно знать: Эта функция является экспериментальной, для её использования необходимо явно указать
experimental.instrumentationHook = true;в вашемnext.config.js.
Наблюдаемость (Observability) крайне важна для понимания и оптимизации поведения и производительности вашего Next.js приложения.
По мере усложнения приложений становится всё труднее выявлять и диагностировать возникающие проблемы. Используя инструменты наблюдаемости, такие как логирование и метрики, разработчики могут получать информацию о поведении приложения и находить области для оптимизации. С наблюдаемостью разработчики могут заранее устранять проблемы до того, как они станут серьёзными, и обеспечивать лучший пользовательский опыт. Поэтому настоятельно рекомендуется использовать наблюдаемость в ваших Next.js приложениях для улучшения производительности, оптимизации ресурсов и повышения качества пользовательского опыта.
Мы рекомендуем использовать OpenTelemetry для инструментирования ваших приложений. Это платформонезависимый способ инструментирования приложений, который позволяет менять провайдера наблюдаемости без изменения кода. Подробнее о OpenTelemetry и принципах его работы читайте в официальной документации OpenTelemetry.
В этой документации используются термины, такие как Span (Спан), Trace (Трейс) или Exporter (Экспортер), все они описаны в OpenTelemetry Observability Primer.
Next.js поддерживает инструментирование OpenTelemetry "из коробки", что означает, что мы уже инструментировали сам Next.js.
При включении OpenTelemetry мы автоматически оборачиваем весь ваш код, например getStaticProps, в спаны с полезными атрибутами.
Полезно знать: В настоящее время мы поддерживаем привязки OpenTelemetry только в серверных функциях (serverless). Мы не предоставляем их для кода на
edgeили клиентского кода.
Начало работы
OpenTelemetry расширяем, но его настройка может быть довольно многословной.
Поэтому мы подготовили пакет @vercel/otel, который помогает быстро начать работу.
Он не расширяем, и если вам нужно настроить OpenTelemetry вручную, вам следует сделать это вручную.
Использование @vercel/otel
Для начала необходимо установить @vercel/otel:
npm install @vercel/otelЗатем создайте файл instrumentation.ts (или .js) в корневой директории проекта (или внутри папки src, если она используется):
import { registerOTel } from '@vercel/otel'
export function register() {
registerOTel('next-app')
}import { registerOTel } from '@vercel/otel'
export function register() {
registerOTel('next-app')
}Полезно знать
- Файл
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.
API OpenTelemetry не совместимы с 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 { NodeSDK } from '@opentelemetry/sdk-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { Resource } from '@opentelemetry/resources'
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node'
const sdk = new NodeSDK({
resource: new Resource({
[SemanticResourceAttributes.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 { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node'
const sdk = new NodeSDK({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'next-app',
}),
spanProcessor: new SimpleSpanProcessor(new OTLPTraceExporter()),
})
sdk.start()Это эквивалентно использованию @vercel/otel, но с возможностью модификации и расширения.
Например, вы можете использовать @opentelemetry/exporter-trace-otlp-grpc вместо @opentelemetry/exporter-trace-otlp-http или указать дополнительные атрибуты ресурсов.
Тестирование инструментирования
Для тестирования трейсов OpenTelemetry локально вам понадобится OpenTelemetry collector с совместимым бэкендом. Мы рекомендуем использовать нашу среду разработки OpenTelemetry.
Если всё работает правильно, вы должны увидеть корневой серверный спан с меткой 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 с ручной настройкой 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.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.methodhttp.status_code
- Серверные HTTP-атрибуты
http.routehttp.target
next.span_namenext.span_typenext.route
render route (app) [next.route]
next.span_type:AppRender.getBodyResult.
Этот спан представляет процесс рендеринга маршрута в маршрутизаторе app.
Атрибуты:
next.span_namenext.span_typenext.route
fetch [http.method] [http.url]
next.span_type:AppRender.fetch
Этот спан представляет fetch-запрос, выполненный в вашем коде.
Атрибуты:
- Общие HTTP-атрибуты
http.method
- Клиентские HTTP-атрибуты
http.urlnet.peer.namenet.peer.port(только если указан)
next.span_namenext.span_type
executing api route (app) [next.route]
next.span_type:AppRouteRouteHandlers.runHandler.
Этот спан представляет выполнение обработчика API-маршрута в маршрутизаторе app.
Атрибуты:
next.span_namenext.span_typenext.route
getServerSideProps [next.route]
next.span_type:Render.getServerSideProps.
Этот спан представляет выполнение getServerSideProps для конкретного маршрута.
Атрибуты:
next.span_namenext.span_typenext.route
getStaticProps [next.route]
next.span_type:Render.getStaticProps.
Этот спан представляет выполнение getStaticProps для конкретного маршрута.
Атрибуты:
next.span_namenext.span_typenext.route
render route (pages) [next.route]
next.span_type:Render.renderDocument.
Этот спан представляет процесс рендеринга документа для конкретного маршрута.
Атрибуты:
next.span_namenext.span_typenext.route
generateMetadata [next.page]
next.span_type:ResolveMetadata.generateMetadata.
Этот спан представляет процесс генерации метаданных для конкретной страницы (один маршрут может иметь несколько таких спанов).
Атрибуты:
next.span_namenext.span_typenext.page