OpenMetrics 1.0

  • 版本: 1.0
  • 狀態: 已釋出
  • 日期: 2020 年 11 月
  • 作者:Richard Hartmann、Ben Kochie、Brian Brazil、Rob Skillington

Prometheus 於 2012 年建立,自 2015 年以來已成為雲原生可觀測性的預設選擇。Prometheus 設計的核心部分是其文字指標公開格式,稱為 Prometheus 公開格式 0.0.4,自 2014 年起穩定。在這種格式中,我們特別注意使其易於生成、易於接收,並且易於人類理解。截至 2020 年,有超過 700 個公開列出的匯出器,數量不詳的未列出匯出器,以及數千個使用此格式的原生庫整合。來自各種專案和公司的數十個接收器支援消費它。

透過 OpenMetrics,我們正在清理和收緊規範,明確目的是將其引入 IETF。我們正在記錄一個具有廣泛和有機採用的工作標準,同時引入最小的、很大程度上向後相容的、經過深思熟慮的更改。截至 2020 年,已有數十個匯出器、整合和接收器使用並優先協商 OpenMetrics。

鑑於生態系統中廣泛的採用和顯著的協調需求,對 Prometheus 公開格式 0.0.4 或 OpenMetrics 1.0 進行徹底的更改被認為超出了範圍。

注意OpenMetrics 2.0 正在開發中。請此處 瞭解如何加入 Prometheus OM 2.0 工作組。

概述

指標是一種特定的遙測資料。它們代表一組資料的當前狀態快照。它們與日誌或事件不同,後者關注的是單個事件的記錄或資訊。

OpenMetrics 主要是一種線路格式,獨立於任何特定的傳輸方式。該格式預計會被定期消費,並且在連續的公開中具有意義。

實現者必須透過對給定程序或裝置的已記錄 URL 發出簡單的 HTTP GET 請求來響應,並以 OpenMetrics 文字格式公開指標。此端點應稱為“/metrics”。實現者也可以透過其他方式公開 OpenMetrics 格式的指標,例如定期將指標集透過 HTTP 推送到操作員配置的端點。

指標和時間序列

本標準將所有系統狀態表示為數值;計數、當前值、列舉和布林狀態是常見示例。與指標相反,單個事件發生在特定時間。指標傾向於在時間上聚合資料。雖然這可能會丟失資訊,但開銷的減少是許多現代監控系統中常見的工程權衡。

時間序列是隨時間變化的資訊記錄。雖然時間序列可以支援任意字串或二進位制資料,但本 RFC 的範圍僅限於數值資料。

指標時間序列的常見示例包括網路介面計數器、裝置溫度、BGP 連線狀態和告警狀態。

資料模型

本節必須與 ABNF 節一起閱讀。如果兩者之間存在分歧,則必須優先考慮 ABNF 的限制。這減少了重複,因為必須支援文字線路格式。

資料型別

價值觀

OpenMetrics 中的指標值必須是浮點數或整數。請注意,格式的接收者可能只支援 float64。非實數值 NaN、+Inf 和 -Inf 必須被支援。NaN 不能被視為缺失值,但可以用來表示除以零的情況。

布林值

布林值必須遵循 `1==true`,`0==false`。

時間戳

時間戳必須是 Unix 紀元秒。可以使用負時間戳。

字串

字串必須僅由有效的 UTF-8 字元組成,並且可以是零長度。必須支援 NULL (ASCII 0x0)。

標籤

標籤是由字串組成的鍵值對。

以下劃線開頭的標籤名稱是保留的,除非本標準指定,否則不得使用。標籤名稱必須遵循 ABNF 部分中的限制。

空標籤值應被視為該標籤不存在。

標籤集

標籤集必須由標籤組成,並且可以為空。在標籤集中,標籤名稱必須是唯一的。

指標點

每個指標點由一組值組成,具體取決於指標族型別。

Exemplars(範例)

Exemplars 是對 MetricSet 外部資料的引用。一個常見的用例是程式跟蹤的 ID。

Exemplars 必須由一個 LabelSet 和一個值組成,並且可以有一個時間戳。它們各自可以與 MetricPoints 的 LabelSet 和時間戳不同。

Exemplar 的 LabelSet 的標籤名稱和值的總長度不得超過 128 個 UTF-8 字元程式碼點。為簡化實現並保持文字和 proto 格式之間的一致性,範例的文字表示中的其他字元(如 `",=`)不計入此限制。

接收器可以丟棄範例。

指標

指標由 MetricFamily 中的唯一 LabelSet 定義。指標必須包含一個或多個 MetricPoint 的列表。對於給定的 MetricFamily,具有相同名稱的指標應在其 LabelSet 中具有相同的標籤名稱集。

指標點不應有明確的時間戳。

如果一個指標公開了多個 MetricPoint,那麼它的 MetricPoint 必須具有單調遞增的時間戳。

指標族

一個指標族可以有零個或多個指標。指標族必須有一個名稱、HELP、TYPE 和 UNIT 元資料。指標族中的每個指標都必須有一個唯一的 LabelSet。

名稱

指標族名稱是一個字串,並且在 MetricSet 中必須是唯一的。名稱應使用蛇形命名法(snake_case)。指標名稱必須遵循 ABNF 部分中的限制。

指標族名稱中的冒號被保留,用於表示該指標族是通用監控系統的計算或聚合結果。

以下劃線開頭的 MetricFamily 名稱是保留的,除非本標準指定,否則不得使用。

字尾

MetricFamily 的名稱在 MetricSet 內的文字格式中,根據 ABNF,不得與其他 MetricFamily 的樣本指標名稱產生潛在衝突。例如,一個名為“foo_created”的 Gauge(測量器)可能會與一個名為“foo”的 Counter(計數器)在文字格式中生成一個“foo_created”。

公開者應避免使用可能與文字格式樣本指標名稱所用字尾混淆的名稱。

  • 各種型別的字尾如下:
  • 計數器: _total, _created
  • 摘要: _count, _sum, _created, `` (空)
  • 直方圖: _count, _sum, _bucket, _created
  • 儀表盤直方圖 (GaugeHistogram):`_gcount`、`_gsum`、`_bucket`
  • 資訊 (Info):`_info`
  • 儀表盤 (Gauge):``(空)
  • 狀態集 (StateSet):``(空)
  • 未知 (Unknown):``(空)
型別

Type 指定 MetricFamily 型別。有效值為 "unknown"、"gauge"、"counter"、"stateset"、"info"、"histogram"、"gaugehistogram" 和 "summary"。

單位

單位指定 MetricFamily 的計量單位。如果非空,它必須是 MetricFamily 名稱的字尾,並以一個下劃線分隔。請注意,進一步的生成規則可能會使其在文字格式中成為一箇中綴。

幫助

Help 是一個字串,並且應該非空。它用於為人類消費提供 MetricFamily 的簡要描述,並且應該足夠短,以便用作工具提示。

指標集

MetricSet 是 OpenMetrics 公開的頂級物件。它必須由 MetricFamilies 組成,並且可以為空。

每個 MetricFamily 名稱必須是唯一的。相同的標籤名稱和值不應該出現在 MetricSet 中的每個 Metric 上。

MetricSet 中不需要對 MetricFamily 進行特定排序。公開者可以讓公開內容更易於人類閱讀,例如,如果效能權衡合理,可以按字母順序排序。

如果存在,根據下文“在基於推送和基於拉取的系統中支援目標元資料”一節,名為“target”的資訊指標族應放在首位。

指標型別

Gauge(儀表盤)

Gauges(儀表盤)是當前的測量值,例如當前使用的記憶體位元組數或佇列中的專案數。對於 Gauges,絕對值是使用者感興趣的。

型別為 gauge 的指標中的 MetricPoint 必須有單個值。

儀表盤可能會隨著時間的推移增加、減少或保持不變。即使它們只朝一個方向變化,它們仍然可能是儀表盤而不是計數器。日誌檔案的大小通常只會增加,資源可能會減少,而佇列大小的限制可能是恆定的。

儀表盤可用於編碼列舉,其中列舉具有許多狀態並隨時間變化,這是最高效但使用者友好度最低的方式。

Counter(計數器)

計數器(Counters)測量離散事件。常見的例子是接收到的 HTTP 請求數、花費的 CPU 秒數或傳送的位元組數。對於計數器,使用者感興趣的是它們隨時間增加的速度。

型別為 Counter 的指標中的 MetricPoint 必須有一個名為 Total 的值。Total 是一個非 NaN 值,並且必須隨時間單調非遞減,從 0 開始。

型別為 Counter 的 Metric 中的 MetricPoint 應該有一個名為 Created 的時間戳值。這可以幫助攝取器區分新指標和之前未見過的長期執行指標。

Metric 的 Counter 型別中的 MetricPoint 的 Total 值可以重置為 0。如果存在,對應的 Created 時間也必須設定為重置的時間戳。

一個指標的計數器的 Total 值可以有一個範例。

狀態集

StateSets(狀態集)表示一系列相關的布林值,也稱為位集。如果需要編碼列舉,可以透過 StateSet 來實現。

StateSet 指標的一個點可以包含多個狀態,並且必須為每個狀態包含一個布林值。狀態有一個名稱,是字串。

StateSet 指標的 LabelSet 不得具有與其 MetricFamily 名稱相同的標籤名稱。

如果編碼為狀態集,列舉在 MetricPoint 內必須只有一個布林值為真。

這適用於列舉值隨時間變化,且狀態數量不超過少數的情況。

型別為 StateSets 的 MetricFamilies 必須有一個空的 Unit 字串。

資訊

資訊指標用於公開在程序生命週期內不應更改的文字資訊。常見示例包括應用程式的版本、修訂控制提交以及編譯器的版本。

Info 指標的 MetricPoint 包含一個 LabelSet。Info MetricPoint 的 LabelSet 不得具有與其 Metric 的 LabelSet 的標籤名稱相同的標籤名稱。

Info 可用於對值不隨時間變化的列舉進行編碼,例如網路介面的型別。

型別為 Info 的 MetricFamilies 必須有一個空的 Unit 字串。

Histogram(直方圖)

直方圖測量離散事件的分佈。常見示例包括 HTTP 請求的延遲、函式執行時間或 I/O 請求大小。

Histogram MetricPoint 必須至少包含一個桶,並且應該包含 Sum 和 Created 值。每個桶都必須有一個閾值和一個值。

直方圖 MetricPoints 必須有一個 +Inf 閾值的桶。桶必須是累積的。例如,對於一個表示請求延遲(單位為秒)的指標,其閾值為 1、2、3 和 +Inf 的桶的值必須遵循 value_1 <= value_2 <= value_3 <= value_+Inf。如果十個請求各耗時 1 秒,則 1、2、3 和 +Inf 桶的值必須等於 10。

+Inf 桶計算所有請求。如果存在,Sum 值必須等於所有測量事件值的總和。MetricPoint 內的桶閾值必須是唯一的。

從語義上講,Sum 和桶值是計數器,因此不能是 NaN 或負數。可以使用負閾值桶,但此時 Histogram MetricPoint 不得包含 Sum 值,因為它在語義上將不再是計數器。桶閾值不能等於 NaN。Count 和桶值必須是整數。

Histogram MetricPoint 應該有一個名為 Created 的時間戳值。這可以幫助攝取器區分新指標和之前未見過的長期執行指標。

直方圖指標的 LabelSet 不得有“le”標籤名。

桶值可以有範例。桶是累積的,允許監控系統出於效能/反拒絕服務的原因丟棄任何非+Inf的桶,這種方式會損失粒度,但仍然是一個有效的直方圖。

每個桶覆蓋小於或等於其值的值,並且範例的值必須在此範圍內。範例應放入值最高的桶中。一個桶不得有多個範例。

儀表盤直方圖

GaugeHistograms(儀表盤直方圖)測量當前的分佈。常見的例子是專案在佇列中等待了多長時間,或者佇列中請求的大小。

GaugeHistogram MetricPoint 必須有一個 +Inf 閾值的桶,並且應該包含一個 Gsum 值。每個桶必須有一個閾值和一個值。

GaugeHistogram 的桶遵循與 Histogram 相同的所有規則。

GaugeHistogram 的 bucket 和 Gsum 在概念上是 gauges,但是 bucket 值不能為負數或 NaN。如果存在負閾值 bucket,那麼 sum 可以為負數。Gsum 不能為 NaN。Bucket 值必須是整數。

GaugeHistogram 的 Metric 的 LabelSet 不得有 "le" 標籤名。

桶值可以有範例。

每個桶覆蓋小於或等於其值的值,並且範例的值必須在此範圍內。範例應放入值最高的桶中。一個桶不得有多個範例。

總結

摘要(Summaries)也測量離散事件的分佈,當直方圖成本過高和/或平均事件大小足夠時,可以使用摘要。

它們也可能用於向後相容,因為一些現有的儀表化庫公開預計算的分位數,並且不支援直方圖。不應使用預計算的分位數,因為分位數不可聚合,並且使用者通常無法推斷它們涵蓋的時間範圍。

Summary MetricPoint 可以包含 Count、Sum、Created 和一組分位數。

在語義上,Count 和 Sum 的值是計數器,因此不能是 NaN 或負數。Count 必須是整數。

型別為 Summary 且包含 Count 或 Sum 值的 Metric 中的 MetricPoint 應該有一個名為 Created 的時間戳值。這可以幫助攝取器區分新指標和之前未見過的長期執行指標。Created 必須與分位數值的收集週期無關。

分位數是從一個分位數到值的對映。例如,一個名為 myapp_http_request_duration_seconds 的指標中,分位數 0.95 對應值 0.2,意味著在未知的時間範圍內,第 95 百分位的延遲是 200 毫秒。如果在相關時間範圍內沒有事件,分位數的值必須是 NaN。分位數指標的 LabelSet 不能有 "quantile" 標籤名。分位數必須在 0 和 1 之間(含)。分位數值不能為負。分位數值應代表最近的值。通常這會是過去 5-10 分鐘。

未知

不應使用 Unknown。當無法從第三方系統確定單個指標的型別時,可以使用 Unknown。

未知型別指標中的一個點必須有單個值。

資料傳輸與線路格式

必須支援文字線路格式,並且是預設格式。可以支援 protobuf 線路格式,但必須在協商後才能使用。

OpenMetrics 格式是正則喬姆斯基文法,使得編寫快速小巧的解析器成為可能。文字格式壓縮效果好,而 protobuf 已經是二進位制且高效編碼的。

部分或無效的公開必須被視為整體錯誤。

協議協商

所有接收器實現必須能夠接收使用 TLS 1.2 或更高版本加密的資料。所有公開器應能夠發出使用 TLS 1.2 或更高版本加密的資料。接收器實現應能夠接收來自不使用 TLS 的 HTTP 的資料。所有實現都應使用 TLS 傳輸資料。

協商使用哪個版本的 OpenMetrics 格式是帶外的。例如,對於透過 HTTP 的拉取式公開,使用標準的 HTTP 內容型別協商,如果未請求更新版本,則必須預設為標準的最舊版本(即 1.0.0)。

基於推送的協商本質上更復雜,因為通常是公開者發起連線。生產者必須使用標準的最舊版本(即 1.0.0),除非接收者另有要求。

文字格式

ABNF

ABNF 遵循 RFC 5234 規範。

"exposition" 是 ABNF 的頂層令牌。

exposition = metricset HASH SP eof [ LF ]

metricset = *metricfamily

metricfamily = *metric-descriptor *metric

metric-descriptor = HASH SP type SP metricname SP metric-type LF
metric-descriptor =/ HASH SP help SP metricname SP escaped-string LF
metric-descriptor =/ HASH SP unit SP metricname SP *metricname-char LF

metric = *sample

metric-type = counter / gauge / histogram / gaugehistogram / stateset
metric-type =/ info / summary / unknown

sample = metricname [labels] SP number [SP timestamp] [exemplar] LF

exemplar = SP HASH SP labels SP number [SP timestamp]

labels = "{" [label *(COMMA label)] "}"

label = label-name EQ DQUOTE escaped-string DQUOTE

number = realnumber
; Case insensitive
number =/ [SIGN] ("inf" / "infinity")
number =/ "nan"

timestamp = realnumber

; Not 100% sure this captures all float corner cases.
; Leading 0s explicitly okay
realnumber = [SIGN] 1*DIGIT
realnumber =/ [SIGN] 1*DIGIT ["." *DIGIT] [ "e" [SIGN] 1*DIGIT ]
realnumber =/ [SIGN] *DIGIT "." 1*DIGIT [ "e" [SIGN] 1*DIGIT ]


; RFC 5234 is case insensitive.
; Uppercase
eof = %d69.79.70
type = %d84.89.80.69
help = %d72.69.76.80
unit = %d85.78.73.84
; Lowercase
counter = %d99.111.117.110.116.101.114
gauge = %d103.97.117.103.101
histogram = %d104.105.115.116.111.103.114.97.109
gaugehistogram = gauge histogram
stateset = %d115.116.97.116.101.115.101.116
info = %d105.110.102.111
summary = %d115.117.109.109.97.114.121
unknown = %d117.110.107.110.111.119.110

BS = "\"
EQ = "="
COMMA = ","
HASH = "#"
SIGN = "-" / "+"

metricname = metricname-initial-char 0*metricname-char

metricname-char = metricname-initial-char / DIGIT
metricname-initial-char = ALPHA / "_" / ":"

label-name = label-name-initial-char *label-name-char

label-name-char = label-name-initial-char / DIGIT
label-name-initial-char = ALPHA / "_"

escaped-string = *escaped-char

escaped-char = normal-char
escaped-char =/ BS ("n" / DQUOTE / BS)
escaped-char =/ BS normal-char

; Any unicode character, except newline, double quote, and backslash
normal-char = %x00-09 / %x0B-21 / %x23-5B / %x5D-D7FF / %xE000-10FFFF

總體結構

必須使用 UTF-8。不得使用位元組順序標記 (BOM)。提醒實現者的一個重點是,位元組 0 是有效的 UTF-8,而位元組 255 則不是。

內容型別必須是

application/openmetrics-text; version=1.0.0; charset=utf-8

換行必須用換行符 (\n) 表示,並且不得包含回車符 (\r)。Exposition 必須以 EOF 結尾,並且應該以 EOF\n 結尾。

一個完整的 exposition 示例

# TYPE acme_http_router_request_seconds summary
# UNIT acme_http_router_request_seconds seconds
# HELP acme_http_router_request_seconds Latency though all of ACME's HTTP request router.
acme_http_router_request_seconds_sum{path="/api/v1",method="GET"} 9036.32
acme_http_router_request_seconds_count{path="/api/v1",method="GET"} 807283.0
acme_http_router_request_seconds_created{path="/api/v1",method="GET"} 1605281325.0
acme_http_router_request_seconds_sum{path="/api/v2",method="POST"} 479.3
acme_http_router_request_seconds_count{path="/api/v2",method="POST"} 34.0
acme_http_router_request_seconds_created{path="/api/v2",method="POST"} 1605281325.0
# TYPE go_goroutines gauge
# HELP go_goroutines Number of goroutines that currently exist.
go_goroutines 69
# TYPE process_cpu_seconds counter
# UNIT process_cpu_seconds seconds
# HELP process_cpu_seconds Total user and system CPU time spent in seconds.
process_cpu_seconds_total 4.20072246e+06
# EOF
轉義

當 ABNF 中提到轉義時,必須應用以下轉義規則:換行符,\n (0x0A) -> 字面量 \\n (位元組碼 0x5c 0x6e) 雙引號 -> \\" (位元組碼 0x5c 0x22) 反斜槓 -> \\\\ (位元組碼 0x5c 0x5c)

應該使用雙反斜槓來表示一個反斜槓字元。不應該對未定義的轉義序列使用單個反斜槓。例如,\\\\a 等效於 \\a 並且更推薦使用前者。

數字

整數不得包含小數點。例如 2300421341298465647914

浮點數必須用小數點或科學記數法表示。例如 8903.1234211.89e-7。浮點數必須在 IEEE 754 定義的 64 位浮點值範圍內,但可能會因為尾數位數過多而導致精度損失。這可以用於編碼納秒級解析度的時間戳。

如“規範數字”一節中所述,“quantile”和“le”標籤值不得使用任意的整數和浮點數表示。在其他任何使用數字的地方,都可以使用這種表示。

注意事項:規範數字

直方圖的 "le" 標籤值和摘要指標的 "quantile" 標籤值中的數字是特殊的,因為它們是標籤值,而標籤值旨在作為不透明的字串。由於終端使用者可能會直接與這些字串值互動,並且許多監控系統缺乏將它們作為一等公民數字處理的能力,因此如果一個給定的數字有完全相同的文字表示,將會非常有益。

一致性非常重要,但現實世界中各種語言及其執行時的實現使得強制要求這一點不切實際。最重要的常見分位數是 0.5、0.95、0.9、0.99、0.999,以及代表從一毫秒到 10.0 秒值的桶值,因為這些覆蓋了典型 Web 服務的延遲 SLA 和 Apdex 等情況。涵蓋 10 的冪是為了確保在定點和指數表示之間的切換是一致的,因為這在不同的執行時中會有所不同。目標渲染等同於 Go 語言 float64 值的預設渲染(即 %g),並在沒有小數點或指數的情況下追加 .0,以明確它們是浮點數。

Exposer 必須將正無窮大輸出為 +Inf。

對於 0.0 到 10.0 之間以 0.001 為增量的值,Exposer 應該按照以下示例生成輸出:0.0 0.001 0.002 0.01 0.1 0.9 0.95 0.99 0.999 1.0 1.7 10.0

對於 1e-10 到 1e+10 之間的 10 的冪次方值,Exposer 應該按照以下示例生成輸出:1e-10 1e-09 1e-05 0.0001 0.1 1.0 100000.0 1e+06 1e+10

解析器不得僅僅因為輸入值不符合規範值而拒絕它們。例如,不能拒絕 1.1e-4,儘管它不是 0.00011 的一致性表示。

對於非規範數字,Exposer 應該遵循這些模式。其目的是透過調整渲染演算法使這些值保持一致,從而使絕大多數其他值也具有一致的渲染。只使用少數特定 le/quantile 值的 Exposer 也可以進行硬編碼。在 C 等語言中,如果 Grisu3 這樣的最小化浮點數渲染演算法不易獲得,Exposer 可以使用不同的渲染方式。

給 C 語言及其他共享其 printf 實現的語言的實現者的一個警告是:%f、%e 和 %g 的標準精度只有六位有效數字。要達到完整精度需要 17 位有效數字,例如 printf("%.17g", d)

時間戳

如果需要納秒級精度,時間戳不應該使用浮點數的指數表示法,因為 float64 的表示沒有足夠的精度,例如 1604676851.123456789

MetricFamily

MetricFamily 之間不得有顯式分隔符。下一個 MetricFamily 必須透過元資料或一個新的樣本指標名稱來表示,該名稱不能是前一個 MetricFamily 的一部分。

MetricFamily 不得交錯出現。

MetricFamily 元資料

有四種元資料:MetricFamily 名稱、TYPE、UNIT 和 HELP。一個名為 foo 的計數器 Metric 的元資料示例如下:

# TYPE foo counter

如果未暴露 TYPE,則 MetricFamily 必須為 Unknown 型別。

如果指定了單位,它必須在 UNIT 元資料行中提供。此外,一個下劃線和單位必須作為 MetricFamily 名稱的字尾。

一個有效的示例,用於一個單位為 "seconds" 的 foo_seconds 指標:

# TYPE foo_seconds counter
# UNIT foo_seconds seconds

一個無效示例,其中單位不是名稱的字尾

# TYPE foo counter
# UNIT foo seconds

以下也是有效的:

# TYPE foo_seconds counter

如果單位已知,則應該提供。

UNIT 或 HELP 行的值可以為空。這必須被視為該 MetricFamily 不存在相應的元資料行。

# TYPE foo_seconds counter
# UNIT foo_seconds seconds
# HELP foo_seconds Some text and \n some \" escaping

對於一個 MetricFamily,每種型別的元資料行不得超過一個。順序應該是 TYPE、UNIT、HELP。

除了這些元資料和訊息末尾的 EOF 行之外,不得暴露以 # 開頭的行。

指標

指標不得交錯出現。

請參閱“文字格式 -> MetricPoint”中的示例。一個沒有標籤或時間戳且值為 0 的樣本必須渲染為以下兩種形式之一:

bar_seconds_count 0

bar_seconds_count{} 0

標籤值可以是任何有效的 UTF-8 值,因此必須按照 ABNF 的規定進行轉義。一個帶有兩個標籤的有效示例:

bar_seconds_count{a="x",b="escaping\" example \n "} 0

MetricPoint 值的渲染可以包含額外的標籤(例如,Histogram 型別的 "le" 標籤),這些標籤的渲染方式必須與 Metric 自身的 LabelSet 相同。

MetricPoint

MetricPoints 不得交錯出現。

一個正確的示例,其中一個 MetricFamily 內有多個 MetricPoints 和 Samples:

# TYPE foo_seconds summary
# UNIT foo_seconds seconds
foo_seconds_count{a="bb"} 0 123
foo_seconds_sum{a="bb"} 0 123
foo_seconds_count{a="bb"} 0 456
foo_seconds_sum{a="bb"} 0 456
foo_seconds_count{a="ccc"} 0 123
foo_seconds_sum{a="ccc"} 0 123
foo_seconds_count{a="ccc"} 0 456
foo_seconds_sum{a="ccc"} 0 456

一個不正確的示例,其中 Metrics 交錯出現:

# TYPE foo_seconds summary
# UNIT foo_seconds seconds
foo_seconds_count{a="bb"} 0 123
foo_seconds_count{a="ccc"} 0 123
foo_seconds_count{a="bb"} 0 456
foo_seconds_count{a="ccc"} 0 456

一個不正確的示例,其中 MetricPoints 交錯出現:

# TYPE foo_seconds summary
# UNIT foo_seconds seconds
foo_seconds_count{a="bb"} 0 123
foo_seconds_count{a="bb"} 0 456
foo_seconds_sum{a="bb"} 0 123
foo_seconds_sum{a="bb"} 0 456

指標型別

Gauge

對於 Gauge 型別的 MetricFamily,其 MetricPoint 值的樣本 MetricName 不得有後綴。

一個 MetricFamily 示例,包含一個無標籤的 Metric 和一個無時間戳的 MetricPoint:

# TYPE foo gauge
foo 17.0

一個 MetricFamily 示例,包含兩個帶標籤的 Metric 和無時間戳的 MetricPoint:

# TYPE foo gauge
foo{a="bb"} 17.0
foo{a="ccc"} 17.0

一個 MetricFamily 示例,不含任何 Metric:

# TYPE foo gauge

一個示例,包含一個帶標籤的 Metric 和一個帶時間戳的 MetricPoint:

# TYPE foo gauge
foo{a="b"} 17.0 1520879607.789

一個示例,包含一個無標籤的 Metric 和一個帶時間戳的 MetricPoint:

# TYPE foo gauge
foo 17.0 1520879607.789

一個示例,包含一個無標籤的 Metric 和兩個帶時間戳的 MetricPoint:

# TYPE foo gauge
foo 17.0 123
foo 18.0 456
Counter

MetricPoint 的 Total 值樣本 MetricName 必須帶有後綴 _total。如果存在,MetricPoint 的 Created 值樣本 MetricName 必須帶有後綴 _created

一個沒有標籤的 Metric 和一個沒有時間戳和沒有 Created 值的 MetricPoint 示例

# TYPE foo counter
foo_total 17.0

一個沒有標籤的 Metric 和一個帶時間戳但沒有 Created 值的 MetricPoint 示例

# TYPE foo counter
foo_total 17.0 1520879607.789

一個沒有標籤的 Metric 和一個沒有時間戳但帶 Created 值的 MetricPoint 示例

# TYPE foo counter
foo_total 17.0
foo_created 1520430000.123

一個沒有標籤的 Metric 和一個帶時間戳和 Created 值的 MetricPoint 示例

# TYPE foo counter
foo_total 17.0 1520879607.789
foo_created 1520430000.123 1520879607.789

Exemplar 可以附加到 MetricPoint 的 Total 樣本上。

StateSet

對於 StateSet 型別的 MetricFamily,其 MetricPoint 值的樣本 MetricName 不得有後綴。

StateSet 的 MetricPoint 中必須為每個狀態包含一個樣本。每個狀態的樣本必須有一個標籤,其標籤名為 MetricFamily 的名稱,標籤值為狀態的名稱。如果狀態為真,狀態樣本的值必須為 1;如果狀態為假,則必須為 0。

一個示例,包含 "a"、"bb" 和 "ccc" 三個狀態,其中只有 bb 值被啟用,指標名稱為 foo:

# TYPE foo stateset
foo{foo="a"} 0
foo{foo="bb"} 1
foo{foo="ccc"} 0

一個在 Metric 上帶有 "entity" 標籤的示例:

# TYPE foo stateset
foo{entity="controller",foo="a"} 1.0
foo{entity="controller",foo="bb"} 0.0
foo{entity="controller",foo="ccc"} 0.0
foo{entity="replica",foo="a"} 1.0
foo{entity="replica",foo="bb"} 0.0
foo{entity="replica",foo="ccc"} 1.0
Info

對於 Info 型別的 MetricFamily,其 MetricPoint 值的樣本 MetricName 必須有 _info 字尾。樣本值必須始終為 1。

一個示例,包含一個無標籤的 Metric,以及一個帶有 "name" 和 "version" 標籤的 MetricPoint 值:

# TYPE foo info
foo_info{name="pretty name",version="8.2.7"} 1

一個示例,包含一個帶 "entity" 標籤的 Metric,以及一個帶有 "name" 和 "version" 標籤的 MetricPoint 值:

# TYPE foo info
foo_info{entity="controller",name="pretty name",version="8.2.7"} 1.0
foo_info{entity="replica",name="prettier name",version="8.1.9"} 1.0

Metric 標籤和 MetricPoint 值標籤可以按任意順序排列。

總結

如果存在,MetricPoint 的 Sum 值樣本 MetricName 必須帶有後綴 _sum。如果存在,MetricPoint 的 Count 值樣本 MetricName 必須帶有後綴 _count。如果存在,MetricPoint 的 Created 值樣本 MetricName 必須帶有後綴 _created。如果存在,MetricPoint 的分位數(Quantile)值必須使用名為 "quantile" 的標籤以及所測量分位數的值來指定所測量的分位數。

一個沒有標籤的 Metric 和一個帶 Sum、Count 和 Created 值的 MetricPoint 示例

# TYPE foo summary
foo_count 17.0
foo_sum 324789.3
foo_created 1520430000.123

一個沒有標籤的 Metric 和一個帶兩個分位數的 MetricPoint 示例

# TYPE foo summary
foo{quantile="0.95"} 123.7
foo{quantile="0.99"} 150.0

分位數可以按任意順序排列。

Histogram

MetricPoint 的桶(Bucket)值樣本 MetricName 必須帶有後綴 _bucket。如果存在,MetricPoint 的 Sum 值樣本 MetricName 必須帶有後綴 _sum。如果存在,MetricPoint 的 Created 值樣本 MetricName 必須帶有後綴 _created。當且僅當 MetricPoint 中存在 Sum 值時,MetricPoint 的 +Inf 桶值也必須出現在帶有後綴 "_count" 的 MetricName 的樣本中。

桶必須按照 "le" 值的數字升序排列,並且 "le" 標籤的值必須遵循規範數字的規則。

一個沒有標籤的 Metric 和一個帶 Sum、Count 和 Created 值以及 12 個桶的 MetricPoint 示例。故意展示了各種廣泛且非典型但有效的“le”值。

# TYPE foo histogram
foo_bucket{le="0.0"} 0
foo_bucket{le="1e-05"} 0
foo_bucket{le="0.0001"} 5
foo_bucket{le="0.1"} 8
foo_bucket{le="1.0"} 10
foo_bucket{le="10.0"} 11
foo_bucket{le="100000.0"} 11
foo_bucket{le="1e+06"} 15
foo_bucket{le="1e+23"} 16
foo_bucket{le="1.1e+23"} 17
foo_bucket{le="+Inf"} 17
foo_count 17
foo_sum 324789.3
foo_created 1520430000.123
Exemplar

沒有標籤的 Exemplar 必須用 {} 表示一個空的 LabelSet。

一個展示了多個有效案例的 Exemplar 示例:“0.01” 桶沒有 Exemplar。0.1 桶有一個無標籤的 Exemplar。1 桶有一個帶一個標籤的 Exemplar。10 桶有一個帶標籤和時間戳的 Exemplar。在實踐中,所有桶都應該有相同風格的 Exemplar。

# TYPE foo histogram
foo_bucket{le="0.01"} 0
foo_bucket{le="0.1"} 8 # {} 0.054
foo_bucket{le="1"} 11 # {trace_id="KOO5S4vxi0o"} 0.67
foo_bucket{le="10"} 17 # {trace_id="oHg5SJYRHA0"} 9.8 1520879607.789
foo_bucket{le="+Inf"} 17
foo_count 17
foo_sum 324789.3
foo_created  1520430000.123
GaugeHistogram

MetricPoint 的桶值樣本 MetricNames 必須有 _bucket 字尾。如果存在,MetricPoint 的 Sum 值樣本 MetricName 必須有 _gsum 字尾。當且僅當一個 MetricPoint 中存在 Sum 值時,該 MetricPoint 的 +Inf 桶值也必須出現在一個帶有 _gcount 字尾的 MetricName 的樣本中。

桶必須按照 "le" 值的數字升序排列,並且 "le" 標籤的值必須遵循規範數字的規則。

一個示例,包含一個無標籤的 Metric,以及一個 MetricPoint 值,該值在桶中沒有 Exemplar。

# TYPE foo gaugehistogram
foo_bucket{le="0.01"} 20.0
foo_bucket{le="0.1"} 25.0
foo_bucket{le="1"} 34.0
foo_bucket{le="10"} 34.0
foo_bucket{le="+Inf"} 42.0
foo_gcount 42.0
foo_gsum 3289.3
Unknown

對於 Unknown 型別的 MetricFamily,其 MetricPoint 值的樣本指標名稱不得有後綴。

一個示例,包含一個無標籤的 Metric 和一個無時間戳的 MetricPoint:

# TYPE foo unknown
foo 42.23

Protobuf 格式

總體結構

Protobuf 訊息必須以二進位制格式編碼,並且其內容型別必須為 application/openmetrics-protobuf; version=1.0.0

所有有效載荷必須是單個二進位制編碼的 MetricSet 訊息,該訊息由 OpenMetrics protobuf 模式定義。

版本

Protobuf 格式必須遵循 proto3 版本的協議緩衝區語言。

字串

所有字串欄位必須使用 UTF-8 編碼。

時間戳

OpenMetrics protobuf 模式中的時間戳表示必須遵循已釋出的 google.protobuf.Timestamp [timestamp] 訊息。時間戳訊息必須是 Unix 紀元秒,以 int64 表示,以及一個納秒級解析度的非負秒數部分,以 int32 表示,從秒時間戳部分開始向前計數。該值必須在 0 到 999,999,999(含)之間。

Protobuf 模式

Protobuf schema 目前可在此處 獲取。

注意Prometheus 及其生態系統不支援 OpenMetrics protobuf 模式,而是使用類似的 io.prometheus.client 格式 。關於 OpenMetrics 2.0 中 protobuf 模式未來的討論正在進行中 

設計考慮

範圍

OpenMetrics 旨在為線上系統提供遙測資料。它執行在不提供硬即時或軟即時保證的協議之上,因此它自身也無法做出任何即時保證。OpenMetrics 的延遲和抖動屬性與底層網路、作業系統、CPU 等一樣不精確。它足夠精確,可以用於聚合以作為決策依據,但不能反映單個事件。

應支援各種規模的系統,從每小時接收幾次請求的應用程式到監控 400Gb 網路埠的頻寬使用情況。應能對傳輸的遙測資料進行任意時間段的聚合和分析。

它旨在以固定的節奏傳輸資料傳輸時刻的狀態快照。

範圍之外

攝取方如何發現哪些暴露方存在,反之亦然,這超出了本標準的範圍,因此未在本標準中定義。

擴充套件與改進

OpenMetrics 的第一個版本基於公認且事實標準的 Prometheus 文字格式 0.0.4,並特意沒有在其之上新增主要的句法或語義擴充套件或最佳化。例如,沒有嘗試使直方圖桶的文字表示更緊湊,而是依賴底層堆疊中的壓縮來處理其重複性。

這是一個刻意的選擇,以便該標準能夠利用現有使用者群的採用和勢頭。這確保了從 Prometheus 文字格式 0.0.4 的過渡相對容易。

它還確保有一個易於實現的基本標準。這可以在標準的未來版本中加以擴充套件。其意圖是,標準的未來版本將始終要求支援這個 1.0 版本,無論是在句法上還是語義上。

我們希望允許監控系統能夠從 OpenMetrics exposition 中獲取有用的資訊,而不會帶來過重的負擔。如果剝離所有元資料和結構,僅將 OpenMetrics exposition 視為一組無序的樣本,那麼它本身也應該是可用的。因此,也沒有不透明的二進位制型別,如 sketch 或 t-digest,這些型別無法表示為 gauge 和 counter 的混合,因為它們需要自定義的解析和處理。

這一原則在整個標準中得到了一貫的應用。例如,MetricFamily 的單位在名稱中重複出現,以便不理解單位元資料的系統也能獲得單位資訊。“le”標籤是一個普通的標籤值,而不是擁有自己特殊的語法,這樣攝取方就不必新增特殊的直方圖處理程式碼來攝取它們。再舉一個例子,沒有複合資料型別。例如,沒有用於緯度/經度的地理位置型別,因為這可以透過單獨的 gauge 指標來完成。

單位和基本單位

為了在系統間保持一致性並避免混淆,單位主要基於國際單位制(SI)基本單位。基本單位包括秒、位元組、焦耳、克、米、比率、伏特、安培和攝氏度。在適用的情況下應提供單位。

例如,將所有持續時間指標都以秒為單位,就不會有猜測某個指標是納秒、微秒、毫秒、秒、分鐘、小時、天還是周的風險,也不必處理混合單位。透過選擇無字首的單位,我們避免了像在複雜系統的湧現行為中出現“千毫秒”這樣的情況。

由於值可以是浮點數,標準內建了亞基本單位的精度。

同樣,混合使用位元和位元組會引起混淆,所以選擇位元組作為基本單位。雖然開爾文在理論上是更好的基本單位,但實際上大多數現有硬體都暴露攝氏度。千克是 SI 基本單位,但“千”這個字首有問題,所以選擇克作為基本單位。

雖然在所有可能的情況下都應該使用基本單位,但開爾文是一個公認的單位,可以在某些用例中替代攝氏度,例如顏色或黑體溫度,因為在這些情況下不太可能比較攝氏度和開爾文指標。

比率是基本單位,而不是百分比。在可能的情況下,應以 gauge 或 counter 的形式暴露給定分子和分母的原始資料。這在攝取方的分析和聚合中具有更好的數學特性。

分貝不是基本單位,首先,“deci”是 SI 字首;其次,貝爾是對數單位。要暴露訊號/能量/功率比,直接暴露比率會更好,如果可能的話,暴露原始功率/能量則更佳。浮點數指數足以覆蓋甚至極端的科學用途。一個電子伏特(~1e-19 J)到一顆超新星釋放的能量(~1e44 J)有 63 個數量級,而一個 64 位浮點數可以覆蓋超過 2000 個數量級。

如果無法避免非基本單位且轉換不可行,實際單位仍應包含在指標名稱中以求清晰。例如,焦耳是能量和功率的基本單位,因為瓦特可以表示為單位為焦耳的計數器。在實踐中,某個第三方系統可能只暴露瓦特,因此在這種情況下,以瓦特表示的 gauge 將是唯一現實的選擇。

並非所有的 MetricFamily 都有單位。例如,HTTP 請求的計數就沒有單位。技術上講,單位是“HTTP 請求”,但從這個意義上說,整個 MetricFamily 名稱就是單位。做到如此極端是沒有用的。應始終牢記在下游系統中為人類消費提供良好圖表軸的可能性。

無狀態性

OpenMetrics 定義的傳輸格式在不同的 exposition 之間是無狀態的。之前暴露過的資訊不得對未來的 exposition 產生任何影響。每個 exposition 都是暴露方當前狀態的一個獨立快照。

必須向現有和新的攝取方提供相同的獨立 exposition。

一個核心設計選擇是,暴露方不得僅僅因為某個指標最近沒有變化或觀測而將其排除。暴露方不得對攝取方消費 exposition 的頻率做任何假設。

跨時間的資料暴露與指標演變

指標在能夠分析其隨時間演變時最為有用,因此,exposition 必須在時間維度上有意義。因此,僅僅一個單獨的 exposition 有用且有效是不夠的。對指標語義的某些更改也可能破壞下游使用者。

解析器通常透過快取之前的結果來進行最佳化。因此,即使在技術上不構成破壞性變更,也應避免在不同的 exposition 之間更改標籤的暴露順序。這通常也使得編寫 exposition 的單元測試更容易。

指標和樣本不應該在不同的 exposition 之間出現又消失,例如,一個計數器只有在有歷史記錄時才有用。原則上,一個給定的指標應該從程序啟動時就存在於 exposition 中,直到程序終止。通常無法預先知道一個 MetricFamily 在給定程序的生命週期內將有哪些指標(例如,延遲直方圖的標籤值是 HTTP 路徑,由終端使用者在執行時提供),但一旦一個類似計數器的指標被暴露,它就應該一直被暴露直到程序終止。一個計數器沒有增加並不意味著它不再具有當前值。在某些情況下,停止暴露某個特定指標可能是合理的;請參閱“缺失資料”一節。

通常,更改 MetricFamily 的型別,或從其指標中新增或刪除標籤,對攝取方來說將是破壞性的變更。

一個顯著的例外是,向 Info MetricPoints 的值新增標籤不是破壞性變更。這樣做是為了您可以向現有的 Info MetricFamily 新增額外資訊,而不是被迫建立一個帶有額外標籤值的全新 info 指標。攝取系統應確保它們能夠適應此類新增。

更改 MetricFamily 的 Help 不是破壞性變更。對於可能的值,在浮點數和整數之間切換不是破壞性變更。向狀態集新增新狀態不是破壞性變更。在不更改指標名稱的情況下新增單位元資料不是破壞性變更。

直方圖的桶不應該在不同的 exposition 之間發生變化,因為這很可能導致效能問題並破壞攝取方。同樣,來自任何一致的應用程式二進位制檔案和環境的所有 exposition 都應該對給定的直方圖 MetricFamily 使用相同的桶,以便所有攝取方都可以對它們進行聚合,而無需攝取方實現異構桶的直方圖合併邏輯。一個例外可能是偶爾手動更改桶,這被認為是破壞性變更,但當效能特徵因新軟體釋出而改變時,這可能是一個有效的權衡。

即使更改在技術上不是破壞性的,它們仍然會帶來成本。例如,頻繁的更改可能會給攝取方帶來效能問題。一個在不同 exposition 之間變化的 Help 字串可能會導致每個 Help 值都被儲存。頻繁地在整數和浮點數值之間切換可能會妨礙高效壓縮。

NaN

在 OpenMetrics 中,NaN 和其他任何數字一樣,通常是除以零的結果,例如,如果最近沒有觀測資料,摘要分位數就會出現這種情況。NaN 在 OpenMetrics 中沒有特殊含義,尤其不得用作缺失或其他壞資料的標記。

缺失資料

在某些有效的情況下,資料會停止存在。例如,一個檔案系統可以被解除安裝,因此其表示可用磁碟空間的 Gauge 指標就不再存在了。對於這種情況,沒有特殊的標記或訊號。後續的 exposition 只是不再包含這個指標。

資料暴露效能

指標只有在合理的時間範圍內能夠被收集時才有用。需要數分鐘才能暴露的指標被認為是沒有用的。

根據經驗,資料暴露不應超過一秒。

透過 OpenMetrics 序列化的舊系統指標可能需要更長時間。因此,不能做硬性的效能假設。

Exposition 應該是最新狀態的。例如,處理 exposition 請求的執行緒不應該依賴於快取的值,應儘可能繞過任何此類快取。

併發性

為了實現高可用性和即時訪問,一種常見的方法是使用多個攝取方。為了支援這一點,必須支援併發 exposition。所有併發系統的最佳實踐(BCP)都應該被遵循,常見的陷阱包括死鎖、競爭條件以及過於粗粒度的鎖定,這些都會妨礙 exposition 的併發進行。

指標命名與名稱空間

我們的目標是在指標和標籤名稱的命名中,在可理解性、避免衝突和簡潔性之間取得平衡。名稱透過下劃線分隔,因此指標名稱最終採用“snake_case”格式。

舉個例子,“http_request_seconds”雖然簡潔,但在大量應用程式之間會發生衝突,而且這個指標具體測量的是什麼也不清楚。例如,在複雜的系統中,它可能是在認證中介軟體之前或之後測量的。

指標名稱應指明它們來自哪部分程式碼。因此,一家名為“萬能製造公司”(A Company Manufacturing Everything)的公司可能會給其程式碼中的所有指標加上“acme_”字首,如果他們有一個測量延遲的 HTTP 路由器庫,它可能會有一個類似“acme_http_router_request_seconds”的指標,並附有幫助字串,說明這是總體延遲。

我們的目的不是要防止所有應用程式之間所有潛在的衝突,因為那需要像全球指標名稱空間登錄檔或基於 DNS 的長名稱空間這樣的繁瑣解決方案。相反,我們的目標是保持一種輕量級的非正式方法,以便對於一個給定的應用程式,其組成庫之間發生衝突的可能性非常小。

在一個監控系統的整個部署中,我們的目標是,相同指標名稱代表不同含義的衝突情況不常見。例如,`acme_http_router_request_seconds` 可能會出現在“萬能製造公司”開發的數百個不同應用程式中,這是正常的。如果“另一家實體制造公司”也在其 HTTP 路由器中使用了 `acme_http_router_request_seconds` 這個指標名稱,那也沒關係。如果兩家公司的應用程式都由同一個監控系統監控,這種衝突是不希望看到的,但可以接受,因為沒有哪個應用程式試圖同時暴露這兩個名稱,也沒有哪個目標試圖(錯誤地)兩次暴露同一個指標名稱。如果一個應用程式希望同時包含“我的示例公司”和“超級刺激公司”的 HTTP 路由器庫,那就會有問題,其中一個指標名稱需要以某種方式更改。

由此推論,一個庫越是公開,其指標名稱的名稱空間就應該越好,以減少此類情況發生的風險。`acme_` 對於公司內部使用來說是個不錯的選擇,但這些公司可能會為在其公司外部共享的程式碼選擇 `acmeverything_` 或 `acorpme_` 這樣的字首。

在按公司或組織進行名稱空間劃分之後,名稱空間和命名應繼續按庫/子系統/應用程式分形進行,例如上面的 `http_router` 庫。目標是,如果您熟悉程式碼庫的整體結構,您可以根據指標名稱很好地猜測出給定指標的檢測程式碼在哪裡。

對於一個常見且非常知名的現有軟體,軟體本身的名稱可能就足以區分了。例如,`bind_` 對於 DNS 軟體來說可能就足夠了,儘管更常規的命名方式是 `isc_bind_`。

以 `scrape_` 為字首的指標由攝取方用於附加與單個 exposition 相關的資訊,因此不應由應用程式直接暴露。已經由通用監控系統消費和處理過的指標,在隨後的 exposition 中可能會包含此類指標名稱。如果一個暴露方希望提供關於單個 exposition 的資訊,可以使用類似 `myexposer_scrape_` 的指標字首。一個常見的例子是 gauge `myexposer_scrape_duration_seconds`,用於表示從暴露方角度看,該 exposition 花費了多長時間。

在 Prometheus 生態系統中,出現了一組在所有實現中都一致的、以 `process_` 為字首的程序級指標。例如,對於開啟檔案的 ulimit,MetricFamilies `process_open_fds` 和 `process_max_fds` 這兩個 gauge 分別提供了當前值和最大值。(這些名稱是遺留的,如果今天定義這樣的指標,它們很可能被稱為 `process_fds_open` 和 `process_fds_limit`)。總的來說,要獲得具有完全相同語義的名稱是非常具有挑戰性的,這就是為什麼不同的檢測工具應該使用不同的名稱。

避免在指標名稱中出現冗餘。避免使用像“metric”、“timer”、“stats”、“counter”、“total”、“float64”等子字串——作為一個透過 OpenMetrics 暴露的具有給定型別(可能還有單位)的指標,這類資訊已經隱含其中,不應明確包含。出於同樣的原因,您不應將指標的標籤名稱包含在指標名稱中,此外,監控系統對指標的後續聚合可能會使此類資訊不正確。

避免在您的檢測程式碼的指標名稱中包含來自監控系統其他層的實現細節。例如,MetricFamily 名稱不應僅僅因為它碰巧目前在某處透過 OpenMetrics 暴露而包含字串“openmetrics”,或者僅僅因為您當前的監控系統是 Prometheus 而包含“prometheus”。

標籤名稱空間

對於標籤名稱,不建議按公司或庫進行顯式名稱空間劃分,考慮到標籤名稱長度的增加,來自指標名稱的名稱空間就足夠了。但是,建議採取一些最基本的措施來避免常見的衝突。

有些標籤名稱,如 region、zone、cluster、availability_zone、az、datacenter、dc、owner、customer、stage、service、team、job、instance、environment 和 env,很可能與通用監控系統可能新增的用於識別目標的標籤發生衝突。儘量避免使用它們,在這些情況下,新增最少的名稱空間可能是合適的。

標籤名“type”非常通用,應避免使用。例如,對於與 HTTP 相關的指標,如果要區分 GET、POST 和 PUT 請求,“method”會是更好的標籤名。

雖然有關於指標名稱的元資料,如 HELP、TYPE 和 UNIT,但沒有關於標籤名稱的元資料。這是因為這樣做會使格式膨脹而收效甚微。帶外文件是暴露方可以向其攝取方呈現這些資訊的一種方式。

指標名稱與標籤

在某些情況下,在 MetricFamily 中使用多個指標或使用多個 MetricFamily 似乎都是合理的。對 MetricFamily 進行求和或求平均值應該是有意義的,即使它不總是有用。例如,混合電壓和風扇速度是沒有意義的。

提醒一下,OpenMetrics 的構建假設是攝取方可以處理和執行資料聚合。

將總和與其他指標一起暴露是錯誤的,因為這會導致下游攝取方在聚合時重複計算。

wrong_metric{label="a"} 1
wrong_metric{label="b"} 6
wrong_metric{label="total"} 7

指標的標籤應保持在確保唯一性所需的最小數量,因為每增加一個標籤,使用者在確定下游要使用的標籤時就需要多考慮一個。可以應用於許多 MetricFamily 的標籤可以考慮移入類似於資料庫{{normalization}}的_info 指標。如果幾乎所有指標的使用者都期望有額外的標籤,那麼將其新增到所有 MetricFamily 可能是一個更好的權衡。例如,如果您有一個與不同 SQL 語句相關的 MetricFamily,其唯一性由包含完整 SQL 語句雜湊的標籤提供,那麼為了人類可讀性,再有一個包含 SQL 語句前 500 個字元的標籤也是可以的。

經驗表明,下游攝取方發現處理單獨的總數和失敗 MetricFamily 比在一個 MetricFamily 中使用 {result="success"} 和 {result="failure"} 標籤更容易。此外,通常最好暴露單獨的讀寫和收發 MetricFamily,因為全雙工系統很常見,下游攝取方更關心這些值的單獨情況而不是聚合情況。

所有這些並不像聽起來那麼容易。這是一個需要領域專家在資料暴露和被暴露系統方面憑藉經驗和工程權衡來找到良好平衡的領域。指標和標籤名稱字元

OpenMetrics 建立在現有廣泛採用的 Prometheus 文字暴露格式及其周圍形成的生態系統之上。向後相容性是一個核心設計目標。擴充套件或收縮 Prometheus 文字格式支援的字元集將違背這一目標。破壞向後相容性將產生比僅僅是傳輸格式更廣泛的影響。特別是,為處理 Prometheus 生態系統內傳輸的資料而建立或採用的查詢語言依賴於這些精確的字元集。標籤值支援完整的 UTF-8,因此該格式可以表示多語言指標。

元資料型別

元資料可以來自不同的來源。多年來,出現了兩個主要來源。雖然它們在功能上通常相同,但討論它們的概念差異有助於理解。

“目標元資料”通常是暴露方外部的元資料。常見的例子是來自服務發現、CMDB 或類似系統的資料,例如關於資料中心區域的資訊,服務是否屬於特定部署,或者生產或測試環境。這可以透過暴露方或攝取方向所有捕獲此元資料的指標新增標籤來實現。透過攝取方來做這件事是首選,因為它更靈活,開銷更小。在靈活性方面,硬體維護團隊可能關心機器位於哪個伺服器機架中,而使用同一臺機器的資料庫團隊可能關心它包含生產資料庫的第 2 個副本。在開銷方面,硬編碼或配置此資訊需要額外的分發路徑。

“暴露方元資料”來自暴露方內部。常見的例子是軟體版本、編譯器版本或 Git 提交的 SHA。

在基於推送和基於拉取的系統中支援目標元資料

在基於推送的消費中,通常由暴露方向攝取方提供相關的目標元資料。在基於拉取的消費中,可以採用基於推送的方法,但更典型的是,攝取方已經先驗地知道目標的元資料,例如從機器資料庫或服務發現系統中獲取,並在消費 exposition 時將其與指標關聯起來。

OpenMetrics 是無狀態的,並向所有攝取方提供相同的 exposition,這與推送式方法相沖突。此外,推送式方法會破壞拉取式攝取方,因為會暴露不需要的元資料。

一種方法是讓推送式攝取方根據操作員配置,透過帶外方式提供目標元資料,例如作為 HTTP 標頭。雖然這可以為推送式攝取方傳輸目標元資料,並且本標準也不禁止這樣做,但它的缺點是,即使拉取式攝取方應該使用自己的目標元資料,能夠訪問暴露方自身知道的元資料通常仍然很有用。

首選的解決方案是將此目標元資料作為 exposition 的一部分提供,但方式不影響整個 exposition。Info MetricFamily 就是為此設計的。一個暴露方可以包含一個名為“target”的 Info MetricFamily,其中只有一個沒有標籤的指標,包含了元資料。文字格式的一個示例如下:

# TYPE target info
# HELP target Target metadata
target_info{env="prod",hostname="myhost",datacenter="sdc",region="europe",owner="frontend"} 1

當一個暴露方為此目的提供此指標時,它應該位於 exposition 的最前面。這是為了效率,以便依賴它獲取目標元資料的攝取方不必在應用基於其內容的業務邏輯之前緩衝其餘的 exposition。

暴露方不得向 exposition 中的所有指標新增目標元資料標籤,除非為特定攝取方明確配置。暴露方不得為 MetricFamily 名稱新增字首或以其他方式根據目標元資料改變 MetricFamily 名稱。通常,同一個標籤不應出現在 exposition 的每個指標上,但在極少數情況下,這可能是湧現行為的結果。同樣,在非常小的 exposition 中,來自一個暴露方的所有 MetricFamily 名稱可能碰巧共享一個字首。例如,由“萬能製造公司”用 Go 語言編寫的應用程式很可能包含帶有 acme_、go_、process_ 字首的指標,以及來自任何使用的第三方庫的指標字首。

暴露方可以作為 Info MetricFamilies 暴露暴露方元資料。

以上討論是在單個暴露方的背景下進行的。來自通用監控系統的 exposition 可能包含來自許多單個目標的指標,因此可能暴露多個目標資訊指標。這些指標在攝取過程中可能已經將目標元資料作為標籤新增。指標名稱不得根據目標元資料而改變。例如,即使所有指標都源自暫存環境中的目標,將所有指標都加上 staging_ 字首也是不正確的。

客戶端計算與派生指標

暴露方應將任何數學或計算留給攝取方處理。一個顯著的例外是 Summary 分位數,不幸的是,為了向後相容性,這是必需的。Exposition 應該是對任意時間段都有用的原始值。

舉個例子,您不應暴露一個表示過去 5 分鐘內計數器平均增長率的 gauge。讓攝取方根據他們跨 exposition 消費的資料點計算增量,具有更好的數學特性,並且對抓取失敗更具彈性。

另一個例子是直方圖/摘要的平均事件大小。暴露自應用程式啟動或自指標建立以來的計數器平均增長率,既有前面例子的問​​題,也妨礙了聚合。

標準差也屬於這一類。將平方和作為計數器暴露是正確的方法。它沒有被包含在本標準的直方圖值中,因為 64 位浮點精度不足以在實踐中實現這一點。由於平方,53 位尾數中只有一半可用於精度。例如,一個每秒觀察 1 萬個事件的直方圖會在 2 小時內失去精度。使用 64 位整數也好不到哪裡去,因為失去了浮點小數點,因為一個通常跟蹤秒級事件長度的納秒級解析度整數在 19 次觀察後就會溢位。當 128 位浮點數變得普遍時,可以重新審視這個設計決策。

另一個例子是避免暴露請求失敗率,而是暴露單獨的失敗請求和總請求計數器。

數字型別

對於一個每秒遞增一百萬次的計數器,使用 float64(它有 53 位尾數)需要超過一個世紀才會開始失去精度。然而,一個 100 Gbps 網路介面的位元組吞吐量精度可能會在大約 20 小時內開始因 float64 而丟失。雖然對於一個 100 Gbps 網路介面來說,在數年內丟失 1KB 的精度在實踐中不太可能成為問題,但對於具有如此高吞吐量的整數資料,int64 是一個選擇。

摘要分位數必須是 float64,因為它們是估計值,因此從根本上說是不準確的。

暴露時間戳

OpenMetrics 的一個核心假設是,暴露方暴露的是他們所暴露內容的最新的快照。

雖然在暴露的資料上附加時間戳的用例有限,但這些情況非常罕見。之前附加過時間戳的資料,特別是已經攝入到通用監控系統中的資料,可能會攜帶時間戳。即時或原始資料不應攜帶時間戳。在不同的 exposition 中暴露具有相同時間戳的相同指標 MetricPoint 值是有效的,但是如果底層指標現在已經丟失,這樣做是無效的。

時間同步是一個難題,每個系統內部的資料應保持一致。因此,攝取方應能根據自己的視角為資料附加當前時間戳,而不是基於暴露方裝置的系統時間。

對於帶時間戳的指標,通常無法檢測到指標在不同 exposition 之間何時消失。然而,對於不帶時間戳的指標,攝取方可以在指標不再出現的 exposition 中使用自己的時間戳。

所有這些都說明,通常情況下,不應暴露 MetricPoint 時間戳,因為應由攝取方自行決定為其攝取的資料樣本附加時間戳。

跟蹤指標最後變更時間

假設你有一個計數器 my_counter,它被初始化,然後在時間 123 增加了 1。以文字格式暴露它的正確方式是:

# HELP my_counter Good increment example
# TYPE my_counter counter
my_counter_total 1

根據父章節的規定,攝取方應該可以自由地附加自己的時間戳,所以以下方式是不正確的:

# HELP my_counter Bad increment example
# TYPE my_counter counter
my_counter_total 1 123

如果計數器最後一次更改的具體時間很重要,那麼正確的方式是:

# HELP my_counter Good increment example
# TYPE my_counter counter
my_counter_total 1
# HELP my_counter_last_increment_timestamp_seconds When my_counter was last incremented
# TYPE my_counter_last_increment_timestamp_seconds gauge
# UNIT my_counter_last_increment_timestamp_seconds seconds
my_counter_last_increment_timestamp_seconds 123

透過將最後更改的時間戳放入其自己的 Gauge 作為值,攝取方可以自由地為這兩個指標附加自己的時間戳。

經驗表明,暴露絕對時間戳(這裡紀元時間被認為是絕對的)比暴露經過的時間、自……以來的秒數或類似的時間戳更穩健。無論哪種情況,它們都會是 gauge。例如:

# TYPE my_boot_time_seconds gauge
# HELP my_boot_time_seconds Boot time of the machine
# UNIT my_boot_time_seconds seconds
my_boot_time_seconds 1256060124

比以下方式更好:

# TYPE my_time_since_boot_seconds gauge
# HELP my_time_since_boot_seconds Time elapsed since machine booted
# UNIT my_time_since_boot_seconds seconds
my_time_since_boot_seconds 123

反之,對範例(exemplar)時間戳沒有最佳實踐限制。請記住,由於競態條件或裝置之間時間未完全同步,範例時間戳可能會相對於攝取器的系統時鐘或來自相同暴露的其他指標略微超前。同樣,MetricPoint 的 "_created" 時間戳也可能略微晚於該 MetricPoint 的範例或樣本時間戳。

請記住,常用的監控系統支援從納秒到秒的各種解析度,因此,如果兩個 MetricPoint 的時間戳在截斷到秒級解析度後相同,可能會導致在攝取方出現明顯的重複。在這種情況下,必須使用時間戳最早的 MetricPoint。

閾值

暴露系統的期望邊界可能是有意義的,但需要謹慎處理。對於普遍適用的值,為這些閾值發出 Gauge 指標是有意義的。例如,資料中心的 HVAC 系統知道當前的測量值、設定點和警報設定點。它對期望的系統狀態有一個全域性有效且正確的檢視。作為反例,一些閾值可能會隨著規模、部署模型或時間的推移而改變。一定量的 CPU 使用率在一種設定下可能是可接受的,而在另一種設定下則是不希望的。值的聚合可以進一步改變可接受的值。在這樣的系統中,暴露邊界可能會適得其反。

例如,佇列的最大大小可以與佇列中當前的專案數量一起暴露,如下所示:

# HELP acme_notifications_queue_capacity The capacity of the notifications queue.
# TYPE acme_notifications_queue_capacity gauge
acme_notifications_queue_capacity 10000
# HELP acme_notifications_queue_length The number of notifications in the queue.
# TYPE acme_notifications_queue_length gauge
acme_notifications_queue_length 42

大小限制

本標準不對單次公開的樣本數量、可能存在的標籤數量、狀態集可能具有的狀態數量、資訊值中的標籤數量或指標名稱/標籤名稱/標籤值/幫助資訊的字元限制規定任何特定限制。

具體的限制可能會妨礙合理的使用場景,例如,雖然一個給定的公開資料在經過通用監控系統後可能具有適當數量的標籤,但可能會新增一些目標標籤,從而使其超過限制。對這些數字的具體限制也無法反映通用監控系統的真實成本所在。因此,這些指導方針旨在幫助公開方和攝取方理解什麼是合理的。

另一方面,如果公開資料在某個維度上過大,與所公開指標帶來的好處相比,可能會導致嚴重的效能問題。因此,關於任何單次公開資料大小的一些指導方針將是有用的。

攝取方可以選擇自行施加限制,特別是為了防止攻擊或服務中斷。儘管如此,攝取方需要考慮合理的使用場景,並儘量避免對其造成不成比例的影響。如果任何單個值/指標/公開資料超過此類限制,則必須拒絕整個公開資料。

總的來說,有三件事會影響通用監控系統攝取時間序列資料的效能:唯一時間序列的數量、這些序列中隨時間變化的樣本數量,以及唯一字串(如指標名稱、標籤名稱、標籤值和幫助資訊)的數量。攝取方可以控制其攝取頻率,因此這方面無需進一步考慮。

唯一時間序列的數量大致等於文字格式中非註釋行的數量。截至2020年,總共1000萬個時間序列被認為是一個很大的數量,通常是任何單例項攝取方上限的數量級。任何單次公開資料都不應在未經盡職調查的情況下超過1萬個時間序列。一個常見的考慮因素是水平擴充套件:如果將例項數量擴充套件1-2個數量級會發生什麼?30年前,在單個部署中擁有一千臺架頂式交換機是難以想象的。如果一個目標是單例的(例如,公開與整個叢集相關的指標),那麼幾十萬個時間序列可能是合理的。重要的不是唯一MetricFamilies的數量或單個標籤/桶/狀態集的基數,而是時間序列的總數量級。1000個各有一個Metric的gauge與一個有1000個Metric的gauge成本相同。

如果特定型別的所有目標都公開相同的時間序列集,那麼每個額外目標的字串對大多數合理的現代監控系統都不會產生增量成本。然而,如果每個目標都有唯一的字串,則會產生這樣的成本。舉一個極端的例子,一個由許多目標使用的1萬字符的指標名稱本身在實踐中不太可能成為問題。相反,一千個目標各自公開一個唯一的36個字元的UUID,在需要儲存的字串方面,其成本是那個1萬字符指標名稱的三倍以上(假設採用現代方法)。此外,如果這些字串隨時間變化,舊的字串仍需要儲存至少一段時間,從而產生額外成本。假設上一段提到的1000萬個時間序列,每小時100MB的唯一字串可能表明該使用場景更像是事件日誌記錄,而不是指標時間序列。

Exemplar長度有128個UTF-8字元的硬性限制,以防止濫用該功能進行追蹤跨度資料和其他事件日誌記錄。

安全性

實現者可以選擇(MAY)提供身份驗證、授權和計費;如果選擇這樣做,這應該(SHOULD)在OpenMetrics之外處理。

所有公開方實現都應該(SHOULD)能夠使用TLS 1.2或更高版本來保護其HTTP流量。如果公開方實現不支援加密,操作員應該(SHOULD)在可行的情況下使用反向代理、防火牆和/或ACL。

指標公開應獨立於向終端使用者公開的生產服務;因此,通常不鼓勵在TCP/80、TCP/443、TCP/8080和TCP/8443等埠上為使用OpenMetrics的公共服務設定/metrics端點。

IANA

雖然目前大多數Prometheus公開格式的實現都使用來自{{PrometheusPorts}}非正式登錄檔的非IANA註冊埠,但OpenMetrics可以在一個明確定義的埠上找到。

IANA為公開資料的客戶端分配的埠是<為歷史一致性請求的9099>。

如果需要在同一個IP地址和埠上訪問多個指標端點,操作員可以考慮使用反向代理,該代理透過localhost地址與公開方通訊。為簡化多路複用,端點應該(SHOULD)在其路徑中包含自己的名稱,即/node_exporter/metrics。不應該(SHOULD NOT)將多個公開資料合併為一個,原因在“在推和拉系統中支援目標元資料”一節中已說明,並且也是為了允許獨立的攝取,避免單點故障。

OpenMetrics希望註冊兩個MIME型別:application/openmetrics-textapplication/openmetrics-proto

本頁內容