跳至主要內容

如何設定 Prisma ORM 的 Datadog 追蹤

15 分鐘

簡介

在本指南中,您將學習如何為新的 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/instrumentationdd-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/instrumentationdd-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 檔案應包含標準連線字串:

.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 區塊替換為以下內容,並新增 UserPost 模型:

prisma/schema.prisma
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:

prisma.config.ts
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 並新增下列程式碼:

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 實例化:

src/client.ts
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:

src/index.ts
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/api1.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/instrumentationdd-trace 設定了 Datadog 追蹤。
  • 驗證了資料庫操作作為片段在 Datadog 中顯示。

若要進一步提升可觀測性:

  • 為您的 HTTP 伺服器或其他服務(例如 Express、Fastify)新增更多檢測。
  • 建立儀表板以檢視來自您資料的關鍵指標。

如需額外指引,請參閱:


與 Prisma 保持聯繫

透過以下方式與我們聯繫,繼續您的 Prisma 旅程: 我們的活躍社群。保持資訊靈通、參與其中,並與其他開發者合作

我們衷心感謝您的參與,並期待您成為我們社群的一份子!

© . This site is unofficial and not affiliated with Prisma Data, Inc.