2022年9月5日

使用 OpenTelemetry 和 Prisma 通过追踪监控您的服务器

追踪是一个强大的工具,可以帮助您分析应用程序的性能并找出瓶颈。本教程将教您追踪的核心概念,以及如何使用 OpenTelemetryPrisma 的追踪功能 将追踪集成到您的应用程序中。

Monitor Your Server with Tracing Using OpenTelemetry & Prisma

目录

简介

在本教程中,您将学习如何将追踪集成到一个使用 Prisma 和 Express 构建的现有 Web 应用程序中。您将使用 OpenTelemetry 实现追踪,这是一个与供应商无关的收集追踪和其他遥测数据(例如日志、指标等)的标准。

最初,您将为 HTTP 端点创建手动追踪并将其打印到控制台。然后,您将学习如何使用 Jaeger 可视化您的追踪。您还将学习如何使用 Prisma 的追踪功能 自动为您的数据库查询生成追踪。最后,您将学习自动插桩和使用追踪时的性能考量。

什么是追踪?

追踪是一种可观测性工具,它记录请求在应用程序中传播时所经过的路径。追踪帮助您将系统为响应特定请求而执行的活动链接起来。追踪还提供有关这些活动的时间信息(例如开始时间、持续时间等)。

单个追踪为您提供有关用户或应用程序发出请求时发生的情况的信息。每个追踪由一个或多个span组成,其中包含有关请求期间发生的单个步骤或任务的信息。

使用像 Jaeger 这样的追踪工具,追踪可以可视化为如下所示的图表

Visualization of a single trace

单个 span 可以有多个子 span,它们表示父 span 期间发生的子任务。例如,在上图中,PRISMA QUERY span 有一个名为 PRISMA ENGINE 的子 span。最顶层的 span 称为根 span,表示从头到尾的整个追踪。在上图中,GET /ENDPOINT 是根 span。

追踪是深入了解和洞察您的系统的好方法。它让您能够精确识别影响应用程序的错误和性能瓶颈。追踪对于调试分布式系统特别有用,在分布式系统中,每个请求可能涉及多个服务,并且特定问题可能难以在本地重现。

注意: 追踪通常与 指标 结合使用,以更好地观测您的系统。要了解有关指标的更多信息,请查看我们的 指标教程

您将使用的技术

您将在本教程中使用以下工具

先决条件

假定知识

这是一个对初学者友好的教程。但是,本教程假设您

  • JavaScript 或 TypeScript 的基本知识(首选)
  • 具备后端 Web 开发的基本知识

注意:本教程假设您没有关于追踪和可观测性的先验知识。

开发环境

为了完成本教程,您需要

  • ... 已安装 Node.js
  • ...已安装 DockerDocker Compose
  • ... _可选_安装 Prisma VS Code 扩展。Prisma VS Code 扩展为 Prisma 添加了一些非常好的智能感知和语法高亮。
  • ... _可选_访问 Unix shell(例如 Linux 和 macOS 中的终端/shell)来运行本系列中提供的命令。

如果您没有 Unix shell(例如,您使用的是 Windows 计算机),您仍然可以继续学习,但 shell 命令可能需要根据您的计算机进行修改。

克隆存储库

您需要一个 Web 应用程序来演示追踪。您可以使用我们为本教程构建的现有 Express Web 应用程序。

要开始,请执行以下操作

  1. 克隆 仓库
  1. 导航到克隆的目录
  1. 安装依赖项
  1. 应用 prisma/migrations 目录中的数据库迁移

注意:此命令还将生成 Prisma Client 并为数据库填充数据。

  1. 启动项目

注意:在开发应用程序时,您应该保持服务器运行。dev 脚本应在代码发生任何更改时重新启动服务器。

应用程序只有一个端点:https://:4000/users/random。此端点将从数据库中返回 10 个用户的随机样本。通过访问上面的 URL 或运行以下命令来测试该端点

项目结构和文件

您克隆的仓库具有以下结构

此存储库中值得注意的文件和目录是

  • prisma
    • schema.prisma:定义数据库 schema。
    • migrations:包含数据库迁移历史记录。
    • seed.ts:包含一个用于使用虚拟数据为您的开发数据库填充数据的脚本。
    • dev.db:存储 SQLite 数据库的状态。
  • server.ts:带有 GET /users/random 端点的 Express 服务器。
  • tsconfig.jsonpackage.json:配置文件。

将追踪集成到您的应用程序中

您的 Express 应用程序已经实现了所有核心“业务逻辑”(即返回 10 个随机用户)。为了衡量性能并提高应用程序的可观测性,您将集成追踪。

在本节中,您将学习如何初始化追踪并手动创建追踪。

初始化追踪

您将使用 OpenTelemetry 追踪 实现追踪。OpenTelemetry 提供了一个开源实现,它兼容各种平台和语言。此外,它还附带用于实现追踪的库和 SDK。

通过安装以下 OpenTelemetry 软件包开始使用追踪

这些软件包包含 OpenTelemetry 追踪的 Node.js 实现。

现在,创建一个新的 tracing.ts 文件来初始化追踪

tracing.ts 中,按如下方式初始化追踪

initializeTracing 函数执行以下几项操作

  1. 它初始化一个 tracer provider,用于创建 tracer。tracer 在您的应用程序中创建追踪/span。
  2. 它定义一个 trace exporter 并将其添加到您的 provider 中。Trace exporter 将追踪发送到各种目的地。在这种情况下,ConsoleSpanExporter 将追踪打印到控制台。
  3. 它通过调用 .register() 函数将 provider 注册为与 OpenTelemetry API 一起使用。
  4. 最后,它创建一个 tracer 并返回,tracer 的名称作为参数传递给函数。

现在,在现有 server.ts 中导入并调用 initializeTracing

现在您已准备好创建您的第一个追踪!

创建您的第一个追踪

在上一节中,您初始化了追踪并将 tracer 导入到您的服务器。现在您可以使用 tracer 对象在您的服务器中创建 span。首先,您将创建一个封装 GET /users/random 请求的追踪。按如下方式更新请求处理程序定义

在这里,您使用 startActiveSpan() 创建一个新 span,并将所有请求处理程序逻辑封装在它提供的回调函数中。回调函数带有一个对 span 对象的引用,您已将其命名为 requestSpan。您可以使用它来修改或向 span 添加属性。在此代码中,您根据请求的结果将一个名为 http.status 的属性设置为 span。最后,一旦请求已处理,您就结束 span。

要查看您新创建的 span,请访问 https://:4000/users/random。或者,您可以在终端中运行以下命令

转到运行 Express 服务器的终端窗口。您应该会看到一个类似以下内容的对象打印到控制台

此对象表示您刚刚创建的 span。其中一些值得注意的属性是

  • id 表示此特定 span 的唯一标识符。
  • traceId 表示特定追踪的唯一标识符。某个追踪中的所有 span 都将具有相同的 traceId。目前,您的追踪仅由一个 span 组成。
  • parentId 是父 span 的 id。在这种情况下,它是 undefined,因为根 span 没有父 span。
  • name 表示 span 的名称。您在创建 span 时指定了它。
  • timestamp 是一个 UNIX 时间戳,表示 span 的创建时间。
  • duration 是 span 的持续时间(以微秒为单位)。

使用 Jaeger 可视化追踪

目前,您正在控制台中查看追踪。虽然这对于单个追踪来说是可管理的,但对于大量追踪来说并不十分有用。为了更好地理解您的追踪,您需要一个可以可视化追踪的追踪解决方案。在本教程中,您将为此目的使用 Jaeger

设置 Jaeger

您可以通过两种方式设置 Jaeger

在本教程中,您将使用 Docker Compose 运行 Jaeger 的 Docker 镜像。首先,创建一个新的 docker-compose.yml 文件

在该文件中定义以下服务

运行此镜像将设置并初始化 Docker 容器中 Jaeger 的所有必要组件。要运行 Jaeger,请打开一个新的终端窗口,并在项目的主文件夹中运行以下命令

注意:如果您关闭运行 docker 容器的终端窗口,它也会停止容器。如果您在命令末尾添加 -d 选项,则可以避免这种情况,例如:docker-compose up -d

如果一切顺利,您应该能够通过 https://:16686 访问 Jaeger。

Jaeger user interface

由于您的应用程序尚未将追踪发送到 Jaeger,因此 Jaeger UI 将为空。

添加 Jaeger 追踪导出器

要在 Jaeger 中查看您的追踪,您需要设置一个新的追踪导出器,它将把追踪从您的应用程序发送到 Jaeger(而不是仅仅将它们打印到控制台)。

首先,在您的项目中安装导出器软件包

现在将导出器添加到 tracing.ts

在这里,您初始化了一个新的 JaegerExporter 并将其添加到您的 tracer provider。 JaegerExporter 构造函数中的 endpoint 属性指向 Jaeger 监听追踪数据的位置。您还删除了不再需要的控制台导出器。

您现在应该能够在 Jaeger 中看到您的追踪。要查看您的第一个追踪

  1. 再次查询 GET /users/random 端点(curl https://:4000/users/random)。
  2. 前往 https://:16686
  3. 在左侧的 搜索 选项卡中,在 服务 下拉列表中,选择 express-server
  4. 搜索 选项卡底部附近,点击 查找追踪
  5. 您现在应该会看到一个追踪列表。点击列表中的第一个追踪。
  6. 您将看到追踪的详细视图。应该有一个名为 GET /users/random 的单个 span。点击该 span 以获取更多信息。
  7. 您应该能够看到有关追踪的各种信息,例如 持续时间开始时间。您还应该看到多个 标签,其中一个由您手动设置(http.status)。

Viewing traces inside Jaeger

为您的 Prisma 查询添加追踪

在本节中,您将学习如何追踪您的数据库查询。最初,您将通过自己创建 span 来手动完成此操作。尽管 Prisma 不再需要手动追踪,但实现手动追踪将让您更好地理解追踪的工作原理。

然后,您将使用 Prisma 中新的 追踪功能 自动执行相同的操作。

手动追踪您的 Prisma 查询

要手动追踪您的 Prisma 查询,您必须将每个查询包装在一个 span 中。您可以通过将以下代码添加到您的 server.ts 文件来完成此操作

您已经为 Prisma 查询创建了一个名为 prisma.user.findmany 的新 span。您还对 users 变量的声明方式进行了一些更改,以使其与您的其余代码保持一致。

通过再次查询 GET /users/random 端点(curl https://:4000/users/random)并在 Jaeger 中查看新生成的追踪来测试新 span。

Child span visualized in Jaeger

您应该会看到生成的追踪有一个新的子 span,名为 prisma.user.findmany,嵌套在父 GET /users/random span 下。现在您可以看到请求执行 Prisma 查询所花费的时间。

手动与自动插桩

到目前为止,您已经学习了如何设置追踪以及如何手动为您的应用程序生成追踪和 span。像这样手动定义 span 称为手动插桩。手动插桩使您可以完全控制应用程序的追踪方式,但它也有一定的缺点

  • 手动追踪您的应用程序非常耗时,特别是当您的应用程序很大时。
  • 并非总是能够手动正确地插桩第三方库。例如,无法使用手动插桩追踪 Prisma 内部组件的执行。
  • 它可能导致错误(例如不正确的错误处理、损坏的 span 等),因为它涉及手动编写大量代码。

幸运的是,许多框架和库提供自动插桩,允许您自动为这些组件生成追踪。自动插桩几乎不需要代码更改,设置非常快速,并且可以为您提供开箱即用的基本遥测。

重要的是要注意,自动和手动插桩不是相互排斥的。同时使用这两种技术可能是有益的。自动插桩可以提供良好的基线遥测,覆盖所有端点。然后可以添加手动插桩以实现特定的细粒度追踪和自定义指标/元数据。

为 Prisma 设置自动插桩

本节将教您如何使用新的追踪功能为 Prisma 设置自动插桩。首先,在 schema.prisma 文件的 generator 块中启用追踪功能标志

注意:追踪目前是一个 预览功能。这就是为什么您必须在使用追踪之前添加 tracing 功能标志。

现在,重新生成 Prisma Client

为了执行自动插桩,您还需要使用 npm 安装两个新软件包

需要这些包是因为

  • @opentelemetry/instrumentation 是设置自动插桩所必需的。
  • @prisma/instrumentation 为 Prisma Client 提供自动插桩。

根据 OpenTelemetry 术语,被插桩库 是您为其收集追踪的库或包。另一方面,插桩库 是为某个被插桩库生成追踪的库。在这种情况下,Prisma Client 是被插桩库,@prisma/instrumentation 是插桩库。

现在您需要向 OpenTelemetry 注册 Prisma 插桩。为此,请将以下代码添加到您的 tracing.ts 文件中

registerInstrumentations 调用接受两个参数

  • instrumentations 接受您要注册的所有插桩库的数组。
  • tracerProvider 接受您的 tracer 的 tracer provider。

由于您正在设置自动插桩,您不再需要手动为 Prisma 查询创建 span。通过删除 Prisma 查询的手动 span 来更新 server.ts

使用自动插桩时,初始化追踪的顺序很重要。您需要在导入被插桩库之前设置追踪并注册插桩。在这种情况下,initializeTracing 调用必须在 PrismaClientimport 语句之前。

再次向 GET /users/random 端点发出请求,并在 Jaeger 中查看生成的追踪。

Visualization of automatic instrumentation with Prisma

这次,相同的 Prisma 查询生成了多个 span,提供了关于查询的更细粒度信息。启用自动插桩后,您添加到应用程序的任何其他查询也将自动生成追踪。

注意: 要了解有关 Prisma 生成的 span 的更多信息,请参阅 追踪文档的追踪输出部分

为 Express 设置自动插桩

目前,您正在通过手动创建 span 来追踪您的端点。就像 Prisma 查询一样,随着端点数量的增长,手动追踪将变得难以管理。为了解决这个问题,您也可以为 Express 设置自动插桩。

首先安装以下插桩库

tracing.ts 中注册这两个新的插桩库

最后,在 server.ts 中删除 GET /users/random 端点的手动 span

GET /users/random 端点发出请求,并在 Jaeger 中查看生成的追踪。

Visualization of automatic instrumentation with Express and Prisma

您应该会看到更细粒度的 span,显示请求通过代码时不同的步骤。特别是,您应该会看到由 ExpressInstrumentation 库生成的新 span,这些 span 显示请求通过各种 Express 中间件和 GET /users/random 请求处理程序。

注意:有关可用插桩库的列表,请查看 OpenTelemetry 注册表

减少追踪的性能影响

如果您的应用程序向收集器(如 Jaeger)发送大量 span,它可能会对应用程序的性能产生显著影响。这在您的开发环境中通常不是问题,但在生产环境中可能会成为问题。您可以采取一些措施来缓解这种情况。

批量发送追踪

目前,您正在使用 SimpleSpanProcessor 发送追踪。这是低效的,因为它一次发送一个 span。您可以改用 BatchSpanProcessor 批量发送 span。

在您的 tracing.ts 文件中进行以下更改,以便在生产环境中使用 BatchSpanProcessor

请注意,您在开发环境中仍然使用 SimpleSpanProcessor,因为在此环境中优化性能并不是一个大问题。这确保了追踪在开发中生成后立即显示。

通过采样发送更少的 span

概率采样 是一种技术,允许 OpenTelemetry 追踪用户通过使用随机采样技术来降低 span 收集的性能成本。使用此技术,您可以减少发送到收集器的 span 数量,同时仍然能够很好地表示应用程序中发生的情况。

更新 tracing.ts 以使用概率采样

就像批量处理一样,您仅在生产环境中才加入概率采样。

总结和最后说明

恭喜!🎉

在本教程中,您学习了

  • 什么是追踪,以及为什么应该使用它。
  • 什么是 OpenTelemetry,以及它与追踪的关系。
  • 如何使用 Jaeger 可视化追踪。
  • 如何将追踪集成到现有 Web 应用程序中。
  • 如何使用自动插桩库来提高代码可观测性。
  • 如何减少追踪在生产环境中的性能影响。

您可以在 GitHub 上找到该项目的源代码。如果您发现问题,请随时在仓库中提出问题或提交 PR。您也可以直接在 Twitter 上联系我。

不要错过下一篇文章!

订阅 Prisma 新闻通讯

© . This site is unofficial and not affiliated with Prisma Data, Inc.