跳至主要内容

OpenTelemetry 追踪

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

信息

追踪让您对 Prisma ORM 项目有高度详细、操作层面的洞察。

关于追踪

当您启用追踪时,Prisma Client 输出以下内容

  • Prisma Client 进行的每个操作(例如 findMany)对应一个追踪。
  • 在每个追踪中,有一个或多个span。每个 span 代表操作一个阶段所需的时间长度,例如序列化或数据库查询。Span 以树状结构表示,其中子 span 表示执行发生在更大的父 span 内。

追踪中 span 的数量和类型取决于追踪所涵盖的操作类型,但例如以下内容

image

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

追踪输出

对于每个追踪,Prisma Client 输出一系列 span。这些 span 的数量和类型取决于 Prisma Client 操作。典型的 Prisma 追踪具有以下 span

  • prisma:client:operation:代表整个 Prisma Client 操作,从 Prisma Client 到数据库并返回。它包含诸如 Prisma Client 调用的模型和方法等详细信息。根据 Prisma 操作,它包含以下一个或多个 span
    • prisma:client:connect:代表 Prisma Client 连接到数据库所需的时间。
    • prisma:client:serialize:代表验证 Prisma Client 操作并将其转换为查询引擎查询所需的时间。
    • prisma:engine:query:代表查询在查询引擎中所需的时间。
      • prisma:engine:connection:代表 Prisma Client 获取数据库连接所需的时间。
      • prisma:engine:db_query:代表对数据库执行的数据库查询。它在标签中包含查询,以及查询运行所需的时间。
      • prisma:engine:serialize:代表将数据库的原始响应转换为类型化结果所需的时间。
      • prisma:engine:response_json_serialization:代表将数据库查询结果序列化为 Prisma Client 的 JSON 响应所需的时间。

例如,给定以下 Prisma Client 代码

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

追踪结构如下

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

注意事项和先决条件

如果您的应用程序向收集器发送大量 span,这可能会对性能产生显著影响。有关如何最大限度地减少此影响的信息,请参阅减少性能影响

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

  1. 安装相应的依赖项.
  2. 安装 OpenTelemetry 包.
  3. 在您的应用程序中注册追踪.

开始在 Prisma ORM 中使用追踪

本节解释如何在您的应用程序中安装和注册追踪。

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

使用 prisma@prisma/client@prisma/instrumentation npm 包的 6.1.0 或更高版本。您还需要安装 @opentelemetry/api 包,因为它是对等依赖项。

npm install prisma@latest --save-dev
npm install @prisma/client@latest --save
npm install @prisma/instrumentation@latest --save
npm install @opentelemetry/api@latest --save
在旧版本 Prisma ORM 上追踪

追踪作为预览功能在 Prisma ORM 的 4.2.0 版本中添加。对于 Prisma ORM 4.2.06.1.0 之间的版本,您需要在 Prisma schema 文件中启用 tracing 预览功能。

generator client {
provider = "prisma-client"
output = "./generated"
previewFeatures = ["tracing"]
}

步骤 2:安装 OpenTelemetry 包

现在安装相应的 OpenTelemetry 包,如下所示

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

步骤 3:在您的应用程序中注册追踪

以下代码提供了在 Prisma 中配置 OpenTelemetry 追踪的两个示例

  1. 使用 @opentelemetry/sdk-trace-node(现有示例),它对追踪设置提供了精细控制。
  2. 使用 @opentelemetry/sdk-node,它提供了更简单的配置,并与 OpenTelemetry 的 JavaScript 入门指南一致。

选项 1:使用 @opentelemetry/sdk-trace-node

此设置为您提供了对插桩和追踪的精细控制。您需要针对您的特定应用程序自定义此配置。这种方法简洁,对于需要快速设置以将追踪发送到 OTLP 兼容后端(如 Honeycomb、Jaeger 或 Datadog)的用户来说更容易。

// Imports
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'
import { PrismaInstrumentation, registerInstrumentations } from '@prisma/instrumentation'
import { resourceFromAttributes } from '@opentelemetry/resources'

// Configure the trace provider
const provider = new NodeTracerProvider({
resource: resourceFromAttributes({
[ATTR_SERVICE_NAME]: 'example application', // Replace with your service name
[ATTR_SERVICE_VERSION]: '0.0.1', // Replace with your service version
}),
spanProcessors: [
// 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).
new SimpleSpanProcessor(new OTLPTraceExporter()),
],
});

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

// Register the provider globally
provider.register()

这种方法提供了最大的灵活性,但可能涉及额外的配置步骤。

选项 2:使用 @opentelemetry/sdk-node

对于许多用户,特别是初学者,NodeSDK 类通过将常见默认值捆绑到单一统一配置中,从而简化了 OpenTelemetry 设置。

// Imports
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'
import { NodeSDK } from '@opentelemetry/sdk-node'
import { PrismaInstrumentation } from '@prisma/instrumentation'

// Configure the OTLP trace exporter
const traceExporter = new OTLPTraceExporter({
url: 'https://api.honeycomb.io/v1/traces', // Replace with your collector's endpoint
headers: {
'x-honeycomb-team': 'HONEYCOMB_API_KEY', // Replace with your Honeycomb API key or collector auth header
},
})

// Initialize the NodeSDK
const sdk = new NodeSDK({
serviceName: 'my-service-name', // Replace with your service name
traceExporter,
instrumentations: [
new PrismaInstrumentation(),
],
})

// Start the SDK
sdk.start()

// Handle graceful shutdown
process.on('SIGTERM', async () => {
try {
await sdk.shutdown()
console.log('Tracing shut down successfully')
} catch (err) {
console.error('Error shutting down tracing', err)
} finally {
process.exit(0)
}
})

如果出现以下情况,请选择 NodeSDK 方法

  • 您刚开始使用 OpenTelemetry 并希望简化设置。
  • 您需要以最少的样板代码快速集成追踪。
  • 您正在使用 OTLP 兼容的追踪后端,如 Honeycomb、Jaeger 或 Datadog。

如果出现以下情况,请选择 NodeTracerProvider 方法

  • 您需要对 span 的创建、处理和导出方式进行详细控制。
  • 您正在使用自定义 span 处理器或导出器。
  • 您的应用程序需要特定的插桩或采样策略。

OpenTelemetry 具有高度可配置性。您可以自定义资源属性、哪些组件被插桩、如何处理 span 以及将 span 发送到何处。

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

追踪操作指南

使用 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://:16686/ 找到追踪仪表盘。当您在启用追踪的情况下使用应用程序时,您将开始在此仪表盘中看到追踪。

将追踪输出发送到控制台

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

// Imports
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks';
import * as api from '@opentelemetry/api';
import {
PrismaInstrumentation,
registerInstrumentations,
} from '@prisma/instrumentation';
import { resourceFromAttributes } 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 NodeTracerProvider({
resource: resourceFromAttributes({
[ATTR_SERVICE_NAME]: 'example application', // Replace with your service name
[ATTR_SERVICE_VERSION]: '0.0.1', // Replace with your service version
}),
spanProcessors: [
// Configure how spans are processed and exported. In this case, we're sending spans
// as we receive them to the console
new SimpleSpanProcessor(consoleExporter),
],
});

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

// Register the provider
provider.register();
}

追踪交互式事务

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

  • prisma:client:transaction:一个根 span,它封装了 prisma span。
    • prisma:engine:itx_runner:代表交互式事务在查询引擎中所需的时间。
    • prisma:engine:itx_query_builder:代表构建交互式事务所需的时间。

例如,以下 Prisma schema

schema.prisma
generator client {
provider = "prisma-client"
output = "./generated"
}

datasource db {
provider = "postgresql"
}

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

添加更多插桩

OpenTelemetry 的一个好处是,只需对应用程序代码进行最少的更改即可添加更多插桩。

例如,要添加 HTTP 和 ExpressJS 追踪,请在您的 OpenTelemetry 配置中添加以下插桩。这些插桩为完整的请求-响应生命周期添加了 span。这些 span 向您显示 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(),
],
})

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

自定义资源属性

您可以通过更改资源属性,使其更具体地针对您的应用程序,从而调整应用程序追踪的分组方式

const provider = new NodeTracerProvider({
resource: new Resource({
[ATTR_SERVICE_NAME]: 'weblog',
[ATTR_SERVICE_VERSION]: '1.0.0',
}),
})

目前正在努力标准化通用资源属性。在可能的情况下,最好遵循标准属性名称

减少性能影响

如果您的应用程序向收集器发送大量 span,这可能会对性能产生显著影响。您可以使用以下方法来减少此影响

使用 BatchSpanProcessor 批量发送追踪

在生产环境中,您可以使用 OpenTelemetry 的 BatchSpanProcessor 批量而不是一次发送 span 到收集器。然而,在开发和测试期间,您可能不希望批量发送 span。在这种情况下,您可能更喜欢使用 SimpleSpanProcessor

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

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

const spanProcessors = [];
if (process.env.NODE_ENV === 'production') {
spanProcessors.push(new BatchSpanProcessor(otlpTraceExporter));
} else {
spanProcessors.push(new SimpleSpanProcessor(otlpTraceExporter));
}

const provider = new NodeTracerProvider({
spanProcessors,
// ...other configurations
});

通过采样向收集器发送更少的 span

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

一个示例实现如下所示

import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { TraceIdRatioBasedSampler } from '@opentelemetry/core';
import { resourceFromAttributes } from '@opentelemetry/resources';

const provider = new NodeTracerProvider({
sampler: new TraceIdRatioBasedSampler(0.1),
resource: resourceFromAttributes({
// we can define some metadata about the trace resource
[ATTR_SERVICE_NAME]: 'test-tracing-service',
[ATTR_SERVICE_VERSION]: '1.0.0',
}),
});

追踪故障排除

我的追踪没有显示

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

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/generated/client'
import async from 'express-async-handler'
import express from 'express'
© . This site is unofficial and not affiliated with Prisma Data, Inc.