使用 OpenTelemetry 構(gòu)建可觀測性 02- 埋點
這是講解 OpenTelemetry 系列博客的第二篇。在上一篇博客中,我們介紹了 OpenTelemetry 是什么以及由什么組成。現(xiàn)在我們將討論如何使用 OTel 準確收集遙測數(shù)據(jù)和鏈路追蹤數(shù)據(jù)。
手動埋點
我們這里談?wù)摗奥顸c”(代碼插樁),是指通過技術(shù)手段采集鏈路追蹤數(shù)據(jù)的行為。通常有兩種方式:手動和自動(下面討論)。顧名思義,手動埋點需要在軟件中顯式的選擇要暴露哪些數(shù)據(jù)。
手動埋點被認為是更高級和定制的遙測方法。手動和自動埋點分別有各自的使用場景,我們將在下文介紹。
一個請求進入系統(tǒng)并通過多個后端服務(wù)時,OpenTelemetry 能夠記錄該請求在系統(tǒng)中調(diào)用流程和經(jīng)過的完整路徑,這個路徑被稱為 鏈路追蹤(trace) 。請求可能觸發(fā)多個操作,每個操作都被記錄在一個 跨度(span) 中,表示具體操作的實例。
每個跨度都有一個父跨度,除非它是鏈路追蹤中的第一個跨度,在這種情況下,其父跨度 ID 為零(形成樹狀結(jié)構(gòu))。
注:示例應(yīng)用程序主要是用 Go 和一些 Python 編寫的。我將使用 Go 語言展示代碼示例,但其中原理和概念同樣適用于 OTel 支持的其他編程語言。
我們可以通過 API 將跨度添加到現(xiàn)有鏈路追蹤中(或啟動新鏈路追蹤)。對于 Go 語言,這意味著我們將引用?go.opentelemetry.io/otel
?庫,它包含了手動埋點所需的所有函數(shù)方法。我們可以通過函數(shù)調(diào)用,使用全局鏈路追蹤生產(chǎn)者來創(chuàng)建跨度:
這里有幾點需要注意。首先,我們先獲取全局鏈路追蹤的實例,使用這個實例創(chuàng)建一個新的跨度。
我們將在下一篇博客文章中更深入地討論鏈路追蹤生產(chǎn)者,它是 SDK 的一個組件,負責決定和管理這些遙測數(shù)據(jù)的流向和傳輸方式。
鏈路追蹤生產(chǎn)者既可以通過調(diào)用?otel.Tracer
?也可以顯式地使用參數(shù)傳遞。此示例應(yīng)用程序依賴于全局跟蹤器提供程序。當我們調(diào)用?otel.Tracer
?時,我們傳入埋點對象名稱,該名稱通常是處理埋點庫名。在示例應(yīng)用中,它被設(shè)置為“ github.com/trstringer/otel-shopping-cart ”。
一旦我們得到了鏈路追蹤生產(chǎn)者,就可以調(diào)用?Start
?函數(shù)并向其傳遞兩個參數(shù):上下文對象( context ,允許我們在不同的執(zhí)行環(huán)境中共享數(shù)據(jù),并且可以跨多函數(shù)調(diào)用、請求處理或線程之間)和跨度的名稱。上下文對象可以被新建(例如?context.Background()
?)或從它的父上下文傳遞(在本例中我使用的是 HTTP 請求上下文)??缍让Q可以是任何字符串,但在這個項目中,使用了一種標準化的命名方式,即選擇描述標識符來命名并且使用下劃線將不同標識符分隔。
Start
?函數(shù)的返回值之一是上下文對象,我們可以把它傳遞給代碼不同執(zhí)行分支或路徑(例如創(chuàng)建子跨度),以滿足那些需要使用相同上下文的調(diào)用;而另一個返回值跨度對象,可以用來處理其他操作。
正如在此示例中所示,首先是通過 defer 關(guān)鍵字聲明對函數(shù)?span.End
?的調(diào)用,以便可以將此跨度標記為完成。我們還可以為?span
?對象添加屬性。
還需要注意的是,跨度是可以被嵌套使用的。通常一個新跨度是進入了一個代碼執(zhí)行分支或路徑并且包含一個父跨度。這樣就形成了跨度的嵌套關(guān)系,準確地反映了請求所經(jīng)歷的代碼調(diào)用路徑。
屬性
在鏈路追蹤系統(tǒng)中,我們采集各種與系統(tǒng)行為相關(guān)的數(shù)據(jù),并將這些數(shù)據(jù)與特定的跨度進行關(guān)聯(lián),以便更好地理解系統(tǒng)行為。通過利用具有多樣取值的高基數(shù)數(shù)據(jù),我們能夠獲取更加詳細和全面的上下文信息,從而更好地觀測和分析系統(tǒng)的運行情況。
可以像下面給跨度設(shè)置屬性:
創(chuàng)建了一個名為?user.name
?的字符串類型的屬性并賦值??缍鹊挠涗浘蜁兂上旅孢@樣:
太棒了!現(xiàn)在名為?get_user_cart
?的跨度就包含這個新屬性?user.name
?。還可以在 Jaeger 中同樣看到這個屬性:

事件
在許多情況下,當使用鏈路追蹤時,您可能希望記錄一些文本或發(fā)生在跨度期間的事件。通過調(diào)用?span.AddEvent
,可以實現(xiàn)這一點:
記錄的事件中還可以設(shè)置屬性變量,如下例所示:

自動埋點
在前面的例子中,我們展示了如何手動在跨度中進行埋點操作。然而,OpenTelemetry 具有一個非常強大的特性,即支持廣泛的自動埋點。
自動埋點適用于以下情況:
對于 OpenTelemetry (OTel) 的新手,他們希望能夠快速利用 OTel 收集與應(yīng)用程序性能相關(guān)的指標和日志信息。
在現(xiàn)有代碼庫的基礎(chǔ)上嘗試集成和使用 OTel 的功能。
對于一些常用的組件或服務(wù),在對遙測數(shù)據(jù)沒有特殊要求的情況下,使用默認的自動埋點機制能夠自動處理數(shù)據(jù)收集。
在購物應(yīng)用的示例程序中,在 Python 服務(wù)(定價服務(wù))中使用自動埋點來處理了兩個事情:
Flask web 服務(wù)
MySQL 連接服務(wù)
自動埋點的神奇之處就在于,它所需要的僅僅是啟用自動埋點功能!然后,不需要任何額外的工作或編寫代碼,就能夠獲得一些關(guān)于 Flask 路由和 MySQL 查詢的非常有用的數(shù)據(jù)。這是 Flask 框架自動埋點的跨度:

記錄中可以看到大量與請求相關(guān)的信息,例如?http.target?、?net.peer.ip?、?http.method?等等。
MySQL 自動埋點有很多有價值的信息:

這太棒了。通過零代碼開發(fā),僅自動獲取跨度,它就已經(jīng)告訴我一個關(guān)鍵的數(shù)據(jù):查詢的持續(xù)時間。此外,還可以看到運行中的查詢以及運行該查詢的用戶。
這些數(shù)據(jù)提供了足夠的信息,用于對慢查詢進行故障排除,并幫助我們識別可能發(fā)生在數(shù)據(jù)庫側(cè)的意外情況。這一切都是因為一行代碼啟用了 MySQL 自動埋點!
總結(jié)
埋點是 OpenTelemetry 的核心。它定義了如何去收集哪些遙測數(shù)據(jù),我們既可以選擇手動埋點還可以利用現(xiàn)成的自動埋點代碼庫。在下一篇博文中,我們將了解 OTel SDK 是如何處理這些數(shù)據(jù)!
本文翻譯自:Observability with OpenTelemetry Part 2 - Instrumentation | Thomas Stringer(?https://trstringer.com/otel-part2-instrumentation/?)
擴展閱讀:
方法論:面向故障處理的可觀測性體系建設(shè)(?https://flashcat.cloud/blog/construction-of-observability-system-for-fault-processing/?)
白皮書:事件 OnCall 中心建設(shè)方法(?https://download.flashcat.cloud/flashduty-white-paper-v1.pdf?)
好工具:FlashDuty - 一站式告警處理平臺:告警降噪、排班OnCall ( https://flashcat.cloud/product/flashduty/?)