追蹤是一種強大的工具,可讓您分析應用程式的效能並識別瓶頸。本教程將教您追蹤的核心概念,以及如何使用 OpenTelemetry 和 Prisma 的追蹤功能將追蹤整合到您的應用程式中。
目錄
簡介
在本教程中,您將學習如何將追蹤整合到使用 Prisma 和 Express 構建的現有 Web 應用程式中。您將使用 OpenTelemetry 實現追蹤,這是一個用於收集追蹤和其他遙測資料(例如日誌、指標等)的廠商中立標準。
首先,您將為 HTTP 端點建立手動追蹤並將其列印到控制檯。然後,您將學習如何使用 Jaeger 視覺化您的追蹤。您還將學習如何使用 Prisma 的追蹤功能自動為您的資料庫查詢生成追蹤。最後,您將瞭解自動插樁以及使用追蹤時的效能考量。
什麼是追蹤?
追蹤是一種*可觀察性工具*,它記錄請求在應用程式中傳播時所經過的路徑。追蹤可幫助您將系統為響應任何特定請求而執行的活動關聯起來。追蹤還提供有關這些活動的時間資訊(例如,開始時間、持續時間等)。
一個單獨的*追蹤*會為您提供有關使用者或應用程式發出請求時發生的情況的資訊。每個追蹤由一個或多個*跨度*組成,其中包含有關請求期間發生的單個步驟或任務的資訊。
使用像 Jaeger 這樣的追蹤工具,追蹤可以視覺化為以下圖表

一個單獨的跨度可以有多個子跨度,它們表示父跨度期間發生的子任務。例如,在上面的圖中,PRISMA QUERY 跨度有一個名為 PRISMA ENGINE 的子跨度。最頂層的跨度稱為*根跨度*,表示從開始到結束的整個追蹤。在上面的圖中,GET /ENDPOINT 是根跨度。
追蹤是深入瞭解和檢視系統的一種絕佳方式。它允許您精確識別影響應用程式的錯誤和效能瓶頸。追蹤對於除錯分散式系統特別有用,在分散式系統中,每個請求可能涉及多個服務,並且特定問題可能難以在本地重現。
您將使用的技術
在本教程中,您將使用以下工具
- OpenTelemetry 作為追蹤庫/API
- Prisma 作為物件關係對映器 (ORM)
- SQLite 作為資料庫
- Jaeger 作為追蹤視覺化工具
- Express 作為 Web 框架
- TypeScript 作為程式語言
先決條件
假設的知識
這是一個適合初學者的教程。但是,本教程假設您
- 對 JavaScript 或 TypeScript(首選)有基本瞭解
- 對後端 Web 開發有基本瞭解
注意:本教程假定您沒有追蹤和可觀察性方面的預備知識。
開發環境
要跟隨本教程,您需要
- ... 安裝 Node.js。
- ... 安裝 Docker 和 Docker Compose。
- ... *可選地*安裝 Prisma VS Code 擴充套件。Prisma VS Code 擴充套件為 Prisma 添加了非常棒的智慧感知和語法高亮。
- ... *可選地*擁有 Unix Shell 訪問許可權(例如 Linux 和 macOS 中的終端/Shell),以執行本系列中提供的命令。
如果您沒有 Unix Shell(例如,您使用的是 Windows 機器),您仍然可以繼續操作,但 Shell 命令可能需要根據您的機器進行修改。
克隆儲存庫
您將需要一個 Web 應用程式來演示追蹤。您可以使用我們為本教程構建的現有 Express Web 應用程式。
首先,執行以下操作
- 克隆 儲存庫
- 導航到克隆的目錄
- 安裝依賴項
- 從
prisma/migrations目錄應用資料庫遷移
注意:此命令還將生成 Prisma Client 併為資料庫填充資料。
- 啟動專案
注意:您在開發應用程式時應保持伺服器執行。
dev指令碼應在程式碼發生任何更改時重新啟動伺服器。
該應用程式只有一個端點:https://:4000/users/random。此端點將從資料庫中返回 10 個使用者的隨機樣本。透過訪問上述 URL 或執行以下命令來測試該端點
專案結構和檔案
您克隆的儲存庫具有以下結構
此儲存庫中值得注意的檔案和目錄有
prismaschema.prisma: 定義資料庫 schema。migrations: 包含資料庫遷移歷史記錄。seed.ts: 包含用於為開發資料庫填充模擬資料的指令碼。dev.db: 儲存 SQLite 資料庫的狀態。
server.ts: 帶有GET /users/random端點的 Express 伺服器。tsconfig.json&package.json: 配置檔案。
將追蹤整合到您的應用程式中
您的 Express 應用程式已經實現了所有核心“業務邏輯”(即返回 10 個隨機使用者)。為了衡量應用程式的效能並提高其可觀察性,您將整合追蹤。
在本節中,您將學習如何初始化追蹤並手動建立追蹤。
初始化追蹤
您將使用 OpenTelemetry 追蹤來實現追蹤。OpenTelemetry 提供了一個開源實現,它相容各種平臺和語言。此外,它還附帶用於實現追蹤的庫和 SDK。
透過安裝以下 OpenTelemetry 包來開始追蹤
這些包包含 OpenTelemetry 追蹤的 Node.js 實現。
現在,建立一個新的 tracing.ts 檔案來初始化追蹤
在 tracing.ts 內部,按如下方式初始化追蹤
initializeTracing 函式執行以下操作
- 它初始化一個追蹤器提供程式,用於建立追蹤器。追蹤器在您的應用程式中建立追蹤/跨度。
- 它定義一個追蹤匯出器並將其新增到您的提供程式中。追蹤匯出器將追蹤傳送到各種目標。在這種情況下,
ConsoleSpanExporter將追蹤列印到控制檯。 - 它透過呼叫
.register()函式來註冊提供程式以與 OpenTelemetry API 一起使用。 - 最後,它建立一個追蹤器並將其返回,追蹤器名稱作為引數傳遞給函式。
現在,匯入並在現有的 server.ts 中呼叫 initializeTracing
現在您已準備好建立您的第一個追蹤!
建立您的第一個追蹤
在上一節中,您初始化了追蹤並將追蹤器匯入到您的伺服器。現在您可以使用 tracer 物件在伺服器中建立跨度。首先,您將建立一個封裝 GET /users/random 請求的追蹤。按如下方式更新請求處理程式定義
在這裡,您使用 startActiveSpan() 建立一個新的跨度,並將所有請求處理程式邏輯包含在它提供的回撥函式中。回撥函式帶有一個對 span 物件的引用,您已將其命名為 requestSpan。您可以使用它來修改或向跨度新增屬性。在此程式碼中,您根據請求的結果將一個名為 http.status 的屬性設定為跨度。最後,一旦請求得到服務,您就結束該跨度。
要檢視新建立的跨度,請轉到 https://:4000/users/random。或者,您可以在終端中執行以下命令
轉到執行 Express 伺服器的終端視窗。您應該會看到一個*類似*以下內容的列印到控制檯的物件
此物件代表您剛剛建立的跨度。其中一些值得注意的屬性是
id表示此特定跨度的唯一識別符號。traceId表示特定追蹤的唯一識別符號。某個追蹤中的所有跨度都將具有相同的traceId。目前,您的追蹤僅包含一個跨度。parentId是父跨度的id。在本例中,它是undefined,因為根跨度沒有父跨度。name表示跨度的名稱。您在建立跨度時指定了它。timestamp是表示跨度建立時間的 UNIX 時間戳。duration是跨度的持續時間,以微秒為單位。
使用 Jaeger 視覺化追蹤
目前,您正在控制檯中檢視追蹤。雖然這對於單個追蹤來說是可管理的,但對於大量追蹤來說並不十分有用。為了更好地理解您的追蹤,您需要一些可以視覺化追蹤的追蹤解決方案。在本教程中,您將為此目的使用 Jaeger。
設定 Jaeger
您可以透過兩種方式設定 Jaeger
在本教程中,您將使用 Docker Compose 來執行 Jaeger 的 Docker 映象。首先,建立一個新的 docker-compose.yml 檔案
在檔案中定義以下服務
執行此映象將在 Docker 容器內設定並初始化 Jaeger 的所有必要元件。要執行 Jaeger,請開啟一個*新的*終端視窗,並在專案的主資料夾中執行以下命令
注意:如果您關閉執行 docker 容器的終端視窗,它也將停止容器。您可以透過在命令末尾新增
-d選項來避免這種情況,例如:docker-compose up -d。
如果一切順利,您應該能夠透過 https://:16686 訪問 Jaeger。

由於您的應用程式尚未向 Jaeger 傳送追蹤,Jaeger UI 將為空。
新增 Jaeger 追蹤匯出器
要在 Jaeger 中檢視您的追蹤,您需要設定一個新的追蹤匯出器,它將把追蹤從您的應用程式傳送到 Jaeger(而不是僅僅將它們列印到控制檯)。
首先,在您的專案中安裝匯出器包
現在將匯出器新增到 tracing.ts
在這裡,您初始化了一個新的 JaegerExporter 並將其新增到您的追蹤器提供程式中。JaegerExporter 建構函式中的 endpoint 屬性指向 Jaeger 監聽追蹤資料的位置。您還刪除了控制檯匯出器,因為它不再需要。
您現在應該能夠在 Jaeger 中看到您的追蹤。要檢視您的第一個追蹤
- 再次查詢
GET /users/random端點 (curl https://:4000/users/random)。 - 前往 https://:16686。
- 在左側的 Search(搜尋)選項卡中,在 Service(服務)下拉選單中,選擇 express-server。
- 在 Search 選項卡底部附近,單擊 Find Traces(查詢追蹤)。
- 您現在應該看到一個追蹤列表。單擊列表中的第一個追蹤。
- 您將看到追蹤的詳細檢視。應該有一個名為 GET /users/random 的單個跨度。單擊該跨度以獲取更多資訊。
- 您應該能夠看到有關追蹤的各種資訊,例如持續時間和開始時間。您還應該看到多個標籤,其中一個您已手動設定(
http.status)。

為您的 Prisma 查詢新增追蹤
在本節中,您將學習如何追蹤您的資料庫查詢。最初,您將透過自行建立跨度來手動執行此操作。儘管 Prisma 現在不再需要手動追蹤,但實現手動追蹤將讓您更好地瞭解追蹤的工作原理。
然後,您將使用 Prisma 的新 追蹤功能自動執行相同的操作。
手動追蹤您的 Prisma 查詢
要手動追蹤您的 Prisma 查詢,您必須將每個查詢包裝在一個跨度中。您可以透過將以下程式碼新增到 server.ts 檔案來完成此操作
您已經為 Prisma 查詢建立了一個名為 prisma.user.findmany 的新跨度。您還對 users 變數的宣告方式進行了一些更改,以使其與您程式碼的其餘部分保持一致。
透過再次查詢 GET /users/random 端點 (curl https://:4000/users/random) 並在 Jaeger 中檢視新生成的追蹤來測試新跨度。

您應該會看到生成的追蹤有一個名為 prisma.user.findmany 的新子跨度,巢狀在父 GET /users/random 跨度之下。現在您可以看到請求中執行 Prisma 查詢所花費的時間。
手動與自動插樁
到目前為止,您已經學習瞭如何設定追蹤以及如何手動為應用程式生成追蹤和跨度。像這樣手動定義跨度稱為*手動插樁*。手動插樁使您能夠完全控制應用程式如何被追蹤,但是,它有一些缺點
- 手動追蹤應用程式非常耗時,特別是當您的應用程式很大時。
- 並非總是能夠正確地手動插樁第三方庫。例如,無法使用手動插樁來追蹤 Prisma 內部元件的執行。
- 它可能導致錯誤和缺陷(例如,錯誤處理不當、損壞的跨度等),因為它涉及手動編寫大量程式碼。
幸運的是,許多框架和庫提供*自動插樁*,允許您自動為這些元件生成追蹤。自動插樁幾乎不需要程式碼更改,設定非常快速,並且可以為您提供開箱即用的基本遙測資料。
值得注意的是,自動插樁和手動插樁並非相互排斥。同時使用這兩種技術可能很有益。自動插樁可以提供良好的基線遙測,覆蓋所有端點。然後可以新增手動插樁,以實現特定的細粒度追蹤和自定義指標/元資料。
為 Prisma 設定自動插樁
本節將教您如何使用新的追蹤功能為 Prisma 設定自動插樁。首先,在您的 schema.prisma 檔案的 generator 塊中啟用追蹤功能標誌
注意:追蹤目前是一個預覽功能。這就是您必須在可以使用追蹤之前新增
tracing功能標誌的原因。
現在,重新生成 Prisma Client
要執行自動插樁,您還需要使用 npm 安裝兩個新包
需要這些包的原因是
@opentelemetry/instrumentation是設定自動插樁所必需的。@prisma/instrumentation為 Prisma Client 提供自動插樁。
根據 OpenTelemetry 術語,*被插樁庫*是為其收集追蹤的庫或包。另一方面,*插樁庫*是為特定被插樁庫生成追蹤的庫。在這種情況下,Prisma Client 是被插樁庫,而 @prisma/instrumentation 是插樁庫。
現在您需要向 OpenTelemetry 註冊 Prisma 插樁。為此,請將以下程式碼新增到您的 tracing.ts 檔案中
registerInstrumentations 呼叫接受兩個引數
instrumentations接受您要註冊的所有插樁庫的陣列。tracerProvider接受您的追蹤器提供程式。
由於您正在設定自動插樁,因此不再需要手動為 Prisma 查詢建立跨度。透過刪除 Prisma 查詢的手動跨度來更新 server.ts
使用自動插樁時,初始化追蹤的順序很重要。您需要在匯入被插樁庫之前設定追蹤並註冊插樁。在這種情況下,initializeTracing 呼叫必須在 import PrismaClient 語句之前。
再次,向 GET /users/random 端點發出請求,並在 Jaeger 中檢視生成的追蹤。

這次,同一個 Prisma 查詢生成了多個跨度,提供了更多關於查詢的細粒度資訊。啟用自動插樁後,您新增到應用程式的任何其他查詢也將自動生成追蹤。
注意:要了解有關 Prisma 生成的跨度的更多資訊,請參閱追蹤文件的追蹤輸出部分。
為 Express 設定自動插樁
目前,您正在透過手動建立跨度來追蹤您的端點。就像 Prisma 查詢一樣,隨著端點數量的增加,手動追蹤將變得難以管理。為了解決這個問題,您也可以為 Express 設定自動插樁。
首先安裝以下插樁庫
在 tracing.ts 內部註冊這兩個新的插樁庫
最後,移除 server.ts 中 GET /users/random 端點的手動跨度
向 GET /users/random 端點發出請求,並在 Jaeger 中檢視生成的追蹤。

您應該會看到更細粒度的跨度,顯示請求透過您的程式碼時不同步驟。特別是,您應該會看到由 ExpressInstrumentation 庫生成的新跨度,這些跨度顯示請求透過各種 Express 中介軟體和 GET /users/random 請求處理程式。
注意:有關可用插樁庫的列表,請檢視 OpenTelemetry 登錄檔。
減少追蹤的效能影響
如果您的應用程式向收集器(如 Jaeger)傳送大量跨度,這可能會對應用程式的效能產生顯著影響。這在您的開發環境中通常不是問題,但在生產環境中可能會成為問題。您可以採取一些措施來緩解此問題。
批次傳送追蹤
目前,您正在使用 SimpleSpanProcessor 傳送追蹤。這種方式效率低下,因為它一次傳送一個跨度。您可以改為使用 BatchSpanProcessor 批次傳送跨度。
在您的 tracing.ts 檔案中進行以下更改,以便在生產環境中使用 BatchSpanProcessor
請注意,在開發環境中,您仍然使用 SimpleSpanProcessor,因為在此環境中最佳化效能並不是主要考慮因素。這確保了在開發過程中,追蹤一經生成便立即顯示。
透過取樣減少跨度傳送
機率取樣是一種技術,允許 OpenTelemetry 追蹤使用者透過使用隨機取樣技術來降低跨度收集的效能成本。使用此技術,您可以減少傳送到收集器的跨度數量,同時仍能很好地表示應用程式中發生的情況。
更新 tracing.ts 以使用機率取樣
與批次處理一樣,您僅在生產環境中引入機率取樣。
總結與最終說明
恭喜!🎉
在本教程中,您學習了
- 什麼是追蹤,以及為什麼要使用它。
- 什麼是 OpenTelemetry,以及它與追蹤的關係。
- 如何使用 Jaeger 視覺化追蹤。
- 如何將追蹤整合到現有 Web 應用程式中。
- 如何使用自動插樁庫來提高程式碼可觀察性。
- 如何減少追蹤在生產環境中的效能影響。
您可以在 GitHub 上找到此專案的原始碼。如果您發現問題,請隨時在儲存庫中提出問題或提交拉取請求。您也可以直接在 Twitter 上與我聯絡。
不要錯過下一篇文章!
訂閱 Prisma 新聞通訊