如何使用 Prisma ORM 设置 Datadog 追踪
简介
在本指南中,你将学习如何为一个新的 Prisma 项目设置 Datadog 追踪。通过将 @prisma/instrumentation
包与 Prisma 客户端扩展结合使用,你可以为每个数据库查询捕获详细的跨度(span)。这些跨度会通过 dd-trace
(Datadog 官方的 Node.js APM 库)被丰富查询元数据并发送到 Datadog,使你能够监控、分析并全面了解应用程序的数据库活动。
什么是跨度(span)和追踪?
-
跨度(Spans)是分布式系统或复杂应用程序中的单个操作或工作单元。每个数据库查询、服务调用或外部请求都由一个跨度表示。
-
追踪(Tracing)将这些跨度连接起来,形成一个请求生命周期的完整、端到端视图。通过追踪,你可以可视化瓶颈、识别有问题的查询,并从查询中精确找出错误发生的位置。
为什么将 Datadog 与 Prisma ORM 结合使用?
Datadog 提供应用程序性能监控 (APM)、指标、日志和仪表板,帮助你观察和调试生产系统。
尽管 Prisma ORM 抽象了 SQL 并提高了开发人员的生产力,但如果没有适当的检测,它可能会掩盖查询性能。通过使用 @prisma/instrumentation
和 dd-trace
将 Datadog 与 Prisma 集成,你可以自动捕获每个数据库查询的跨度。
这使你能够:
- 测量每个查询的延迟。
- 检查查询参数和原始 SQL。
- 在应用程序级别请求的上下文中追踪 Prisma 操作。
- 识别与数据库访问相关的瓶颈。
这种集成以最小的努力提供了 Prisma 查询的运行时可见性,帮助你实时捕获慢查询和错误。
先决条件
开始之前,请确保你已具备以下条件:
- 已安装 Node.js(推荐 v18+)。
- 一个本地或托管的 PostgreSQL 数据库。
- 一个 Datadog 帐户。如果你没有,请在此处注册。
- 在你的机器或将运行此应用程序的服务器上安装并运行 Datadog Agent。你可以按照 Datadog Agent 安装文档 进行设置。
1. 创建一个新项目
我们将从创建一个新的 Node.js 项目开始,以演示 Datadog 和 Prisma ORM 的追踪功能。这将是一个最小的、独立的设置,专注于运行和追踪 Prisma 查询,以独立理解检测流程。
如果你正在将追踪集成到现有 Prisma 项目中,可以跳过此步骤,直接从设置追踪部分开始。只需确保在项目对应的文件夹结构中应用更改即可。
mkdir prisma-datadog-tracing
cd prisma-datadog-tracing
npm init -y
在此设置中,你将:
- 使用基本模型定义 Prisma schema。
- 连接到 Postgres 数据库(Prisma Postgres 或你自己的数据库)。
- 使用
@prisma/instrumentation
和dd-trace
配置所有查询的 Datadog 追踪。 - 运行一个执行 Prisma 操作并将跨度发送到 Datadog 的示例脚本。
2. 设置 Prisma ORM
在本节中,你将安装 Prisma,创建你的 schema,并生成 Prisma 客户端。这将为你的应用程序准备好运行数据库查询——你将使用 Datadog 追踪这些查询。
2.1. 安装并初始化 Prisma ORM
运行以下命令安装 Prisma 和一个最小的 TypeScript 运行器:
npm install -D prisma tsx
然后初始化 Prisma:
你可以在项目中初始化 Prisma 时使用 --db
标志来创建一个 新的 Prisma Postgres 实例。
- Prisma Postgres(推荐)
- 你自己的数据库
npx prisma init --db --output ../src/generated/prisma
系统将提示你命名数据库并选择最接近的区域。为清晰起见,请选择一个易于记忆的名称(例如,My Datadog Project
)。
npx prisma init --output ../src/generated/prisma
此命令执行以下操作:
- 创建包含
schema.prisma
文件的prisma
目录。 - 在
/src/generated/prisma
目录中生成 Prisma Client(如--output
标志所指定)。 - 在项目根目录创建包含数据库连接字符串 (
DATABASE_URL
) 的.env
文件。
如果你没有使用 --db
标志,请替换 .env
文件中的占位符数据库 URL:
- Prisma Postgres
- 你自己的数据库
DATABASE_URL="prisma+postgres://accelerate.prisma-data.net/?api_key=..."
# Placeholder url you have to replace
DATABASE_URL="postgresql://janedoe:mypassword@localhost:5432/mydb?schema=sample"
如果你正在使用 Prisma Postgres,也请安装:
npm i @prisma/extension-accelerate
此扩展使你能够缓存你的 Prisma 查询。
2.2. 定义模型
现在,打开 prisma/schema.prisma
并更新你的生成器块和模型。将 generator
块替换为以下内容,并添加 User
和 Post
模型:
generator client {
provider = "prisma-client-js"
output = "../src/generated/prisma"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
authorId Int
author User @relation(fields: [authorId], references: [id])
}
2.3. 生成 Prisma 客户端并运行迁移
生成 Prisma 客户端并将你的 schema 应用到数据库:
npx prisma generate
npx prisma migrate dev --name "init"
这将根据你的 schema 在 Postgres 数据库中创建表,并为你生成一个客户端以与数据库进行交互。
3. 安装追踪所需的依赖项
除了 Prisma,你还需要以下包用于 Datadog 追踪:
npm install @prisma/instrumentation \
dd-trace
还要确保你有 TypeScript 的开发依赖项:
npm install -D typescript
以下是快速概览:
@prisma/instrumentation
:检测 Prisma 查询,使它们在你的追踪器中显示为跨度。dd-trace
:Datadog 官方的 Node.js 追踪库。
4. 设置 Datadog 追踪
在 src
文件夹中创建一个 tracer.ts
文件来实例化你的追踪逻辑:
touch src/tracer.ts
4.1. 配置追踪器
打开 src/tracer.ts
并添加以下代码:
import Tracer from "dd-trace";
import {
PrismaInstrumentation,
registerInstrumentations,
} from "@prisma/instrumentation";
const tracer = Tracer.init({
apmTracingEnabled: true,
service: "prisma-datadog-tracing",
version: "1.0.0",
profiling: true
});
const provider = new tracer.TracerProvider();
// Register the provider globally
provider.register();
registerInstrumentations({
instrumentations: [
new PrismaInstrumentation({
enabled: true,
}),
],
tracerProvider: provider,
});
export { tracer };
如果由于类型不兼容而在 traceProvider: provider
行遇到 linting 错误,这很可能是由 @opentelemetry/api
包的版本不匹配引起的。
要解决此问题,请在 package.json
中添加以下覆盖:
"overrides": {
"@opentelemetry/api": "1.8.0"
}
这是必要的,因为 dd-trace
尚不支持 @opentelemetry/api
的 1.9.0
或更高版本。
更新 package.json
后,重新安装你的依赖项:
npm i
这应该会解决 linting 错误。
解释
Tracer.init
使用service
名称配置dd-trace
。此名称会显示在 Datadog 的APM
>Services
列表中。@prisma/instrumentation
自动将每个 Prisma 查询记录为 Datadog 跨度。middleware: true
选项确保每个查询都被拦截以进行检测。
5. 实例化 Prisma 并运行查询
5.1. 创建 Prisma 客户端实例
创建 src/client.ts
以保存你的 Prisma 客户端实例化:
- Prisma Postgres(推荐)
- 你自己的数据库
import { tracer } from "./tracer";
import { withAccelerate } from "@prisma/extension-accelerate";
import { PrismaClient } from "./generated/prisma";
const prisma = new PrismaClient({
log: [{ emit: "event", level: "query" }],
})
.$on("query", (e) => {
const span = tracer.startSpan(`prisma_raw_query`, {
childOf: tracer.scope().active() || undefined,
tags: {
"prisma.rawquery": e.query,
},
});
span.finish();
})
.$extends({
query: {
async $allOperations({ operation, model, args, query }) {
const span = tracer.startSpan(
`prisma_query_${model?.toLowerCase()}_${operation}`,
{
tags: {
"prisma.operation": operation,
"prisma.model": model,
"prisma.args": JSON.stringify(args),
"prisma.rawQuery": query,
},
childOf: tracer.scope().active() || undefined,
}
);
try {
const result = await query(args);
span.finish();
return result;
} catch (error) {
span.setTag("error", error);
span.finish();
throw error;
}
},
},
})
.$extends(withAccelerate());
export { prisma };
import { tracer } from "./tracer";
import { withAccelerate } from "@prisma/extension-accelerate";
import { PrismaClient } from "./generated/prisma";
const prisma = new PrismaClient({
log: [{ emit: "event", level: "query" }],
})
.$on("query", (e) => {
const span = tracer.startSpan(`prisma_raw_query`, {
childOf: tracer.scope().active() || undefined,
tags: {
"prisma.rawquery": e.query,
},
});
span.finish();
})
.$extends({
query: {
async $allOperations({ operation, model, args, query }) {
const span = tracer.startSpan(
`prisma_query_${model?.toLowerCase()}_${operation}`,
{
tags: {
"prisma.operation": operation,
"prisma.model": model,
"prisma.args": JSON.stringify(args),
"prisma.rawQuery": query,
},
childOf: tracer.scope().active() || undefined,
}
);
try {
const result = await query(args);
span.finish();
return result;
} catch (error) {
span.setTag("error", error);
span.finish();
throw error;
}
},
},
});
export { prisma };
上述设置使你对查询如何被追踪有了更多控制:
- 通过在创建 Prisma 客户端之前导入
tracer
,追踪会尽早初始化。 $on("query")
钩子捕获原始 SQL 查询并将其作为独立的跨度发送。$allOperations
扩展将所有 Prisma 操作包装在自定义跨度中,允许你使用模型、操作类型和参数等元数据来标记它们。
与 @prisma/instrumentation
包提供开箱即用的自动追踪不同,这种手动设置让你完全控制每个跨度的结构和标签。当你需要自定义跨度名称、附加元数据、更简单的设置,或者解决 OpenTelemetry 生态系统中的限制或兼容性问题时,这很有帮助。它还允许你根据查询上下文调整追踪行为,这在复杂应用程序中特别有用。
5.2. 添加一个执行查询的脚本
创建一个 src/index.ts
文件,并添加代码以执行对数据库的查询并将追踪发送到 Datadog:
import { prisma } from "./client";
async function main() {
const user1Email = `alice${Date.now()}@prisma.io`;
const user2Email = `bob${Date.now()}@prisma.io`;
let alice, bob;
// 1. Create users concurrently
try {
[alice, bob] = await Promise.all([
prisma.user.create({
data: {
email: user1Email,
name: "Alice",
posts: {
create: {
title: "Join the Prisma community on Discord",
content: "https://pris.ly/discord",
published: true,
},
},
},
include: { posts: true },
}),
prisma.user.create({
data: {
email: user2Email,
name: "Bob",
posts: {
create: [
{
title: "Check out Prisma on YouTube",
content: "https://pris.ly/youtube",
published: true,
},
{
title: "Follow Prisma on Twitter",
content: "https://twitter.com/prisma/",
published: false,
},
],
},
},
include: { posts: true },
}),
]);
console.log(
`✅ Created users: ${alice.name} (${alice.posts.length} post) and ${bob.name} (${bob.posts.length} posts)`
);
} catch (err) {
console.error("❌ Error creating users:", err);
return;
}
// 2. Fetch all published posts
try {
const publishedPosts = await prisma.post.findMany({
where: { published: true },
});
console.log(`✅ Retrieved ${publishedPosts.length} published post(s).`);
} catch (err) {
console.error("❌ Error fetching published posts:", err);
}
// 3. Create & publish a post for Alice
let post;
try {
post = await prisma.post.create({
data: {
title: "Join the Prisma Discord community",
content: "https://pris.ly/discord",
published: false,
author: { connect: { email: user1Email } },
},
});
console.log(`✅ Created draft post for Alice (ID: ${post.id})`);
} catch (err) {
console.error("❌ Error creating draft post for Alice:", err);
return;
}
try {
post = await prisma.post.update({
where: { id: post.id },
data: { published: true },
});
console.log("✅ Published Alice’s post:", post);
} catch (err) {
console.error("❌ Error publishing Alice's post:", err);
}
// 4. Fetch all posts by Alice
try {
const alicePosts = await prisma.post.findMany({
where: { author: { email: user1Email } },
});
console.log(
`✅ Retrieved ${alicePosts.length} post(s) by Alice.`,
alicePosts
);
} catch (err) {
console.error("❌ Error fetching Alice's posts:", err);
}
}
// Entrypoint
main()
.catch((err) => {
console.error("❌ Unexpected error:", err);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
console.log("🔌 Disconnected from database.");
});
6. 运行查询并查看追踪
运行查询:
npx tsx src/index.ts
这将执行你的脚本,脚本会:
- 注册 Datadog 追踪器。
- 执行多个 Prisma 查询。
- 记录每个操作的结果。
然后,在 Datadog 中确认追踪:
- 打开你的 Datadog APM 页面。
- 在侧面板中导航到 APM > Traces > Explorer。
- 浏览追踪和跨度列表,每个列表都代表一个 Prisma 查询(例如
prisma:query
)。
根据你的 Datadog 设置,新数据可能需要一到两分钟才能显示。如果你没有立即看到追踪,请刷新或稍等片刻。
下一步
你已成功:
- 使用 Prisma Postgres 创建了一个 Prisma ORM 项目。
- 使用
@prisma/instrumentation
和dd-trace
设置了 Datadog 追踪。 - 验证了数据库操作在 Datadog 中显示为跨度。
为了进一步提高你的可观察性:
- 为你的 HTTP 服务器或其他服务(例如 Express、Fastify)添加更多检测。
- 创建仪表板以查看数据的关键指标。
如需更多指导,请查看:
与 Prisma 保持联系
通过以下方式继续你的 Prisma 之旅: 我们的活跃社区。保持了解,参与其中,并与其他开发者协作
- 在 X 上关注我们 获取公告、直播活动和实用技巧。
- 加入我们的 Discord 提问、与社区交流并获得积极的对话支持。
- 在 YouTube 上订阅 观看教程、演示和直播。
- 在 GitHub 上参与 点赞仓库、报告问题或贡献代码。