Opentelemetry Metrics API
注:為了理解的一致性,本文檔將使用SDK規定的術語,不做翻譯。注意區分Measurements和instrument的區別,前者指的是度量數據,后者是一個工具
概覽
OpenTelemetry Metrics API用於捕獲計算機程序運行期間的產生的度量數據。Metrics API是專門為處理原始度量數據而設計的,旨在(高效,同步)持續地生成這些度量數據的摘要。下面"API"特指OpenTelemetry Metrics API。
API通過幾種不同性能級別的調用規范來提供捕獲原始度量數據的功能。無論是哪種調用規范,都將metric event定義為捕獲新度量時發生的邏輯事件,SDK會將該時刻作為一個隱式的時間戳。
這里的"semantic"或"semantics"指的是,當在API下發生metric event時,如何賦予這些metric event意義。下面將描述一個標准實現來幫助理解相關的含義,標准實現會對每種類型的metric event執行聚合操作。
監控和告警通常會使用metric event提供的經聚合以及類型轉換后的數據。但metric event還有許多其他用途,如記錄聚合或tracing和logging系統中的原始度量數據。為此,Opentelemetry要求將API和SDK分開,這樣就可以在運行期間配置不同的SDK。
在沒有安裝SDK情況下的API行為
在沒有安裝Metrics SDK的情況下,Metrics API僅包含無操作(no-ops)的功能函數,即所有對API的調用都不會產生任何影響。Meters必須返回指標的無操作實現。從用戶的角度看,對這些API的調用將不會產生任何錯誤,可以直接忽略返回值。當調用這些API時,API不能拋出異常。
Measurements
術語"捕獲"用於描述當用戶給API傳入一個度量時產生的動作。捕獲的結果取決於配置的SDK,如果沒有安裝SDK,默認對捕獲的事件不做任何響應。這種做法的目的是為了傳達"度量時可能發生任何事",具體取決於SDK,同時也意味着用戶已在采用某種度量。出於性能和語義方面的原因,API允許用戶在兩種度量方法中進行選擇(adding和grouping)。
術語"adding"用於指定添加某些度量的特征,意味着只有總和才被認為是有意義的信息。這些度量值可以用算術加法組合起來,通常是某個值的實際數量(例如字節數)。
當一組值(也稱為總體)被假定為有用的信息時,就可以使用grouping(下稱分組)度量。一個分組度量要么無法使用算法加法組合(如請求延時)的度量;要么是雖然可以使用加法,但目的是監視值的分布(例如,隊列大小)。中值被認為是分組測量的有用信息。
分組instruments在語義上會比adding instruments捕獲更多的信息,但在定義上,分組度量要比adding度量開銷大。通常用戶只需選擇adding instruments ,除非希望通過額外的開銷來獲取更多的信息。
Metric Instruments
一個metric instrument指API用於捕獲原始度量數據的設備。下面列出了標准的instruments,每種instrument都有各自的用途。API有意避免使用改變instrument語義解釋的可選特性,而更傾向於通過instruments提供單一方法,且每種方法都有固定的解釋。
API捕獲的所有度量都與使用度量的instrument相關聯,instrument會賦予其語義屬性。通過調用Meter
API可以創建和定義instrument,該API是面向用戶的SDK入口點。
通過如下方式類區分不同的Instruments :
- 同步性:synchronous instrument由用戶在分布式上下文中調用(例如,關聯的span、Baggage等)。asynchronous instrument沒有上下文,由SDK在每個收集間隔內調用。
- Adding vs Grouping:一個adding指標用於記錄adding度量,與上面描述的grouping instrument相反
- 單調性:一個單調instrument也是一個adding instrument,計算出的和的級數是非遞減的。單調instrument對於監控頻率信息很有用。
下面的metric instrument展示了其同步,adding以及/或單調性:
下表中的"No"表示該instrument為另外一個屬性,如SumObserver的同步(Synchronous)為
No
,則表示它是異步的。
Name | Synchronous | Adding | Monotonic |
---|---|---|---|
Counter | Yes | Yes | Yes |
UpDownCounter | Yes | Yes | No |
ValueRecorder | Yes | No | No |
SumObserver | No | Yes | Yes |
UpDownSumObserver | No | Yes | No |
ValueObserver | No | No | No |
synchronous instruments對於在分布式環境中(即,關聯的span,Baggage等)收集的度量很有用。asynchronous instruments用於周期性采集開銷比較大的度量信息。更多參見下文的同步和異步instrument的特性。
同步和異步adding指標有一個明顯的差異:synchronous instruments用於捕獲sum的變化,而asynchronous instruments直接捕獲sum。更多參見下文的adding指標的特性。
單調adding instruments非常重要,因為它們支持頻率計算。更多參見下文選擇metric指標。
instrument定義描述了instrument的一些屬性,包含名稱和類型。其他屬性則是可選的,包含描述和度量單位。一個instrument描述與它產生的數據相關。
標簽(labels)
標簽指與metric event相關聯的key-value屬性,與tracing API的Span屬性類似。每個標簽都會對metric event進行分類,允許對事件進行過濾和分組,然后進行分析。
每個instrument 調用規范(見下)都會使用一組標簽作為一個參數,這組標簽定義了一個唯一的key-value映射。通常傳遞給API的標簽格式為key:value,在這種情況下,規范規定通過獲取列表中出現的最后一個value來解析key的重復項。
synchronous instrument的度量通常與具有完全相同標簽集的其他度量進行組合,從而優化度量結果。更多參見下文的通過聚合合並度量。
Meter接口
API定義了一個Meter
接口,該接口包含一組instrument構造函數,以及一種以語義上的原子方式捕獲批量度量數據的工具。
第三方代碼可以使用全局的Meter
實例來嵌入第三方代碼。使用該實例允許代碼靜態地初始化metric instruments,而無需顯示注入依賴。在應用安裝SDK並通過服務的provider接口或其他特定語言支持的方式初始化全局Meter實例前,該實例僅作為一個無操作(no-op)的實現。注意,可以不使用全局實例:支持同時運行Opentelemetry SDK的多個實例。
作為一個強制性步驟,API要求調用者提供獲得一個Meter
實現時提供 instrumenting library 的名稱(可選版本)。庫名用於標識由該庫產生的工具,可以用於禁用工具,配置聚合以及應用采樣策略等目的。更多細節可以參見TracerProvider。
聚合
Aggregation 指將程序運行的一段時間內產生的多個度量合並為確切的或估計的統計信息的過程。
每個instrument 都指定了一個符合該instrument 語義的默認聚合,用於解釋其屬性並讓用戶了解如何使用聚合。在沒有任何配置覆蓋的情況下,默認聚合提供了一種開箱即用的方式。
adding instruments (Counter
, UpDownCounter
, SumObserver
, UpDownSumObserver
)默認使用Sum聚合。計算Sum聚合的詳細信息各不相同,但從用戶的角度看,它可以用於監控捕獲的數值的總和。同步和異步instruments 的區別在於如何指定exporter的工作方式,更多參見SDK specification (WIP)。
ValueRecorder
指標默認使用TBD issue 636 聚合。
ValueObserver
指標默認使用LastValue 聚合。這種聚合會持續觀測最后一個值,及其時間戳。
還有其他標准的聚合方式,特別對於分組instruments,通常會傾向於獲取不同的摘要信息,如直方圖,分位數總結,基數估計和其他類型的概要數據結構。
默認的Opentelemetry SDK實現了一個Views API (WIP),支持在單個instrument層面配置非默認的聚合行為。雖然Opentelemetry SDK可以配置為以非標准方式處理instrument,但仍用戶仍希望可以根據其語義來選擇對應的instrument(使用默認的聚合進行解釋)。
時間
時間是metric events的基本屬性,但並不需要明確給出。用戶不需要給metric events提供時間戳。不建議SDK捕獲每個事件的當前時間戳(通過讀取時鍾),除非明確需要計算每個事件的高精度時間戳。
這種不需要實現的需求源自對metric報告的優化,即配置一個相對短的周期(如1秒)來采集metric數據,並使用一個時間戳來描述一批暴露出來的數據(當跨分鍾或小時進行聚合時,時間精度並不是非常重要)。
聚合通常是在一個連續時間內的一系列事件上進行計算的,這段時間被稱為采集間隔。由於SDK控制何時進行采集,因此可以采集聚合的數據,但僅需要在每個采集間隔讀取一次時鍾。默認的SDK采用了這種方式。
使用synchronous instruments產生的metric events是立即發生的,這些落入采集間隔的數據會與其他使用相同instrument和標簽集的事件一起聚合。由於各個事件都可能同時發生,因此無法很好地定義最近的事件。
Asynchronous instruments允許SDK通過在每次收集間隔執行一次觀察的方式來評估metric instrument 。由於與采集耦合(與synchronous instruments不同),因此這些instruments明確定義了最近的事件。例如將針對某個時刻使用相同的instrument和標簽集的Last Value定義為最近一次采集間隔內測得的值。
由於metric events的時間戳是隱式的,因此可以將一些列的metric events看作是一個時間序列事件。但在SDK規范中保留了這個術語,它指代數據格式的一部分,這些數據格式以序列的方式顯式地表示帶有時間戳的值,這些值是一段時間內的原始度量的聚合結果。
Metric Event格式
無論哪種類型的instrument,其Metric events具有相同的邏輯表達。通過instrument捕獲的metric事件包含:
- 時間戳(隱式)
- instrument定義(名稱,類型,描述,度量單位)
- 標簽集(key和value)
- 值(有符號整數或浮點數)
- 啟動時與SDK相關的資源
同步事件還有一個額外的屬性,即處於活動狀態的分布式上下文(包含Span,Bagage等)。
Meter provider
通過初始化和配置OpenTelemetry Metrics SDK,可以獲得具體的MeterProvider
實現。本文不會指定如何構造一個SDK,僅說明這些SDK必須實現MeterProvider
。一旦配置完成,應用或庫會選擇是否使用MeterProvider
接口的全局實例,或使用依賴注入來更好地配置provider。
獲取一個Meter
可以通過MeterProvider
及其GetMeter(name, version)
方法來創建一個新的Meter
實例。通常會將MeterProvider
作為單實例使用。實現時應該提供一個全局默認的MeterProvider
。GetMeter
方法包含兩個字符串參數:
- name(必要):name用於標識 instrumentation library(如
io.opentelemetry.contrib.mongodb
)。當指定了無效的名稱(null或空字符串)時,將返回一個默認的Meter
實現,不應該返回null或拋出異常。如果應用所有者將SDK配置為禁用該庫生成的遙測,則此時MeterProvider
可以返回一個無操作(no-op)的Meter
。 - version(可選):指定instrumentation library的版本(如
1.0.0
)。
每個不同名稱的Meter
都會為它的metric instrument創建一個獨立的命名空間,這樣instrumentation library就可以使用相同的instrument名稱來報告metric。不建議將Meter
的名稱作為instrument名稱的一部分,因為這樣會導致instrumentation library無法使用相同的名稱來捕獲metrics。
全局Meter provider
很多場景下,使用全局實例看起來像是一個相反的模式(單例模式)。但在大多數場景下,為了在不使用依賴注入的情況下合並來自相互依賴庫的遙測數據,這反而是一種正確的模式,出於這種原因,Opentelemetry 語言對應的API應該提供一個全局實例。各個語言提供的全局實例必須保證通過全局MeterProvider
分配的Meter
以及通過這些Meter實例分配的instrument的初始化推遲到全局SDK首次初始化時。
獲取全局的MeterProvider
由於全局MeterProvider
是單例的,且僅支持一個單獨的方法,調用者可以使用全局GetMeter
的來獲取全局的Meter
。例如,global.GetMeter(name, version)會在全局MeterProvider
上調用GetMeter
,並返回一個命名的Meter
實例。
設置全局的MeterProvider
全局的函數會安裝一個MeterProvider作為全局SDK,例如在初始化之后使用global.SetMeterProvider(MeterProvider)
安裝SDK。
instrument屬性
由於API與SDK是划分開的,具體的實現將決定metric event是如何被處理的。因此,應該按照語義和預期的解釋來選擇instrument。通過多種屬性來定義單個instrument的語義和細節,可以幫助選擇合適的instrument。
instrument命名要求
metric instrument主要通過名稱進行定義,這也是外部系統引用的方式。metric名稱遵循如下語法:
- 非空字符串
- 大小寫不敏感
- 第一個字符必須是非數字,非空格,非標點
- 后續的字符必須使用字母數字,'_', '.', 和'-'。
metric instrument屬於一個命名空間,通過相關的Meter
實例進行創建。當多個instrument使用相同的名稱進行注冊時,Meter
必須返回錯誤。
Metric instrument名稱應該具有語義意義,它獨立於原始的Meter名稱。例如,當監測一個http server時,"latency"並不是一個合適的instrument名稱,因為它代表的意義太寬泛。相反,應該采用如"http_request_latency"這樣的instrument,通過名稱可以告知觀察者延遲測量的語義。多個 instrumentation libraries可能生成該instrument。
同步和異步instruments比較
Synchronous instruments是在請求中調用的,這意味着它們有一個關聯的分布式上下文(包括Span、Baggage等)。在一個給定的采集間隔內,可能有多個metric event對應一個Synchronous instruments。
Asynchronous instruments 通過回調函數(在每次采集間隔時)進行報告,缺少上下文。每個周期每個標簽組只能報告一個值。如果針對相同的標簽集,應用觀察到了多個值,則會僅會保留最后一個值。
Adding和分組instruments比較
Adding instruments 用於捕獲有關總和的信息,根據定義,只有總和才有意義。對於這些instruments 來說,單獨的事件是沒有意義的。例如兩個Counter
事件Add(N)
和Add(M)
等價於一個Counter
事件Add(N + M)
。這是因為Counter
是同步的,且synchronous adding instruments會將捕獲到的度量進行加法運算。
Asynchronous adding instruments(如SumObserver
)用於直接捕獲總和。如,對於給定的instrument 和標簽集,在SumObserver
觀察序列中,Last Value定義了instruments的和。
同步和異步場景下,每個采集間隔內都會將adding instruments以低成本的方式聚合成單個數字,而不會丟失信息。這種屬性使得adding instruments相比分組instruments具有更高的性能。
與記錄完整的數據相比,默認情況下分組instruments會使用一種相對低廉的聚合方式。但仍然比默認的adding instruments(Sum)開銷大。與只關心sum的adding instruments不同,分組instruments可以配置開銷更大的聚合器。
單調和非單調instruments比較
單調性僅適用於adding instruments。Counter
和SumObserver
被定義為是單調的,因為它們的和是非減的。 以UpDown-
開頭的instrument是非單調的,意味着總和可以增加,減少,或保持不變。
單調instruments通常用於捕獲關於和的信息,這些信息用於監控速率變化。本API定義的單調屬性指非遞減的和。Metric API不考慮非遞增和。
函數名稱
每個instrument都支持一個函數,對函數的命名應該傳達instrument的語義。
Synchronous adding instruments支持Add()
函數,意味着這些instruments 會計算和,不會直接捕獲和(sum)。
Synchronous grouping instruments支持Record()
,意味着這些instruments 捕獲單個事件,不僅僅是和(sum)。
所有的asynchronous instruments都支持Observe()
函數,意味着,每個度量間隔僅會捕獲一個值。
instrument
Counter
Counter
是最常用的synchronous instrument,該instrument支持通過Add(increment)
函數來報告和,僅限非負增量。與所有adding instrument相同,默認的聚合為Sum
。
Counter
的例子如:
- 計算接收的字節數
- 計算完成的請求數
- 計算創建的賬戶數
- 計算檢查點數
- 計算5xx錯誤數
上述示例instruments對於監控這些這些場景下的的速率變化很有用。在這些場景中,通常會報告一個sum是如何改變的,而非根據每次度量進行計算和報告sum。
UpDownCounter
UpDownCounter
類似Counter
,不同之處在於Add(increment)
支持負增長。這使得UpDownCounter
不適用於計算速率聚合。該instrument可以通過Sum
進行聚合,但結果是非單調的。通常用於計算使用資源的總數,或請求的上升和下降。
UpDownCounter
的例子為:
- 計算活動的請求數
- 計算使用new和delete的內存
- 計算
enqueue
和dequeue
的隊列長度 - 計算信號量的
up
和down
操作。
這些例子監控一組進程的資源使用情況。
ValueRecorder
ValueRecorder
是一個grouping synchronous instrument ,用於記錄所有分組數字(正數或負數)。Record(value)
捕獲的值被認為屬於一個正在匯總的分布中的單個事件。當捕獲總和沒有意義的度量值時,或雖然捕獲的數字本身就是adding的,但認為單獨的數值增長更具有現實意義時,應該選擇ValueRecorder
。
最常使用ValueRecorder
的場景是捕獲延時度量。延時度量並沒有adding的意義,因此沒有必要知道所有處理的請求的延遲總和。使用`ValueRecorder instrument 捕獲延時度量通常是因為對平均值、中位數和其他個別事件的匯總統計數據感興趣。
ValueRecorder
的默認聚合會計算最小和最大值,事件值的總和以及事件的總數,允許監控輸入值的速率、平均值和范圍。
使用ValueRecorder
的例子為:
- 捕獲各種類型的時間信息
- 捕獲飛行員所經歷的加速
- 捕獲噴油器的噴嘴壓力
- 捕捉MIDI按鍵的速度
ValueRecorder
捕獲度量中用到的adding僅僅是累加,但最終關注的是數值的分布,而不僅僅是總和:
- 捕獲請求大小
- 捕獲賬戶負載
- 捕獲隊列大小
- 捕獲一些板英尺的木材。
這些例子展示了,雖然這些度量是可以adding的,但選擇ValueRecorder
而不是Counter
或UpDownCounter
意味着關心的維度不僅限於總和。如果不關心采集到的分布情況,那么可以選擇后者的某一個。使用ValueRecorder
捕獲分布情況對於觀察性設置非常重要。
使用這類instrument需要注意,它們比adding度量開銷大。
SumObserver
SumObserver
是Counter對應的asynchronous instrument,用於通過 Observe(sum)
捕獲單調和。Sum的名字可以提醒用戶,該instrument用於直接捕獲和。使用SumObserver
來捕獲從0開始並在整個處理生命周期中上升且不會下降的所有值。
SumObserver
的例子為:
- 捕獲處理用戶/系統的CPU秒數
- 捕獲緩存miss數
當計算一個度量的開銷比較大時,可以選擇使用SumObserver
(這樣就不會因為每個請求而浪費計算資源)。例如,一個系統調用需要捕獲處理使用的CPU,因此需要周期性地進行采集,而不是針對每個請求都進行采集。當通過測量單個變更來計算總和是不切實際或比較浪費資源的情況下,也可以使用SumObserver
。例如,即使緩存miss的和是單個緩存miss事件的總和,但使用Counter
同步捕獲每個事件會浪費大量資源。
UpDownSumObserver
UpDownSumObserver
是對應UpDownCounter
的asynchronous instrument。可以通過Observe(sum)
捕獲非單調總數。使用UpDownSumObserver
來捕獲從0開始並在整個處理生命周期中上升和下降的任何值。
UpDownSumObserver
的例子為:
- 捕獲進程堆大小
- 捕獲活動的分片數
- 捕獲開始的/完成的請求數
- 捕獲當前隊列大小
選擇UpDownSumObserver
而不選擇同步UpDownCounter
的原因與選擇SumObserver
而不選擇同步Counter的原因相同。如果計算一個度量的開銷比較大,或對應的變更非常頻繁,那么使用UpDownSumObserver
來進行度量比較現實。
ValueObserver
ValueObserver
是對應ValueRecorder
的asynchronous instrument,可以通過Observe(value)
捕獲一部分組度量。這些instrument通常用於捕獲比較難以計算的度量,因為它可以讓SDK控制評估的頻率。
ValueObserver
的例子為:
- 捕獲CPU風扇速度
- 捕獲CPU溫度
注意這些例子都使用分組度量。在上面的ValueRecorder
場景中,給出了在請求期間捕獲同步adding度量的示例(例如,請求看到的當前隊列大小)。那么在異步場景中,用戶如何決定選擇ValueObserver
還是UpDownSumObserver
。
考慮一個場景,如何異步地報告一個隊列的大小。邏輯上,ValueObserver
和UpDownSumObserver
都可以應用於這種場景。asynchronous instrument只會在每個采集間隔采集一個度量。這種情況下,UpDownSumObserver
會報告一個當前的總和,而ValueObserver
會報告一個當前的總和(等於最大和最小),且總數為1。如果沒有聚合,此時的結果是相等的。
當只有一個數據點時,定義默認聚合並沒有什么意義。默認的聚合在執行空間聚合時才會有用,意思是跨標簽集或在分布式設置中合並測量。雖然一個ValueObserver
在每個采集間隔僅觀測一個值,但默認的聚合將指定如何將它與其它值進行聚合,而無需其他配置。
因此,當考慮應該選擇ValueObserver
還是UpDownSumObserver
時,建議選擇擁有更合適的默認聚合的instrument。如果想要觀測一組機器上的隊列大小,且僅關心聚合的隊列大小,那么可以選擇SumObserver
,該asynchronous 會生成一個總和,而不是分布。如果想要觀測一組機器上的隊列大小,且對這些機器上的隊列大小分布比較感興趣,那么可以選擇ValueObserver
。
Interpretation
那么這些instrument 最根本的不同是什么,為什么只有三種,為什么不是一種或十種?
從前面可以看到,這些instrument按照同步,adding,和/或單調進行了分類。這種方式給予每個instrument特殊的語義,通過這種方式提升了metric event的性能和解釋性。
建立不同類型的instrument 是非常重要的,因為大部分場景下,SDK會提供默認的開箱即用的功能,而無需配置其他行為。對instrument的選擇不僅僅取決於事件的含義,也取決於用戶調用的函數的名稱。函數名Add()
,用於adding instrument,Record()
用於grouping instrument,Observe()
用於asynchronous instrument,各自傳達了這些動作的意義。
單個instrument的屬性和標准實現描述如下:
Name | Instrument kind | Function(argument) | Default aggregation | Notes |
---|---|---|---|---|
Counter | Synchronous adding monotonic | Add(increment) | Sum | Per-request, part of a monotonic sum |
UpDownCounter | Synchronous adding | Add(increment) | Sum | Per-request, part of a non-monotonic sum |
ValueRecorder | Synchronous | Record(value) | TBD issue 636 | Per-request, any grouping measurement |
SumObserver | Asynchronous adding monotonic | Observe(sum) | Sum | Per-interval, reporting a monotonic sum |
UpDownSumObserver | Asynchronous adding | Observe(sum) | Sum | Per-interval, reporting a non-monotonic sum |
ValueObserver | Asynchronous | Observe(value) | LastValue | Per-interval, any grouping measurement |
TIPs:
注意其默認的聚合模式,sum類型的聚合模式會對給出的數值進行累加,適用於通過exporter進行數值的累加計算。如果用戶已經給出了結果,則可以使用ValueObserver直接獲取該值,不進行聚合計算。
構造器
Meter
接口支持創建並注冊新的metric instrument。instrument構造器是通過在它構造的instrument類型上添加一個New-
前綴來命名的,使用構造器模式或該語言中的其他慣用方法。
在本規范中,每種instrument至少有一個構造函數。例如,如果需要專門處理整數和浮點指針數,則OpenTelemetry API將為每種instrument類型提供2個構造函數。
將instruments綁定到單個Meter
實例有兩個好處:
- 可以在首次使用之前就從零狀態導出instruments,而無需顯式的注冊調用
- 二進制名稱和版本與metric event隱式地關聯起來
一些現有的metric系統支持靜態地分配metric instruments,並在使用時提供等價的Meter
接口。在一個典型的statsd客戶端示例中,現有的代碼可能無法在一個方便的地方存儲新的metric instruments。如果這種方式是一種負擔,則建議使用全局MeterProvider
構造一個靜態Meter
,並構造和使用全局范圍的metric instruments。
上述場景類似使用現有Prometheus客戶端的用戶,此時可以使用全局Registerer
分配instruments。這類代碼可能無法在定義instruments的地方訪問一個合適的MeterProvider
或Meter
實例。如果成為一種負擔,建議通過全局Meter provider構建出的靜態命名的Meter
來構建metric instruments。
應用希望構建長生命的instruments。SDK中的instruments是永久的,沒有方法刪除。
標簽集
語義上,一組標簽為一個唯一的字符串key到value的映射。在整個API中,必須以相同的慣用格式來傳遞一組標簽。通常的表達方式包括一個順序的key:value列表,或一個key:value的映射。
當標簽一一個順序的key:value列表進行傳遞時,如果發現重復的key,則對任何給定的key,將使用列表中的最后一個value來構造唯一的映射。
標簽值的類型通常被exporters假定為字符串,盡管作為語言級別的決策,但標簽值類型可以是該語言中具有字符串表示形式的任何慣用類型。
用戶不需要提前聲明將會被API的metric instruments使用的標簽key集。用戶在調用API時可以為任何metric event設置任何標簽。
標簽性能
在整個metric數據的生成中,對標簽的處理是一個很大的成本。
SDK對處理聚合的支持取決於查找instrument-標簽集組合對的活動記錄的能力。這種方式允許對度量進行組合。可以通過使用綁定synchronous instruments和批量報告函數 (RecordBatch
, BatchObserver
)來降低標簽的開銷。
可選:有序列表
作為語言級別的決策,API可能會支持標簽key排序。這種情況下,用戶可能會指定有序的標簽keys序列,用於從一個有序的標簽values序列中創建一個無序的標簽集,例如:
var rpcLabelKeys = OrderedLabelKeys("a", "b", "c")
for _, input := range stream {
labels := rpcLabelKeys.Values(1, 2, 3) // a=1, b=2, c=3
// ...
}
這種方式被指定為一種語言可選的特性,因為它的安全性以及作為監控輸入的值取決於源語言中的類型檢查。傳遞無序的標簽(即從key到value的映射)被認為是更安全的選擇。
Synchronous instrument細節
下面介紹Synchronous instrument的細節。
同步調用規范
metric API提供了三種語義上等價的方法來使用Synchronous instruments捕獲度量:
- 調用綁定的instruments,它們具有一組預先關聯的標簽
- 直接調用instruments,傳入相關的一組標簽
- 使用一組標簽來批量記錄多個instruments的度量
三種方式都會生成相同的metric events,但在性能和便捷度上不盡相同。
metric API的性能取決於輸入新度量所做的工作,通常主要是處理標簽產生的開銷。綁定instruments是性能最高的調用規范,因為它們可以將處理標簽的成本分攤到許多用途上(首先固定一組標簽,然后應用到多個度量上)。通過RecordBatch()
記錄多個度量值是提高性能的一個不錯的選擇,因為處理標簽的成本分布到了多個度量值上(其實就是多個度量使用了一組標簽,這樣處理一組標簽就可以滿足多個度量的要求,通過這種方式降低了開銷)。直接調用規范是最方便的,但也是通過API輸入度量使用的性能最低的調用規范。
綁定instrument的調用規范
當兼顧到性能要求,且一個metric instrument重復使用了相同的標簽集時,開發人員可能會選擇使用綁定instrument調用規范來進行優化。對於綁定instrument,它要求重復使用特定的instrument和標簽。如果帶有相同標簽的instrument多次被使用,通過獲取與標簽相對應的綁定instrument,可以達到最高的性能。
為了綁定一個instrument,可以使用Bind(labels...)
方法來返回支持同步API的接口(即Add()
或Record()
)。綁定instrument不需要通過標簽進行調用,因為與metric event對於的標簽已經綁定到了instrument上。
綁定instrument會提升性能,但也會消耗SDK中的資源。綁定instrument必須提供一個Unbind()
方法來讓用戶取消綁定,並釋放相關的資源。注意Unbind()
不會暗示刪除時間戳,僅保證SDK在沒有等待處理更新后忘記timeseries的存在。
例如,使用相同的標簽重復更新一個counter:
通過給instrument綁定預先定義的標簽,后續使用instrument時將不會使用標簽
func (s *server) processStream(ctx context.Context) {
// The result of Bind() is a bound instrument
// (e.g., a BoundInt64Counter).
counter2 := s.instruments.counter2.Bind(
kv.String("labelA", "..."),
kv.String("labelB", "..."),
)
defer counter2.Unbind()
for _, item := <-s.channel {
// ... other work
// High-performance metric calling convention: use of bound
// instruments.
counter2.Add(ctx, item.size())
}
}
直接instrument調用規范
當使用方便比性能更重要時,或事先不知道值時,用戶可能會直接操作metric instruments,意味着需要在調用時提供標簽。這種方式提供了最好的便利性。
例如,更新一個counter:
func (s *server) method(ctx context.Context) {
// ... other work
s.instruments.counter1.Add(ctx, 1,
kv.String("labelA", "..."),
kv.String("labelB", "..."),
)
}
直接調用非常方便,因為無需申請和保存綁定的instrument。當一個instrument很少被使用,或很少使用相同的標簽時就可以使用這種方式。與綁定instrument不同,使用直接調用規范不會長期消耗SDK資源。
RecordBatch 調用規范
這是最后一個輸入度量的調用規范,與直接調用規范類似,但同時支持多個度量。RecordBatch
API支持輸入多個度量,意味着對多個instruments進行語義上的原子更新。調用RecordBatch
可以將標簽的處理成本分攤到多個度量中。
func (s *server) method(ctx context.Context) {
// ... other work
s.meter.RecordBatch(ctx, labels,
s.instruments.counter.Measurement(1),
s.instruments.updowncounter.Measurement(10),
s.instruments.valuerecorder.Measurement(123.45),
)
}
另外一個用於record batch的有效接口使用了構造器模式:
meter.RecordBatch(labels).
put(s.instruments.counter, 1).
put(s.instruments.updowncounter, 10).
put(s.instruments.valuerecorder, 123.45).
record();
使用record batch調用規范在語義上與直接調用相同,只是增加了原子性。由於通過單個調用來輸入值,從exporter的角度來看,SDK能夠實現原子更新,因為SDK可以以隊列的方式實現單次批量更新,或者僅使用一次鎖。與直接調用相同,使用批量調用規范不會長期消耗SDK資源。
與分布式上下文進行關聯
Synchronous measurements在運行時隱式地與分布式上下文關聯,其中可能包括Span和Baggage項。Metric SDK可以以多種方式使用此信息,這是OpenTelemetry中特別關注的一個功能。
Baggage into metric labels
OpenTelemetry 支持的Baggage 用於在分布式計算中將標簽從一個進程傳遞到下一個進程。有時會使用分布式baggage項作為metric標簽來聚合度量數據。
必須通過顯式的配置才能使用Baggage,使用Views API (WIP)選擇特定的baggage項作為標簽。由於使用Baggage標簽的開銷非常大,默認的SDK不會在export pipeline中自動使用Baggage標簽。
配置應用於Baggage標簽的視圖的相關工作正在進行中
Asynchronous instrument
下面介紹Asynchronous instrument的細節。
異步調用規范
metrics API提供了兩種語義上等價的方法來使用asynchronous instrument捕獲度量:要么通過單instrument回調,要么通過多instrument批量回調。
無論是單還是批量,asynchronous instruments只能通過一個回調進行觀測。對於null的observer回調,構造器會返回無操作的instrument。如果為asynchronous instruments指定了多個回調,則會將其視為錯誤。
每個instrument的不同標簽集不能觀察到一個以上的值。當一個instruments和標簽集觀測到多個值時,會采用最后一個觀測到的值,並丟棄之前的值,不會返回錯誤。
單instrument observer
單instrument回調會綁定到一個instrument。回調會接收一個帶有Observe(value, labels…)
函數的ObserverResult
。
func (s *server) registerObservers(.Context) {
s.observer1 = s.meter.NewInt64SumObserver(
"service_load_factor",
metric.WithCallback(func(result metric.Float64ObserverResult) {
for _, listener := range s.listeners {
result.Observe(
s.loadFactor(),
kv.String("name", server.name),
kv.String("port", listener.port),
)
}
}),
metric.WithDescription("The load factor use for load balancing purposes"),
)
}
Batch observer
BatchObserver
回調支持在一個回調中觀測多個instruments。它會接收一個帶有Observe(labels, observations...)
函數的BatchObserverResult
。
通過在asynchronous instrument上調用Observation(value)
來返回觀測值。
func (s *server) registerObservers(.Context) {
batch := s.meter.NewBatchObserver(func (result BatchObserverResult) {
result.Observe(
[]kv.KeyValue{
kv.String("name", server.name),
kv.String("port", listener.port),
},
s.observer1.Observation(value1),
s.observer2.Observation(value2),
s.observer3.Observation(value3),
},
)
s.observer1 = batch.NewSumObserver(...)
s.observer2 = batch.NewUpDownSumObserver(...)
s.observer3 = batch.NewValueObserver(...)
}
異步觀測結果構造當前值集合
允許asynchronous instrument回調對針對每個instrument、每個不同的標簽集、每個回調調用來觀測一個值。一個回調調用記錄的一組值代表該instrument的當前快照。 這組值定義了直到下一個采集間隔前,該instrument的Last Value。
asynchronous instrument需要對它認為是“當前”的每個標簽集記錄一個觀察結果,意味着即使在上次回調調用結束后,某個值沒有任何變動,異步調用也能夠觀測到它。不觀測某個標簽集意味着其對應的值不再是當前值。如果在采集間隔中未觀察到Last Value,則該值將不再是當前的值,因此該值將變得不確定。
可以在asynchronous instrument中定義Last Value,因為SDK會協調采集,並報告所有的當前值。另外一個對該屬性的解釋為,SDK可以在內存中保留一個觀察值的采集間隔值,用於查找任何instrument和標簽集的當前Last Value。通過這種方式,asynchronous instrument支持查詢當前值,不依賴於采集間隔的持續時間,而使用單點時間采集的數據。
回顧一下,Last Value並不是為asynchronous instrument定義的,它是一個精確的數值,因為並沒有很好地定義"當前"的概念。為了確定asynchronous instrument記錄的最后一個值,需要檢查采集數據的窗口,因為並沒有機制來保證每個間隔都會記錄當前值。
asynchronous instrument定義時間比例
上述asynchronous instrument的當前集合的概念同樣使用於監控比率。當一種instrument的一組觀測值加起來是一個整體時,那么可以使用觀測值除以相同間隔內采集的的觀測值之和來計算其相對貢獻。通過這種方式定義了當前相對貢獻,計算方式與收集間隔時間無關,得益於asynchronous instrument的屬性。
並行
對於支持並行執行的語言,Metrics API提供了特定的保證和安全性。但並不是所有的API函數都可以被安全地並發執行:
MeterProvider - 所有函數都可以並發執行
Meter - 所有函數都可以並發執行
Instrument - 所有指標的所有方法都可以並發執行
Bound Instrument - 所有綁定指標的所有方法都可以並發執行
相關的工作
如下工作正在進行中
Metric Views
API不支持為metric Instrument配置聚合。
View API定義為一個SDK機制的接口,該機制支持配置聚合,包括應用了哪些操作符(sum、p99、last-value等)以及使用了哪些維度。
參見 current issue discussion on this topic 和 current OTEP draft.
OTLP Metric 協議
如上所述,OTLP協議旨在以無內存的方式導出metric標准數據。 該協議的幾個細節正在制定中。 參見current protocol。
Metric SDK默認實現
OpenTelemetry SDK 默認支持metric API,默認SDK的規范正在進行中,參見 current draft。