昨天,我們了解了 Collector 如何扮演一個 data pipeline 的角色,它可以收集來自不同來源的 telemetry,集中處理後送往不同的儲存後端。那麼,該怎麼讓服務產生並傳送 telemetry 呢?
SDK 的目的是讓不同語言都能夠透過標準化的方法生成 telemetry,所以在介紹 SDK 之前,先來介紹 OpenTelemetry 產生 telemetry 資料的過程,官方文件把這個過程稱作 instrumentation。
Instrumentation 是指在應用程式中加入程式碼來產生 telemetry 資料的過程。在 OpenTelemetry 中,instrumentation 可以分為兩種類型:
若維運人員想要讓現有的應用程式發送 telemetry,他可能不希望要透過修改現有程式碼來做到這一點,此時,OpenTelemetry 的 zero-code instrumentation 提供一個開箱及用的方案,可以讓我們不需要 API 或者 SDK,也能觀測應用程式。
若維運人員想要讓現有的應用程式發送 telemetry,他可能不希望修改程式碼。這時候,OpenTelemetry 的 zero-code instrumentation 就能派上用場。它的核心概念很單純──只要透過額外的工具或代理程式,就能在不改動任何程式碼的情況下,讓應用程式自動產生並傳送 telemetry。這對維運團隊來說相當方便。
目前有多種語言支援 zero-code insturmentation。可以參照官方文件來查看各個語言的 zero-code instrumentation 如何實作。
Code-based instrumentation 的實作主要透過 OpenTelemetry 的 API 和 SDK:
OpenTelemetry 採用了分離的設計理念,將 API 和 SDK 分開。簡單來說,API 定義了「什麼」資料被收集(例如特定的指標或者 span),以及收集資料的共同概念與程序;而 SDK 實作「如何」處理與匯出資料,其實作的內容則遵循 API 之定義。
透過這個分層架構,可以看到各個 singals 都有對應的 API。API 在這裡扮演的是一個抽象化的介面,而應用程式只需要知道這個介面。後續的資料操作則會透過背後已經註冊的 SDK 進行實作。
當然,我們可以只使用 API 而不使用 SDK,但是,我們就必須在程式碼自己實作處理與匯出資料的部分。不過反過來說,使用 SDK 也會產生比僅使用 API 更高的效能開銷。
講完定義的部分,接下來讓我們透過程式碼的使用來觀察差異,這裡以 Python 為例:
使用 API 的程式碼
from opentelemetry import metrics
# 透過 API 獲取 meter
meter = metrics.get_meter(__name__)
# 建立 counter
order_counter = meter.create_counter(
name="orders_processed",
description="Number of orders processed"
)
def process_order(order_id):
# 記錄 metric
order_counter.add(1, {"status": "processing"})
# 執行業務邏輯
validate_order(order_id)
calculate_total(order_id)
order_counter.add(1, {"status": "completed"})
在這個例子中,我們只使用了 OpenTelemetry 的 API 來定義標準介面:
metrics.get_meter()
- 透過 API 獲取 metermeter.create_counter()
- 建立 countercounter.add()
- 記錄數值這些都是 API 層級的操作,不涉及具體的實作細節。
使用 SDK 的程式碼
from opentelemetry import metrics
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
# 透過 SDK 配置 meter provider
otlp_exporter = OTLPMetricExporter(
endpoint="http://localhost:4317",
insecure=True
)
metric_reader = PeriodicExportingMetricReader(
exporter=otlp_exporter,
export_interval_millis=30000 # 每 30 秒匯出一次
)
metrics.set_meter_provider(MeterProvider(metric_readers=[metric_reader]))
# 現在使用 API(與上面相同)
meter = metrics.get_meter(__name__)
order_counter = meter.create_counter(
name="orders_processed",
description="Number of orders processed"
)
def process_order(order_id):
order_counter.add(1, {"status": "processing"})
validate_order(order_id)
calculate_total(order_id)
order_counter.add(1, {"status": "completed"})
在 SDK 的部分,我們設定了:
MeterProvider
- 管理 meter 的生命週期OTLPMetricExporter
- 定義資料要送往哪裡PeriodicExportingMetricReader
- 定義多久匯出一次 metrics總結來看,API 和 SDK 的結合使用,讓我們可以完整享受 OpenTelemetry 在收集 signals 上的便利性;從程式碼也能看到兩者的主要差異如下:
即便 OpenTelemetry 提供了標準化且跨語言的 SDK,使我們能夠輕鬆地讓服務發送 telemetry,但實作上,除了對於 SDK 操作要有一定的了解之外,團隊也必須要對 OpenTelemetry 的 signals 定義有基本的認識,才不會誤用了 signal 的種類,造成後續資料處理與溝通上的麻煩。
明天開始,本系列文將會接著介紹在 OpenTelemetry 中 signal 的定義,包含了 Metrics, Traces, Logs 以及 Baggage,並以 Python API & SDK 為範例,介紹該如何收集這些 signals 並傳送到 OpenTelemetry Collector。
OpenTelemetry Docs - Instrumentation
OpenTelemetry Docs - OpenTelemetry-Python API Reference
OpenTelemetry Docs - OTel 1.49.0 - Overview
SigNoz - OpenTelemetry API vs SDK - Key Differences Explained