在昨天的 Traces 介紹中,我們了解到 SpanContext 提供了追蹤關聯性,讓分散式系統中的操作能夠正確地關聯到同一個 trace。然而,在實際的業務場景中,我們往往希望請求能攜帶一些額外的上下文資訊,例如用戶ID、所在地區、實驗標籤等。雖然 SpanContext 提供了基本的追蹤關聯性,但對於這類業務上下文的傳遞,OpenTelemetry 提供了另一個重要的機制:Baggage。
Baggage 是一個 key-value 形式的上下文資訊儲存機制,專門用於在分散式系統的服務間傳遞業務相關的 metadata。與 SpanContext 不同,Baggage 不是用來追蹤 span 之間的關係,而是用來攜帶對業務邏輯有意義的額外資訊。
讓我們來盤點一下這兩者的差異:
SpanContext(追蹤資訊):
Baggage(業務資訊):
還記得昨天我們介紹到 W3C 協議,Baggage 透過與 W3C Trace Context 類似的標準化方式進行跨服務傳播。當 HTTP 請求在不同服務間傳遞時,Baggage 資訊會透過專門的 HTTP header 來傳遞:
baggage header:
key1=value1,key2=value2,key3=value3
user_id=12345,region=asia-pacific,experiment_group=feature-a
GET /api/orders HTTP/1.1
Host: order-service.example.com
traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
baggage: user_id=12345,region=asia-pacific,experiment_group=feature-a
由於 Baggage 會在網路中傳輸,使用時需要注意以下幾點:
以下是使用 Python SDK 操作 Baggage 的完整範例:
from opentelemetry import baggage, trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor, ConsoleSpanExporter
# 初始化 Tracer
trace.set_tracer_provider(TracerProvider())
tracer = trace.get_tracer(__name__)
span_processor = SimpleSpanProcessor(ConsoleSpanExporter())
trace.get_tracer_provider().add_span_processor(span_processor)
# 在服務入口設定 Baggage
with tracer.start_as_current_span("user-request") as span:
# 設定業務上下文到 Baggage
ctx = baggage.set_baggage("user_id", "12345")
ctx = baggage.set_baggage("region", "asia-pacific", context=ctx)
ctx = baggage.set_baggage("experiment_group", "feature-a", context=ctx)
# 呼叫下游服務
with tracer.start_as_current_span("call-downstream-service", context=ctx):
# 在任何地方都可以取得 Baggage 資訊
user_id = baggage.get_baggage("user_id")
region = baggage.get_baggage("region")
experiment = baggage.get_baggage("experiment_group")
print(f"處理用戶 {user_id} 在 {region} 的請求,實驗組別:{experiment}")
# 將 Baggage 資訊加入到 span attributes
span.set_attribute("user.id", user_id)
span.set_attribute("user.region", region)
span.set_attribute("experiment.group", experiment)
Baggage 本身不會自動成為 span 的 attributes,需要明確地將 Baggage 資訊複製到 span 中:
def add_baggage_to_span(span):
"""將重要的 Baggage 資訊加入到當前 span"""
user_id = baggage.get_baggage("user_id")
region = baggage.get_baggage("region")
if user_id:
span.set_attribute("business.user_id", user_id)
if region:
span.set_attribute("business.region", region)
Baggage 本身雖然可以讓我們很好地攜帶上下文,但是,也不是任何資訊都適合放在 baggage 中傳輸,除了安全性的考量以外,過於龐大的 baggage 資訊可能也會影響到傳輸上的效能。
# 好的做法:精簡且有意義的資訊
ctx = baggage.set_baggage("user_tier", "premium")
ctx = baggage.set_baggage("region", "us-west", context=ctx)
# 避免的做法:過多或敏感的資訊
ctx = baggage.set_baggage("user_password", "secret123") # 敏感資訊
ctx = baggage.set_baggage("large_data", "..." * 1000) # 資料過大
Baggage 為 OpenTelemetry 補足了業務上下文傳遞的能力,讓我們不僅能夠追蹤技術層面的請求流程,更能在整個分散式系統中保持業務層面的上下文資訊。透過適當的使用,Baggage 能夠讓 traces、logs 和 metrics 包含更豐富的業務語義,提升可觀測性的價值。
OpenTelemetry Python SDK - Baggage API