Menu

OpenTelemetry

可观测性对于理解和优化 Next.js 应用的行为和性能至关重要。

随着应用变得越来越复杂,识别和诊断可能出现的问题变得越来越困难。通过利用可观测性工具,如日志记录和指标,开发者可以深入了解应用的行为并识别需要优化的地方。有了可观测性,开发者可以在问题变成大麻烦之前主动解决,并提供更好的用户体验。因此,强烈建议在你的 Next.js 应用中使用可观测性来提高性能、优化资源和增强用户体验。

我们推荐使用 OpenTelemetry 来检测你的应用。这是一种平台无关的应用检测方式,允许你更换可观测性提供商而无需更改代码。阅读 OpenTelemetry 官方文档 以了解更多关于 OpenTelemetry 及其工作原理的信息。

本文档使用了诸如 SpanTraceExporter 等术语,这些都可以在 OpenTelemetry 可观测性入门 中找到。

Next.js 原生支持 OpenTelemetry 检测,这意味着我们已经对 Next.js 本身进行了检测。当你启用 OpenTelemetry 时,我们会自动用带有有用属性的 spans 包装你的所有代码,如 getStaticProps

入门

OpenTelemetry 是可扩展的,但正确设置可能会相当冗长。这就是为什么我们准备了一个包 @vercel/otel,帮助你快速入门。

使用 @vercel/otel

首先,安装以下包:

Terminal
npm install @vercel/otel @opentelemetry/sdk-logs @opentelemetry/api-logs @opentelemetry/instrumentation

接下来,在项目的根目录(或者如果使用 src 文件夹则在其中)创建一个自定义的 instrumentation.ts (或 .js) 文件:

your-project/instrumentation.ts
import { registerOTel } from "@vercel/otel";
 
export function register() {
  registerOTel({ serviceName: "next-app" });
}
your-project/instrumentation.js
import { registerOTel } from "@vercel/otel";
 
export function register() {
  registerOTel({ serviceName: "next-app" });
}

查看 @vercel/otel 文档 了解更多配置选项。

值得注意的是

  • instrumentation 文件应该在你项目的根目录中,而不是在 apppages 目录内。如果你使用 src 文件夹,那么将文件放在 src 中,与 pagesapp 并列。
  • 如果你使用 pageExtensions 配置选项 添加后缀,你还需要更新 instrumentation 文件名以匹配。
  • 我们创建了一个基本的 with-opentelemetry 示例供你使用。

手动 OpenTelemetry 配置

@vercel/otel 包提供了许多配置选项,应该能满足大多数常见用例。但如果它不符合你的需求,你可以手动配置 OpenTelemetry。

首先,你需要安装 OpenTelemetry 包:

Terminal
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 与边缘运行时不兼容,所以你需要确保只在 process.env.NEXT_RUNTIME === 'nodejs' 时导入它们。我们建议创建一个新文件 instrumentation.node.ts,只在使用 node 时有条件地导入:

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

这样做相当于使用 @vercel/otel,但可以修改和扩展一些 @vercel/otel 没有暴露的功能。如果需要边缘运行时支持,你将不得不使用 @vercel/otel

测试你的检测

你需要一个带有兼容后端的 OpenTelemetry 收集器来在本地测试 OpenTelemetry 跟踪。 我们推荐使用我们的 OpenTelemetry 开发环境

如果一切正常,你应该能够看到标记为 GET /requested/pathname 的根服务器 span。 该特定跟踪的所有其他 span 将嵌套在其下。

Next.js 跟踪的 span 比默认发出的要多。 要查看更多 span,你必须设置 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 配置

自定义 Span

你可以使用 OpenTelemetry API 添加自定义 span。

Terminal
npm install @opentelemetry/api

以下示例演示了一个获取 GitHub 星星数的函数,并添加了一个自定义的 fetchGithubStars span 来跟踪获取请求的结果:

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 函数将在你的代码在新环境中运行之前执行。 你可以开始创建新的 span,它们应该被正确地添加到导出的跟踪中。

Next.js 中的默认 Span

Next.js 自动为你检测了几个 span,以提供有关应用性能的有用见解。

span 上的属性遵循 OpenTelemetry 语义约定。我们还在 next 命名空间下添加了一些自定义属性:

  • next.span_name - 复制 span 名称
  • next.span_type - 每个 span 类型都有一个唯一标识符
  • next.route - 请求的路由模式 (例如,/[param]/user)。
  • next.rsc (true/false) - 请求是否为 RSC 请求,如预取。
  • next.page
    • 这是应用路由器内部使用的值。
    • 你可以将其视为特殊文件的路由 (如 page.tslayout.tsloading.ts 等)
    • 它只有在与 next.route 配对时才能用作唯一标识符,因为 /layout 可以用来标识 /(groupA)/layout.ts/(groupB)/layout.ts

[http.method] [next.route]

  • next.span_type: BaseServer.handleRequest

这个 span 代表了你的 Next.js 应用中每个传入请求的根 span。它跟踪请求的 HTTP 方法、路由、目标和状态码。

属性:

render route (app) [next.route]

  • next.span_type: AppRender.getBodyResult

这个 span 表示在应用路由器中渲染路由的过程。

属性:

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

fetch [http.method] [http.url]

  • next.span_type: AppRender.fetch

这个 span 表示在你的代码中执行的获取请求。

属性:

可以通过在你的环境中设置 NEXT_OTEL_FETCH_DISABLED=1 来关闭这个 span。这在你想使用自定义的 fetch 检测库时很有用。

executing api route (app) [next.route]

  • next.span_type: AppRouteRouteHandlers.runHandler

这个 span 表示在应用路由器中执行 API 路由处理程序。

属性:

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

getServerSideProps [next.route]

  • next.span_type: Render.getServerSideProps

这个 span 表示为特定路由执行 getServerSideProps

属性:

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

getStaticProps [next.route]

  • next.span_type: Render.getStaticProps

这个 span 表示为特定路由执行 getStaticProps

属性:

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

render route (pages) [next.route]

  • next.span_type: Render.renderDocument

这个 span 表示为特定路由渲染文档的过程。

属性:

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

generateMetadata [next.page]

  • next.span_type: ResolveMetadata.generateMetadata

这个 span 表示为特定页面生成元数据的过程(一个路由可能有多个这样的 span)。

属性:

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

resolve page components

  • next.span_type: NextNodeServer.findPageComponents

这个 span 表示解析特定页面的页面组件的过程。

属性:

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

resolve segment modules

  • next.span_type: NextNodeServer.getLayoutOrPageModule

这个 span 表示为布局或页面加载代码模块。

属性:

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

start response

  • next.span_type: NextNodeServer.startResponse

这个零长度 span 表示响应中发送第一个字节的时间。