跳过到主要内容

OpenTelemetry 追踪

追踪提供了 Prisma 客户端执行的活动的详细日志,在操作级别,包括执行每个查询所需的时间。它可以帮助您分析应用程序的性能并识别瓶颈。追踪完全符合 OpenTelemetry,因此您可以将其用作端到端应用程序追踪系统的一部分。

info

追踪为您提供了对 Prisma ORM 项目的高度详细的操作级洞察。如果您想要聚合的数值报告,例如查询计数、连接计数和总查询执行时间,请参阅 指标

关于追踪

启用追踪时,Prisma 客户端会输出以下内容

  • Prisma 客户端执行的每个操作(例如 findMany)都会生成一个追踪。
  • 在每个追踪中,包含一个或多个 跨度。每个跨度表示操作的某个阶段所花费的时间长度,例如序列化或数据库查询。跨度以树形结构表示,其中子跨度表示执行发生在更大的父跨度内。

追踪中跨度的数量和类型取决于追踪涵盖的操作类型,但示例如下

image

您可以 将追踪输出发送到控制台,或在任何 OpenTelemetry 兼容的追踪系统(如 JaegerHoneycombDatadog)中对其进行分析。在本页上,我们给出了如何将追踪输出发送到 Jaeger 的示例,您可以 在本地运行它

追踪输出

对于每个追踪,Prisma 客户端都会输出一系列跨度。这些跨度的数量和类型取决于 Prisma 客户端操作。典型的 Prisma 追踪包含以下跨度

  • prisma:client:operation:表示整个 Prisma 客户端操作,从 Prisma 客户端到数据库,然后再返回。它包含详细信息,例如 Prisma 客户端调用的模型和方法。根据 Prisma 操作,它包含以下跨度之一或多个
    • prisma:client:connect:表示 Prisma 客户端连接到数据库所需的时间长度。
    • prisma:client:serialize:表示将 Prisma 客户端操作验证并转换为 查询引擎 的查询所需的时间长度。
    • prisma:engine:表示查询在查询引擎中执行所需的时间长度。
      • prisma:engine:connection:表示 Prisma 客户端获取数据库连接所需的时间长度。
      • prisma:engine:db_query:表示针对数据库执行的数据库查询。它在标签中包含查询以及查询运行所需的时间长度。
      • prisma:engine:serialize:表示将数据库查询结果转换为 Prisma 客户端结果所需的时间长度。

例如,给定以下 Prisma 客户端代码

prisma.user.findMany({
where: {
email: email,
},
include: {
posts: true,
},
})

追踪的结构如下

  • prisma:client:operation
    • prisma:client:serialize
    • prisma:engine
      • prisma:engine:connection
      • prisma:engine:db_query:第一个 SQL 查询或命令的详细信息...
      • prisma:engine:db_query:...下一个 SQL 查询或命令的详细信息...
      • prisma:engine:serialize

注意事项和先决条件

如果您的应用程序向 收集器 发送了大量的跨度,这可能会对性能产生重大影响。有关如何最大程度地减少这种影响的信息,请参阅 降低性能影响

要使用追踪,您必须执行以下操作

  1. 安装适当的依赖项.
  2. 在 Prisma 架构文件中启用 tracing 功能标志.
  3. 安装 OpenTelemetry 包.

开始在 Prisma ORM 中使用追踪

本节介绍如何在应用程序中安装和注册追踪。

步骤 1. 安装最新的 Prisma ORM 依赖项

使用版本 4.2.0 或更高版本的 prisma@prisma/client@prisma/instrumentation npm 包。

npm install prisma@latest --save-dev
npm install @prisma/client@latest --save
npm install @prisma/instrumentation@latest --save

步骤 2:在 Prisma 架构文件中启用功能标志

schema.prisma 文件的 generator 块中,启用 tracing 功能标志

generator client {
provider = "prisma-client-js"
previewFeatures = ["tracing"]
}

步骤 3:安装 OpenTelemetry 包

最后,安装适当的 OpenTelemetry 包,如下所示

npm install @opentelemetry/semantic-conventions @opentelemetry/exporter-trace-otlp-http @opentelemetry/instrumentation @opentelemetry/sdk-trace-base @opentelemetry/sdk-trace-node @opentelemetry/resources

在应用程序中注册追踪

以下代码提供了最小的追踪配置。您需要根据您的特定应用程序对该配置进行自定义。

setup.ts
// Imports
import { SEMRESATTRS_SERVICE_NAME, SEMRESATTRS_SERVICE_VERSION } from '@opentelemetry/semantic-conventions'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { registerInstrumentations } from '@opentelemetry/instrumentation'
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'
import { PrismaInstrumentation } from '@prisma/instrumentation'
import { Resource } from '@opentelemetry/resources'

// Configure the trace provider
const provider = new NodeTracerProvider({
resource: new Resource({
[SEMRESATTRS_SERVICE_NAME]: 'example application',
[SEMRESATTRS_SERVICE_VERSION]: '0.0.1',
}),
})

// Configure how spans are processed and exported. In this case we're sending spans
// as we receive them to an OTLP-compatible collector (e.g. Jaeger).
provider.addSpanProcessor(new SimpleSpanProcessor(new OTLPTraceExporter()))

// Register your auto-instrumentors
registerInstrumentations({
tracerProvider: provider,
instrumentations: [new PrismaInstrumentation()],
})

// Register the provider globally
provider.register()

OpenTelemetry 具有高度可配置性。您可以自定义资源属性、要检测的组件、跨度处理方式以及跨度发送位置。

您可以在 此示例应用程序 中找到包含指标的完整示例。

追踪操作说明

使用 Jaeger 可视化追踪

Jaeger 是一个免费的开源 OpenTelemetry 收集器和仪表板,您可以使用它来可视化您的追踪。

以下屏幕截图显示了一个追踪可视化示例

Jaeger UI

要在本地运行 Jaeger,请使用以下 Docker 命令

docker run --rm --name jaeger -d -e COLLECTOR_OTLP_ENABLED=true -p 16686:16686 -p 4318:4318 jaegertracing/all-in-one:latest

您现在将在 https://127.0.0.1:16686/ 处找到追踪仪表板。当您在启用追踪的情况下使用应用程序时,您将开始在该仪表板中看到追踪。

将追踪输出发送到控制台

以下示例使用 @opentelemetry/sdk-trace-base 中的 ConsoleSpanExporter 将输出追踪发送到控制台。

// Imports
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'
import { registerInstrumentations } from '@opentelemetry/instrumentation'
import {
BasicTracerProvider,
ConsoleSpanExporter,
SimpleSpanProcessor,
} from '@opentelemetry/sdk-trace-base'
import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'
import * as api from '@opentelemetry/api'
import { PrismaInstrumentation } from '@prisma/instrumentation'
import { Resource } from '@opentelemetry/resources'

// Export the tracing
export function otelSetup() {
const contextManager = new AsyncHooksContextManager().enable()

api.context.setGlobalContextManager(contextManager)

//Configure the console exporter
const consoleExporter = new ConsoleSpanExporter()

// Configure the trace provider
const provider = new BasicTracerProvider({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'test-tracing-service',
[SemanticResourceAttributes.SERVICE_VERSION]: '1.0.0',
}),
})

// Configure how spans are processed and exported. In this case we're sending spans
// as we receive them to the console
provider.addSpanProcessor(new SimpleSpanProcessor(consoleExporter))

// Register your auto-instrumentors
registerInstrumentations({
tracerProvider: provider,
instrumentations: [new PrismaInstrumentation()],
})

// Register the provider
provider.register()
}

追踪 Prisma Client 中间件

默认情况下,追踪不会为 Prisma Client 中间件 输出跨度。要将你的中间件包含在你的追踪中,请在你的 registerInstrumentations 语句中将 middleware 设置为 true,如下所示

registerInstrumentations({
instrumentations: [new PrismaInstrumentation({ middleware: true })],
})

这将在你的追踪中添加以下跨度类型

  • prisma:client:middleware:表示操作在你的 中间件 中花费的时间。

追踪交互式事务

当你执行交互式事务时,除了 标准跨度 之外,你还会看到以下跨度

  • prisma:client:transaction:一个 根跨度,它包装了 prisma 跨度。
    • prisma:engine:itx_runner:表示交互式事务在 查询引擎 中花费的时间。
    • prisma:engine:itx_query_builder:表示构建交互式事务所需的时间。

例如,请看以下 Prisma 模式

schema.prisma
generator client {
provider = "prisma-client-js"
previewFeatures = ["tracing", "interactiveTransactions"]
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model User {
id Int @id @default(autoincrement())
email String @unique
}

model Audit {
id Int @id
table String
action String
}

给定以下交互式事务

await prisma.$transaction(async (tx) => {
const user = await tx.user.create({
data: {
email: email,
},
})

await tx.audit.create({
data: {
table: 'user',
action: 'create',
id: user.id,
},
})

return user
})

追踪的结构如下

  • prisma:client:transaction
  • prisma:client:connect
  • prisma:engine:itx_runner
    • prisma:engine:connection
    • prisma:engine:db_query
    • prisma:engine:itx_query_builder
      • prisma:engine:db_query
      • prisma:engine:db_query
      • prisma:engine:serialize
    • prisma:engine:itx_query_builder
      • prisma:engine:db_query
      • prisma:engine:db_query
      • prisma:engine:serialize
  • prisma:client:operation
    • prisma:client:serialize
  • prisma:client:operation
    • prisma:client:serialize

添加更多 instrumentation

OpenTelemetry 的一个优点是,只需对你的应用程序代码进行少量更改,即可添加更多 instrumentation。

例如,要添加 HTTP 和 ExpressJS 追踪,请将以下 instrumentation 添加到你的 OpenTelemetry 配置中。这些 instrumentation 为完整的请求-响应生命周期添加跨度。这些跨度向你展示了 HTTP 请求花费了多长时间。

// Imports
import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express'
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'

// Register your auto-instrumentors
registerInstrumentations({
tracerProvider: provider,
instrumentations: [
new HttpInstrumentation(),
new ExpressInstrumentation(),
new PrismaInstrumentation(),
],
})

有关可用 instrumentation 的完整列表,请查看 OpenTelemetry 注册表.

自定义资源属性

你可以通过更改资源属性使其更具体地反映你的应用程序,来调整你的应用程序追踪的分组方式

const provider = new NodeTracerProvider({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'weblog',
[SemanticResourceAttributes.SERVICE_VERSION]: '1.0.0',
}),
})

正在努力对常见资源属性进行标准化。在可能的情况下,最好遵循 标准属性名称.

减少性能影响

如果你的应用程序向收集器发送大量跨度,这会对性能产生重大影响。你可以使用以下方法来减少这种影响

使用 BatchSpanProcessor 批量发送追踪

在生产环境中,你可以使用 OpenTelemetry 的 BatchSpanProcessor 将跨度批量发送到收集器,而不是一次发送一个。但是,在开发和测试期间,你可能不想批量发送跨度。在这种情况下,你可能更愿意使用 SimpleSpanProcessor

你可以根据环境配置你的追踪配置以使用适当的跨度处理器,如下所示

import {
SimpleSpanProcessor,
BatchSpanProcessor,
} from '@opentelemetry/sdk-trace-base'

if (process.env.NODE_ENV === 'production') {
provider.addSpanProcessor(new BatchSpanProcessor(otlpTraceExporter))
} else {
provider.addSpanProcessor(new SimpleSpanProcessor(otlpTraceExporter))
}

使用采样向收集器发送更少的跨度

另一种减少性能影响的方法是 使用概率采样 向收集器发送更少的跨度。这降低了追踪的收集成本,但仍然很好地反映了你的应用程序中发生的情况。

示例实现如下所示

import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'
import { TraceIdRatioBasedSampler } from '@opentelemetry/core'
import { Resource } from '@opentelemetry/resources'

const provider = new NodeTracerProvider({
sampler: new TraceIdRatioBasedSampler(0.1),
resource: new Resource({
// we can define some metadata about the trace resource
[SemanticResourceAttributes.SERVICE_NAME]: 'test-tracing-service',
[SemanticResourceAttributes.SERVICE_VERSION]: '1.0.0',
}),
})

排查追踪问题

我的追踪没有显示

设置追踪的顺序很重要。在你的应用程序中,请确保在导入任何受 instrumentation 的依赖项之前注册追踪和 instrumentation。例如

import { registerTracing } from './tracing'

registerTracing({
name: 'tracing-example',
version: '0.0.1',
})

// You must import any dependencies after you register tracing.
import { PrismaClient } from '@prisma/client'
import async from 'express-async-handler'
import express from 'express'