Skip to main content

OpenTelemetry

您需要知道: 此功能是实验性的,您需要通过在next.config.js中提供experimental.instrumentationHook = true;来明确选择加入。

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

随着应用程序变得越来越复杂,识别和诊断可能出现的问题变得越来越困难。通过利用可观察性工具(如日志记录和指标),开发人员可以深入了解其应用程序的行为并识别优化的领域。通过可观察性,开发人员可以主动解决问题,防止其成为重大问题,并提供更好的用户体验。因此,强烈建议在您的 Next.js 应用程序中使用可观察性,以提高性能、优化资源并增强用户体验。

我们建议使用 OpenTelemetry 来检测您的应用程序。 这是一种与平台无关的方式来检测应用,允许你在不改变代码的情况下改变你的可观察性提供者。 阅读官方 OpenTelemetry 文档以了解有关 OpenTelemetry 及其工作原理的更多信息。

本文档使用了诸如 SpanTraceExporter 等术语,所有这些术语都可以在the OpenTelemetry Observability Primer中找到。

Next.js 开箱即支持 OpenTelemetry 检测,这意味着我们已经对 Next.js 本身进行了检测。 当您启用 OpenTelemetry 时,我们会自动将所有代码(如getStaticProps)包装在带有有用属性的 spans 中。

开始

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文件应位于项目的根目录中,而不是在apppages 目录中。如果您使用src文件夹, 那么请将文件放在与pagesapp同级的src文件夹中。
  • 如果您使用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

现在,您可以在您的instrumentation.ts文件中初始化NodeSDK。 与 @vercel/otel不同, NodeSDK不兼容 Edge 运行时,因此您需要确保仅在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 { 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();

这样做相当于使用@vercel/otel, ,但可以修改和扩展一些 @vercel/otel未公开的功能。如果需要 Edge 运行时支持,您将不得不使用@vercel/otel.

测试您的检测

您需要一个兼容后端的 OpenTelemetry 收集器来本地测试 OpenTelemetry traces。 我们建议使用我们的OpenTelemetry 开发环境

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

Next.js 追踪的 spans 比默认情况下发出的更多。 要查看更多 spans,您必须设置NEXT_OTEL_VERBOSE=1

部署

使用 OpenTelemetry Collector

当您使用 OpenTelemetry Collector 部署时,您可以使用@vercel/otel。 它在 Vercel 和自托管时都能正常工作。

在 Vercel 上部署

我们确保 OpenTelemetry 在 Vercel 上开箱即用。

请参阅 Vercel 文档以将您的项目连接到可观察性提供商。

自托管

部署到其他平台也很简单。您需要启动自己的 OpenTelemetry 收集器来接收和处理来自您的 Next.js 应用的遥测数据。

要做到这一点,请按照OpenTelemetry Collector 入门指南 操作,该指南将引导您设置收集器并配置它以接收来自您的 Next.js 应用的数据。

一旦您的收集器启动并运行,您可以按照各自的平台部署指南将您的 Next.js 应用部署到您选择的平台。

自定义导出器

OpenTelemetry Collector 不是必需的。您可以使用@vercel/otel手动配置 OpenTelemetry 来使用自定义的 OpenTelemetry 导出器。

自定义 Spans

您可以使用OpenTelemetry APIs添加自定义 span。

npm install @opentelemetry/api

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

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

Next.js 中默认的 Spans

Next.js 会自动为您检测服务器 spans,以提供有关应用性能的有用见解。

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

  • next.span_name - duplicates span name
  • next.span_type - each span type has a unique identifier
  • next.route - The route pattern of the request (e.g., /[param]/user).
  • next.rsc (true/false) - Whether the request is an RSC request, such as prefetch.
  • next.page
    • This is an internal value used by an app router.
    • You can think about it as a route to a special file (like page.ts, layout.ts, loading.ts and others)
    • It can be used as a unique identifier only when paired with next.route because /layout can be used to identify both /(groupA)/layout.ts and /(groupB)/layout.ts

[http.method] [next.route]

  • next.span_type: BaseServer.handleRequest

This span represents the root span for each incoming request to your Next.js application. It tracks the HTTP method, route, target, and status code of the request.

Attributes:

render route (app) [next.route]

  • next.span_type: AppRender.getBodyResult.

This span represents the process of rendering a route in the app router.

Attributes:

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

fetch [http.method] [http.url]

  • next.span_type: AppRender.fetch

This span represents the fetch request executed in your code.

Attributes:

This span can be turned off by setting NEXT_OTEL_FETCH_DISABLED=1 in your environment. This is useful when you want to use a custom fetch instrumentation library.

executing api route (app) [next.route]

  • next.span_type: AppRouteRouteHandlers.runHandler.

This span represents the execution of an API route handler in the app router.

Attributes:

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

getServerSideProps [next.route]

  • next.span_type: Render.getServerSideProps.

This span represents the execution of getServerSideProps for a specific route.

Attributes:

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

getStaticProps [next.route]

  • next.span_type: Render.getStaticProps.

This span represents the execution of getStaticProps for a specific route.

Attributes:

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

render route (pages) [next.route]

  • next.span_type: Render.renderDocument.

This span represents the process of rendering the document for a specific route.

Attributes:

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

generateMetadata [next.page]

  • next.span_type: ResolveMetadata.generateMetadata.

This span represents the process of generating metadata for a specific page (a single route can have multiple of these spans).

Attributes:

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

resolve page components

  • next.span_type: NextNodeServer.findPageComponents.

This span represents the process of resolving page components for a specific page.

Attributes:

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

resolve segment modules

  • next.span_type: NextNodeServer.getLayoutOrPageModule.

This span represents loading of code modules for a layout or a page.

Attributes:

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

start response

  • next.span_type: NextNodeServer.startResponse.

This zero-length span represents the time when the first byte has been sent in the response.