如何設定 Prisma ORM 的 Datadog 追蹤
簡介
在本指南中,您將學習如何為新的 Prisma 專案設定 Datadog 追蹤。透過結合 @prisma/instrumentation 套件與 Prisma Client 擴充功能,您可以捕捉每個資料庫查詢的詳細追蹤片段(Spans)。這些片段會被注入查詢的中繼資料,並使用 Datadog 官方的 Node.js APM 函式庫 dd-trace 發送至 Datadog,讓您能夠監控、分析並了解應用程式的資料庫活動。
什麼是片段(Spans)與追蹤(Tracing)?
-
片段(Spans)是分散式系統或複雜應用程式中,單一的操作或工作單元。每一個資料庫查詢、服務呼叫或外部請求都由一個片段來表示。
-
追蹤(Tracing)將這些片段串聯起來,形成一個請求生命週期的完整圖景。透過追蹤,您可以視覺化瓶頸、識別問題查詢,並精確定位查詢發生錯誤的位置。
為什麼要將 Datadog 與 Prisma ORM 搭配使用?
Datadog 提供應用程式效能監控(APM)、指標、日誌和儀表板,協助您觀察並除錯生產環境系統。
雖然 Prisma ORM 抽象化了 SQL 並提高了開發人員的生產力,但在沒有適當的檢測(Instrumentation)情況下,它可能會隱蔽查詢效能。透過 @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、建立架構並產生 Prisma Client。這能讓您的應用程式準備好執行資料庫查詢——即您將使用 Datadog 追蹤的查詢。
2.1. 安裝並初始化 Prisma ORM
執行以下指令來安裝 Prisma 和極簡的 TypeScript 執行器:
npm install -D prisma tsx
然後使用 --db 旗標初始化 Prisma,以建立新的 Prisma Postgres 實例:
npx prisma init --db --output ../src/generated/prisma
系統會提示您為資料庫命名並選擇最近的區域。為了清晰起見,請選擇一個容易記住的名字(例如 My Datadog Project)。
此指令會執行下列操作:
- 建立一個包含
schema.prisma檔案的prisma目錄。 - 在
/src/generated/prisma目錄中產生 Prisma Client(如--output旗標所示)。 - 在專案根目錄建立一個包含資料庫連線字串(
DATABASE_URL)的.env檔案。
.env 檔案應包含標準連線字串:
# Placeholder url you have to replace
DATABASE_URL="postgresql://janedoe:mypassword@localhost:5432/mydb?schema=sample"
安裝 PostgreSQL 的驅動程式配接器(Driver Adapter):
npm i @prisma/adapter-pg pg
npm i -D @types/pg
如果您使用的是不同的資料庫提供者(MySQL、SQL Server、SQLite),請安裝相應的驅動程式適配器套件,而不是 @prisma/adapter-pg。如需更多資訊,請參閱資料庫驅動程式。
2.2. 定義模型
現在,開啟 prisma/schema.prisma 並更新您的產生器區塊與模型。將 generator 區塊替換為以下內容,並新增 User 和 Post 模型:
generator client {
provider = "prisma-client"
output = "../src/generated/prisma"
}
datasource db {
provider = "postgresql"
}
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])
}
建立 prisma.config.ts 檔案以設定 Prisma:
import 'dotenv/config'
import { defineConfig, env } from 'prisma/config';
export default defineConfig({
schema: 'prisma/schema.prisma',
migrations: {
path: 'prisma/migrations',
},
datasource: {
url: env('DATABASE_URL'),
},
});
如果您尚未安裝 dotenv 套件,則需要安裝它:
npm install dotenv
2.3. 產生 Prisma Client 並執行遷移
產生 Prisma Client 並將您的架構套用到資料庫:
npx prisma generate
npx prisma migrate dev --name "init"
這會根據您在 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";
tracer.init({
profiling: true,
logInjection: true,
runtimeMetrics: true,
dbmPropagationMode: "full",
env: "dev",
sampleRate: 1,
service: "prisma-datadog-tracing",
version: "1.0.0"
});
export { tracer };
說明
tracer.init使用service名稱設定dd-trace。此名稱會顯示在 Datadog 的APM>Services清單中。@prisma/instrumentation會自動記錄每個 Prisma 查詢為一個 Datadog 片段。
5. 實例化 Prisma 並執行查詢
5.1. 建立 Prisma Client 實例
建立 src/client.ts 來存放您的 Prisma Client 實例化:
import { tracer } from "./tracer";
import { PrismaClient } from "./generated/prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL!,
});
const prisma = new PrismaClient({
adapter,
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 Client 之前匯入
tracer,確保儘早初始化追蹤。 $on("query")鉤子會捕捉原生 SQL 查詢並將其作為獨立片段發送。$allOperations擴充功能將所有 Prisma 操作封裝在自訂片段中,允許您使用模型、操作類型和引數等中繼資料來標記它們。
與提供自動追蹤開箱即用體驗的 @prisma/instrumentation 套件不同,這種手動設定讓您能完全控制每個片段的結構和標記方式。當您需要自訂片段名稱、額外中繼資料、更簡單的設定,或是為了繞過 OpenTelemetry 生態系統中的限制或相容性問題時,這會很有幫助。它還允許您根據查詢情境調整追蹤行為,這在複雜的應用程式中特別有用。
5.2. 新增執行查詢的指令碼
建立 src/index.ts 檔案,並新增程式碼來執行資料庫查詢並將追蹤發送至 Datadog:
import { tracer } from "./tracer";
import {
PrismaInstrumentation,
registerInstrumentations,
} from "@prisma/instrumentation";
import { prisma } from "./client";
const provider = new tracer.TracerProvider();
registerInstrumentations({
instrumentations: [new PrismaInstrumentation()],
tracerProvider: provider,
});
provider.register();
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.");
});
如果您在 tracerProvider: provider 這行遇到型別不相容的 linting 錯誤,很可能是因為 @opentelemetry/api 套件的版本不符。
若要解決此問題,請將下列覆寫(override)新增至您的 package.json:
"overrides": {
"@opentelemetry/api": "1.8.0"
}
這是必要的,因為 dd-trace 尚未支援 @opentelemetry/api 的 1.9.0 或更高版本。
更新 package.json 後,重新安裝您的相依項目:
npm i
這樣應該就能解決 linting 錯誤。
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 上交流 透過為存放庫加星標、報告問題或為 issue 做出貢獻。