可观测性对于理解和优化 Next.js 应用的行为和性能至关重要。
随着应用程序变得越来越复杂,识别和诊断可能出现的问题变得越来越困难。通过利用日志记录和指标等可观测性工具,开发人员可以深入了解应用程序的行为,并找出可优化的区域。借助可观测性,开发人员可以在问题演变为重大问题之前主动解决它们,并提供更好的用户体验。因此,强烈建议在 Next.js 应用程序中使用可观测性,以提高性能、优化资源并增强用户体验。
我们建议使用 OpenTelemetry 来检测(instrumenting)您的应用程序。它是一种平台无关的应用程序检测方式,允许您在不更改代码的情况下更换可观测性提供商。阅读 Official OpenTelemetry docs 以获取有关 OpenTelemetry 及其工作原理的更多信息。
本文档中使用了 跨度(Span)、跟踪(Trace)或 导出器(Exporter)等术语,所有这些术语都可以在 the 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接下来,在项目根目录(如果使用 src 文件夹,则在其内部)中创建自定义的 instrumentation.ts(或 .js)文件:
接下来,在项目根目录(如果使用 src 文件夹,则在其内部)中创建自定义的 instrumentation.ts(或 .js)文件:
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 示例供您使用。
须知:
instrumentation文件应位于项目的根目录中,而不是app或pages目录中。如果您使用的是src文件夹,则将文件放在src中,与pages和app并列。- 如果您使用
pageExtensions配置选项 来添加后缀,您还需要更新instrumentation文件名以匹配。- 我们创建了一个基本的 with-opentelemetry 示例供您使用。
@vercel/otel 包提供了许多配置选项,应该能满足大多数常见用例。但如果它不符合您的需求,您可以手动配置 OpenTelemetry。
首先,您需要安装 OpenTelemetry 软件包:
npm install @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/semantic-conventions @opentelemetry/sdk-trace-node @opentelemetry/exporter-trace-otlp-http现在您可以在 instrumentation.ts 中初始化 NodeSDK。与 @vercel/otel 不同,NodeSDK 不兼容 edge runtime,因此您需要确保仅在 process.env.NEXT_RUNTIME === 'nodejs' 时才导入它们。我们建议创建一个新文件 instrumentation.node.ts,仅在使用 Node.js 时有条件地导入它:
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 { 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()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()这样做等同于使用 @vercel/otel,但可以修改和扩展 @vercel/otel 未暴露的一些功能。如果需要 edge runtime 支持,则必须使用 @vercel/otel。
您需要一个带有兼容后端的 OpenTelemetry 收集器才能在本地测试 OpenTelemetry 跟踪。我们建议使用我们的 OpenTelemetry dev environment。
如果一切正常,您应该能够看到标记为 GET /requested/pathname 的根服务器跨度。该特定跟踪中的所有其他跨度都将嵌套在其下。
Next.js 跟踪的跨度比默认发出的要多。要查看更多跨度,您必须设置 NEXT_OTEL_VERBOSE=1。
当您使用 OpenTelemetry 收集器进行部署时,可以使用 @vercel/otel。它在 Vercel 和自托管环境中都将工作。
我们确保 OpenTelemetry 在 Vercel 上开箱即用。
按照 Vercel documentation 将您的项目连接到可观测性提供商。
部署到其他平台也很简单。您需要启动自己的 OpenTelemetry 收集器来接收和处理来自 Next.js 应用程序的遥测数据。
为此,请按照 OpenTelemetry Collector Getting Started guide 进行操作,该指南将引导您设置收集器并将其配置为从 Next.js 应用程序接收数据。
一旦您的收集器启动并运行,您可以按照所选平台的部署指南将 Next.js 应用程序部署到该平台。
OpenTelemetry 收集器并非必需。您可以将自定义 OpenTelemetry 导出器与 @vercel/otel 或 手动 OpenTelemetry 配置 配合使用。
您可以使用 OpenTelemetry APIs 添加自定义跨度。
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 自动为您检测多个跨度,以提供有关应用程序性能的有用见解。
跨度上的属性遵循 OpenTelemetry semantic conventions。我们还在 next 命名空间下添加了一些自定义属性:
next.span_name - 复制跨度名称next.span_type - 每个跨度类型都有一个唯一的标识符next.route - 请求的路由模式(例如,/[param]/user)。next.rsc (true/false) - 请求是否为 RSC 请求,例如预取。next.page
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.methodhttp.status_codehttp.routehttp.targetnext.span_namenext.span_typenext.routerender route (app) [next.route]next.span_type: AppRender.getBodyResult。此跨度表示在应用程序路由器中渲染路由的过程。
属性:
next.span_namenext.span_typenext.routefetch [http.method] [http.url]next.span_type: AppRender.fetch此跨度表示在您的代码中执行的 fetch 请求。
属性:
http.methodhttp.urlnet.peer.namenet.peer.port (仅在指定时)next.span_namenext.span_type可以通过在您的环境中设置 NEXT_OTEL_FETCH_DISABLED=1 来关闭此跨度。当您希望使用自定义的 fetch 检测库时,这会很有用。
executing api route (app) [next.route]next.span_type: AppRouteRouteHandlers.runHandler。此跨度表示在应用程序路由器中执行 API 路由处理程序。
属性:
next.span_namenext.span_typenext.routegetServerSideProps [next.route]next.span_type: Render.getServerSideProps。此跨度表示特定路由的 getServerSideProps 的执行。
属性:
next.span_namenext.span_typenext.routegetStaticProps [next.route]next.span_type: Render.getStaticProps。此跨度表示特定路由的 getStaticProps 的执行。
属性:
next.span_namenext.span_typenext.routerender route (pages) [next.route]next.span_type: Render.renderDocument。此跨度表示特定路由的文档渲染过程。
属性:
next.span_namenext.span_typenext.routegenerateMetadata [next.page]next.span_type: ResolveMetadata.generateMetadata。此跨度表示为特定页面生成元数据的过程(单个路由可以有多个这样的跨度)。
属性:
next.span_namenext.span_typenext.pageresolve page componentsnext.span_type: NextNodeServer.findPageComponents。此跨度表示为特定页面解析页面组件的过程。
属性:
next.span_namenext.span_typenext.routeresolve segment modulesnext.span_type: NextNodeServer.getLayoutOrPageModule。此跨度表示加载布局或页面的代码模块。
属性:
next.span_namenext.span_typenext.segmentstart responsenext.span_type: NextNodeServer.startResponse。此零长度跨度表示响应中发送第一个字节的时间。