如何使用 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 上參與 點贊倉庫、報告問題或貢獻程式碼。