OpenMetrics 2.0 [實驗性]
- 版本: 2.0.0-rc0
- 狀態: 實驗性
- 日期: 2026 年 3 月
- 作者: Arthur Silva Sens, Bartłomiej Płotka, David Ashpole, György Krajcsovits, Owen Williams, Richard Hartmann
- 榮譽作者: Ben Kochie, Brian Brazil, Rob Skillington
Prometheus 於 2012 年建立,自 2015 年以來已成為雲原生可觀測性的預設選擇。Prometheus 設計的核心部分是其文字指標公開格式,稱為 Prometheus 公開格式 0.0.4,自 2014 年起穩定。在這種格式中,我們特別注意使其易於生成、易於接收,並且易於人類理解。截至 2020 年,有超過 700 個公開列出的匯出器,數量不詳的未列出匯出器,以及數千個使用此格式的原生庫整合。來自各種專案和公司的數十個接收器支援消費它。
2020 年,OpenMetrics 1.0 釋出,旨在清理和收緊規範,並額外旨在將其納入 IETF。OpenMetrics 1.0 文字暴露格式記錄了一個工作標準,該標準在數十個匯出器、整合和攝取器中獲得了廣泛而自然的採用。
大約在 2024 年,OpenMetrics 專案被納入 CNCF Prometheus 專案旗下。結合大規模部署 OpenMetrics 1.0 的生產經驗以及文字格式中缺失的 Prometheus 新創新積壓,Prometheus 社群決定推出 OpenMetrics 標準的第二個版本。
OpenMetrics 2.0 的目的是以 OpenMetrics 1.0 為基礎,並對其進行增強,以實現更高的可靠性、可用性以及與現代 Prometheus 資料模型的一致性,同時不犧牲易用性和可讀性。OpenMetrics 2.0 還改進了與 OpenTelemetry 資料模型和命名約定的相容性。
本文件旨在作為獨立規範使用。
注意這是 OpenMetrics 2.0 規範的候選釋出 (RC) 版本。這意味著此規範目前處於實驗階段——預計不會有重大更改,但我們保留根據早期採用者的反饋在必要時破壞相容性的權利。潛在的反饋、問題和建議應作為prometheus/openmetrics倉庫上的一個 issue 新增。
概述
指標是一種特定的遙測資料。它們代表一組資料的當前狀態快照。它們與日誌或事件不同,後者關注的是單個事件的記錄或資訊。
OpenMetrics 主要是一種線路格式,獨立於任何特定的傳輸方式。該格式預計會被定期消費,並且在連續的公開中具有意義。
實現者應 (SHOULD) 透過對給定程序或裝置的文件化 URL 發出 HTTP GET 請求,以 OpenMetrics 文字格式暴露指標。此端點應 (SHOULD) 命名為 "/metrics"。實現者也可以 (MAY) 透過其他方式暴露 OpenMetrics 格式的指標,例如,透過 HTTP 定期將指標集推送到操作員配置的端點。
指標和時間序列
本標準將所有系統狀態表示為數值;計數、當前值、分佈、列舉和布林狀態是常見的例子。與指標不同,單一事件發生在特定時間。指標傾向於按時間聚合資料並提供系統狀態的樣本。雖然這可能會丟失資訊,但開銷的減少是許多現代監控系統普遍選擇的工程權衡。
時間序列是隨時間變化的資訊記錄。指標時間序列的常見示例包括網路介面計數器、裝置溫度、BGP 連線狀態、延遲分佈和告警狀態。
規範性語言
本文件中的關鍵詞“必須 (MUST)”、“不得 (MUST NOT)”、“必需 (REQUIRED)”、“應 (SHALL)”、“不應 (SHALL NOT)”、“應該 (SHOULD)”、“不應該 (SHOULD NOT)”、“推薦 (RECOMMENDED)”、“不推薦 (NOT RECOMMENDED)”、“可以 (MAY)”和“可選 (OPTIONAL)”應按照 RFC 2119 和 RFC 8174 中所述進行解釋,並且僅當它們以全大寫形式出現時才如此。
本文件中詞語“保留 (RESERVED)”用於指定為將來使用或本標準本身使用而預留的值、名稱或欄位。除非本標準或其未來版本明確允許,否則不得 (MUST NOT) 使用描述為“保留 (RESERVED)”的值、名稱或欄位。
資料模型
本節必須 (MUST) 與 ABNF 章節一併閱讀。如果兩者之間存在分歧,ABNF 的限制必須 (MUST) 優先。
資料型別
樣本值
OpenMetrics 中的指標值必須 (MUST) 是 Number 或 CompositeValue。
數值 (Number)
Number 值必須 (MUST) 是浮點數或整數。
請注意,格式的攝取器可以 (MAY) 只支援 float64,例如 Go 的 float64 是一種 IEEE 754-2008 雙精度 (binary64) 浮點數,具有大約 15-17 位有效十進位制數字的精度。必須 (MUST) 支援非實數值 NaN、+Inf 和 -Inf。NaN 值不得 (MUST NOT) 被視為缺失值,但可以 (MAY) 用於表示除以零或任何其他產生未定義或不確定結果的數學運算。
布林值必須 (MUST) 表示為 Number 值,其中 1 為真,0 為假。
複合值 (CompositeValue)
CompositeValue 必須 (MUST) 包含在 MetricFamily 中重新建立 Metric 樣本值所需的所有資訊。
以下 MetricFamily 型別必須 (MUST) 使用 CompositeValue 作為指標值
- Histogram (直方圖) MetricFamily 型別。
- GaugeHistogram (測量直方圖) MetricFamily 型別。
- Summary (摘要) MetricFamily 型別。
其他 MetricFamily 型別必須 (MUST) 使用 Numbers。
時間戳
時間戳必須 (MUST) 是以秒為單位的 Unix Epoch。時間戳應該 (SHOULD) 是浮點數以表示亞秒精度,例如毫秒或微秒。可以使用 (MAY) 負時間戳。
本標準中少數幾個使用時間戳的地方:
- Exemplar 的時間戳
- 樣本的時間戳
- 樣本的開始時間戳
字串
字串必須僅由有效的 UTF-8 字元組成,並且可以是零長度。必須支援 NULL (ASCII 0x0)。
標籤
標籤是由字串組成的鍵值對。
以下劃線開頭的一個或多個標籤名稱是保留 (RESERVED) 的,除非本標準指定,否則不得 (MUST NOT) 使用。在 MetricFamily 的元資料可能衝突的情況下,例如指標聯邦案例,此類標籤名稱可以 (MAY) 用於代替 TYPE 和 UNIT 元資料。
標籤名稱應該 (SHOULD) 遵循 ABNF 章節中 label-name 部分的限制。標籤名稱可以 (MAY) 是 ABNF 章節中描述的任何帶引號的轉義 UTF-8 字串。請注意,暴露 UTF-8 指標可能會降低可用性。
空標籤值應被視為該標籤不存在。
標籤集
標籤集必須由標籤組成,並且可以為空。在標籤集中,標籤名稱必須是唯一的。
Exemplars(範例)
Exemplars 是對 MetricSet 外部資料的引用。一個常見的用例是程式跟蹤的 ID。
Exemplar 必須 (MUST) 由一個 LabelSet 和一個 Number 值組成,並且必須 (MUST) 具有時間戳。LabelSet 不應該 (SHOULD NOT) 包含 Metric 的 LabelSet 中包含的任何標籤名稱。如果存在,時間戳應該 (SHOULD) 早於或等於樣本的時間戳。如果存在,時間戳應該 (SHOULD) 晚於或等於樣本的開始時間戳。樣本的 Exemplar 應該 (SHOULD) 具有相同的標籤名稱,以保持一致的風格。
Exemplar 的時間戳應該 (SHOULD) 接近其被觀察的時間點,但不必精確。例如,如果獲取精確時間戳成本較高,則可以使用外部源或估算值。
當 exemplar 引用 Trace Context (跟蹤上下文) 時,它應該 (SHOULD) 使用 trace_id 鍵作為 trace-id 欄位,並使用 span_id 鍵作為 parent-id 欄位。
雖然沒有指定硬性限制,但 Exemplar 的 LabelSet 不應該 (SHOULD NOT) 用於傳輸大型資料,例如跟蹤跨度詳情或其他事件日誌。
攝取器可以 (MAY) 截斷 Exemplar 的 LabelSet 或丟棄 Exemplar。截斷 Exemplar 的 LabelSet 時,即使截斷後也應該 (SHOULD) 保留 trace_id 和 span_id。
樣本 (Sample)
樣本是指標中的單個數據點。它必須 (MUST) 具有值,可以 (MAY) 具有時間戳。它還可以 (MAY) 包含 Exemplar,並可以 (MAY) 具有開始時間戳,具體取決於 MetricFamily 型別。
樣本不應該 (SHOULD NOT) 具有時間戳。有關不推薦此做法的原因,請參閱暴露時間戳。如果存在,樣本的時間戳指定了觀察到值的時間。
如果存在,樣本的開始時間戳應該 (SHOULD) 指定測量週期何時開始。這可以幫助攝取器區分新指標和以前未見的長期執行指標,即使在兩次攝取之間計數器值沒有減少,也能檢測到計數器重置。
指標
指標由 MetricFamily 中唯一的 LabelSet 定義。指標必須 (MUST) 包含一個或多個樣本的列表。如果為一個指標暴露了多個樣本,則其樣本必須 (MUST) 具有單調遞增的時間戳。
給定 MetricFamily 的同名指標應該 (SHOULD) 在其 LabelSet 中具有相同的標籤名稱集。
指標族
A MetricFamily 可以 (MAY) 包含零個或多個指標。MetricFamily 中的每個指標必須 (MUST) 具有唯一的 LabelSet。MetricFamily 必須 (MUST) 具有名稱,並且應該 (SHOULD) 具有 Help、Type 和 Unit 元資料。
名稱
MetricFamily 名稱
- 必須 (MUST) 是字串。
- 在 MetricSet 中必須 (MUST) 是唯一的。
- 必須 (MUST) 與族中每個指標的名稱相同。
注意:OpenMetrics 1.0 要求 MetricName 必須包含字尾,並且 MetricFamily 名稱與之匹配但沒有後綴。為了提高解析器可靠性(即匹配MetricFamily 元資料)和未來相容性,本規範要求指標名稱必須嚴格匹配其 MetricFamily 名稱。
名稱應該 (SHOULD) 使用 snake_case。名稱應該 (SHOULD) 遵循 ABNF 章節中 metricname 部分的限制。MetricFamily 名稱可以 (MAY) 是 ABNF 章節中描述的任何帶引號的轉義 UTF-8 字串。請注意,暴露 UTF-8 指標可能會降低可用性,尤其是在名稱中不包含 _total 或單位字尾時。
指標族名稱中的冒號被保留,用於表示該指標族是通用監控系統的計算或聚合結果。
以下劃線開頭的一個或多個 MetricFamily 名稱是保留 (RESERVED) 的,除非本標準指定,否則不得 (MUST NOT) 使用。
不推薦的字尾
MetricFamily 名稱不應該 (SHOULD NOT) 以 _count, _sum, _gcount, _gsum, _bucket 結尾。具體來說,當轉換為 OpenMetrics 1.0 文字格式時,名稱不應該 (SHOULD NOT) 造成 MetricName 衝突。攝取器可以 (MAY)拒絕包含此類 MetricFamily 的 MetricSet。
一個不符合規範的例子是,一個名為 foo_bucket 的 gauge 和一個名為 foo 的 histogram。暴露器在協商舊的 OpenMetrics 或文字格式時,或者僅支援舊資料模型的攝取器,最終可能會將 foo histogram 以經典表示形式(foo_bucket、foo_count、foo_sum)儲存,這將與 gauge 衝突並導致抓取拒絕或資料丟失。
此規則的存在是因為本規範遵循 Prometheus 生態系統向複合值而非“經典”表示形式的轉變。然而,這種轉換需要時間。避免此類字尾可以提高與舊攝取器的相容性以及最終的遷移過程。
型別
Type 指定 MetricFamily 型別。有效值為 "unknown"、"gauge"、"counter"、"stateset"、"info"、"histogram"、"gaugehistogram" 和 "summary"。
單位
Unit (單位) 指定 MetricFamily 的單位。如果非空,它應該 (SHOULD) 是 MetricFamily 名稱的字尾,並由下劃線分隔。其他特定型別字尾在單位字尾之後。直接向終端使用者暴露沒有單位作為 MetricFamily 名稱字尾的指標可能會因為對指標單位的混淆而降低可用性。有關推薦的單位值,請參閱單位和基本單位。
幫助
Help 是一個字串,並且應該非空。它用於為人類消費提供 MetricFamily 的簡要描述,並且應該足夠短,以便用作工具提示。
指標集
MetricSet 是 OpenMetrics 公開的頂級物件。它必須由 MetricFamilies 組成,並且可以為空。
每個 MetricFamily 名稱必須是唯一的。相同的標籤名稱和值不應該出現在 MetricSet 中的每個 Metric 上。
MetricSet 中不需要對 MetricFamily 進行特定排序。公開者可以讓公開內容更易於人類閱讀,例如,如果效能權衡合理,可以按字母順序排序。
如果存在,根據下文在推拉式系統中支援目標元資料一節,名為 "target_info" 的 Info MetricFamily 應該 (SHOULD) 排在首位。
MetricFamily 型別
Gauge(儀表盤)
Gauges(儀表盤)是當前的測量值,例如當前使用的記憶體位元組數或佇列中的專案數。對於 Gauges,絕對值是使用者感興趣的。
型別為 gauge 的指標中的樣本必須 (MUST) 具有 Number 值。
儀表盤可能會隨著時間的推移增加、減少或保持不變。即使它們只朝一個方向變化,它們仍然可能是儀表盤而不是計數器。日誌檔案的大小通常只會增加,資源可能會減少,而佇列大小的限制可能是恆定的。
Counter(計數器)
計數器(Counters)測量離散事件。常見的例子是接收到的 HTTP 請求數、花費的 CPU 秒數或傳送的位元組數。對於計數器,使用者感興趣的是它們隨時間增加的速度。
Counter 型別的 MetricFamily 名稱應該 (SHOULD) 以 _total 結尾。暴露沒有 _total 字尾的指標可能會因對指標型別的混淆而降低可用性。
型別為 Counter 的指標中的樣本應該 (SHOULD) 具有開始時間戳。
型別為 Counter 的指標中的樣本必須 (MUST) 具有非 NaN 的 Number 值。該值必須 (MUST) 隨時間單調不減,除非它被重置為 0,並從 0 開始。該值可以 (MAY) 將其值重置為 0。如果存在,對應的開始時間戳也必須 (MUST) 設定為近似的重置時間。
型別為 Counter 的指標中的樣本可以 (MAY) 具有 exemplars。
狀態集
StateSets(狀態集)表示一系列相關的布林值,也稱為位集。如果需要編碼列舉,可以透過 StateSet 來實現。
StateSet 被構造為一組指標,每個狀態一個指標,稱為 StateSet MetricGroup。
注意在 OpenMetrics 1.0 中,指標由 MetricPoint 組成(例如,Histogram 指標有一個 MetricPoint 代表每個帶有特殊“le”標籤的 Bucket),但在 OpenMetrics 2.0 中不再如此。OpenMetrics 1.0 StateSet Metric 等同於 OpenMetrics 2.0 StateSet MetricGroup,而 OpenMetrics 1.0 StateSet MetricPoint 等同於 OpenMetrics 2.0 StateSet Metric。
StateSet MetricGroup 包含一個或多個狀態,並且必須 (MUST) 為每個狀態包含一個具有布林值的指標。狀態具有一個作為字串的名稱。
如果編碼為 StateSet,ENUM 必須 (MUST) 在 MetricGroup 中,對於單個時間戳,恰好有一個樣本為 1 (真)。
這適用於列舉值隨時間變化,且狀態數量不超過少數的情況。
StateSets 型別的 MetricFamily 必須 (MUST) 具有空的 Unit 字串。
資訊
資訊指標用於公開在程序生命週期內不應更改的文字資訊。常見示例包括應用程式的版本、修訂控制提交以及編譯器的版本。
Info 指標的 MetricFamily 名稱必須 (MUST) 以 _info 結尾。
Info 型別的 MetricFamily 必須 (MUST) 具有空的 Unit 字串。
Histogram(直方圖)
直方圖測量離散事件的分佈。常見示例包括 HTTP 請求的延遲、函式執行時間或 I/O 請求大小。
Histogram 樣本必須 (MUST) 包含 Count 和 Sum。
Count 值必須 (MUST) 等於 Histogram 所測量的數量。Count 在語義上是一個計數器。Count 應該 (SHOULD) 是一個整數。Count 不得 (MUST NOT) 為負。Count 不應該 (SHOULD NOT) 是 +Inf、NaN。
允許浮點 Count,以便暴露直方圖算術運算的結果,例如加法可能會導致超出整數範圍的值。
Sum 值必須 (MUST) 等於所有測量的事件值的總和。只要 Histogram 沒有測量到負事件值,Sum 在語義上才是一個計數器。
Histogram 必須 (MUST) 在經典桶或原生桶或兩者中測量非 NaN 值。測量 NaN 對於經典桶和原生桶有所不同,請參閱各自的部分。
每個桶必須 (MUST) 具有明確定義的邊界和值。桶值通常被稱為桶計數。桶的邊界不得 (MUST NOT) 為 NaN。桶值在語義上是計數器。桶值應該 (SHOULD) 是整數。桶值不得 (MUST NOT) 為負。桶值不應該 (SHOULD NOT) 是 +Inf、NaN。
允許浮點桶值,以便暴露直方圖算術運算的結果,例如加法可能會導致超出整數範圍的值。
Histogram 不應該 (SHOULD NOT) 包含 NaN 測量值,因為在 Sum 中包含 NaN 會使 Sum 等於 NaN,並掩蓋時間序列生命週期內真實測量的總和。如果 Histogram 包含 NaN 測量值,則 NaN 測量值必須 (MUST) 計入 Count 中,並且 Sum 必須 (MUST) 為 NaN。
如果 Histogram 包含 +Inf 或 -Inf 測量值,則 +Inf 或 -Inf 必須 (MUST) 計入 Count 中,並且必須 (MUST) 新增到 Sum 中,這可能會導致 Sum 為 +Inf、-Inf 或 NaN,例如將 +Inf 新增到 -Inf 的情況。請注意,在這種情況下,有限測量的 Sum 將被掩蓋,直到 Histogram 下次重置。
Histogram 樣本應該 (SHOULD) 具有開始時間戳。
如果 Histogram 指標的樣本具有經典桶,則 Histogram 的指標的 LabelSet 不得 (MUST NOT) 具有“le”標籤名稱,因為如果樣本以帶有 _bucket 字尾的經典直方圖序列儲存,則 Histogram 中的“le”標籤將與從桶閾值生成的“le”標籤衝突。
Histogram 型別是隨時間累積的,但可以 (MAY) 重置。當 Histogram 重置時,Sum、Count、經典桶和原生桶必須 (MUST) 重置為其零狀態,如果存在開始時間戳,則必須 (MUST) 將其設定為近似重置時間。Histogram 重置對於限制 Histogram 使用的原生桶的數量很有用。
Histogram 樣本可以 (MAY) 具有 exemplars。Histogram 樣本中 exemplars 的值應該 (SHOULD) 均勻分佈,例如,如果包含經典桶,則為每個經典桶保留一個 exemplar。
經典桶 (Classic Buckets)
每個經典桶必須 (MUST) 具有一個閾值。樣本中的經典桶閾值必須 (MUST) 是唯一的。經典桶閾值可以 (MAY) 為負。
經典桶必須 (MUST) 統計小於或等於其閾值的測量值數量,包括在較低桶中也被計數的測量值。這允許監控系統出於效能或反拒絕服務的原因丟棄除 +Inf 桶之外的任何桶,這種方式會損失粒度但仍然是一個有效的直方圖。
例如,對於一個表示以秒為單位的請求延遲的指標,具有經典桶和閾值 1、2、3 和 +Inf,則 value_1 <= value_2 <= value_3 <= value_+Inf。如果十個請求每個耗時一秒,則 1、2、3 和 +Inf 桶的值都將等於 10。
具有經典桶的 Histogram 樣本必須 (MUST) 具有一個帶有 +Inf 閾值的經典桶。+Inf 桶計數所有測量值。Count 值必須 (MUST) 等於 +Inf 桶的值。
暴露的經典桶閾值應該 (SHOULD) 在隨時間變化以及指標旨在被聚合的目標之間保持不變。閾值的改變可能會阻止受影響的直方圖參與相同的操作(例如,不同指標的聚合或隨時間變化的速率計算)。
如果允許 NaN 值,它必須 (MUST) 計入 +Inf 桶中,並且不得 (MUST NOT) 計入任何其他桶中。理由是 NaN 在數學上不屬於任何桶,但埋點庫傳統上將其放入 +Inf 桶中。
原生桶 (Native Buckets)
具有原生桶的 Histogram 樣本必須 (MUST) 具有 Schema 值。Schema 必須 (MUST) 是 -4 到 8(含)之間的 8 位帶符號整數,這些被稱為標準(指數)Schema。
超出 -4 到 8 範圍的 Schema 值保留供將來使用,不得 (MUST NOT) 使用。
對於任何標準 Schema n,Histogram 樣本可以 (MAY) 包含正向和/或負向原生桶,並且必須 (MUST) 包含一個零原生桶。不應該 (SHOULD NOT) 存在空的正向或負向原生桶。
在標準 Schema 的情況下,索引為 i 的正向或負向原生桶的邊界必須 (MUST) 按以下方式計算(使用 Python 語法)
正向原生桶的上限(包含):(2**2**-n)**i
正向原生桶的下限(不包含):(2**2**-n)**(i-1)
負向原生桶的下限(包含):-((2**2**-n)**i)
負向原生桶的上限(不包含):-((2**2**-n)**(i-1))
i 是一個整數,可以 (MAY) 為負。
上述規則有一些例外,涉及可表示為 float64 的最大和最小有限值(稱為 MaxFloat64 和 MinFloat64)以及正負無窮大值(+Inf 和 -Inf)
包含 MaxFloat64 的正向原生桶(根據上述邊界公式)的上限是 MaxFloat64(而不是根據上述公式計算出的會溢位 float64 的限制)。
下一個正向原生桶(相對於前一項的桶,索引為 i+1)的下限(不包含)是 MaxFloat64,上限(包含)是 +Inf。(可以稱之為正向原生溢位桶。)
包含 MinFloat64 的負向原生桶(根據上述邊界公式)的下限是 MinFloat64(而不是根據上述公式計算出的會下溢 float64 的限制)。
下一個負向原生桶(相對於前一項的桶,索引為 i+1)的上限(不包含)是 MinFloat64,下限(包含)是 -Inf。(可以稱之為負向原生溢位桶。)
不得 (MUST NOT) 使用超出上述 +Inf 和 -Inf 桶的原生桶。
零原生桶的邊界是 [-threshold, threshold](包含)。零閾值必須 (MUST) 是一個非負 float64 值 (threshold >= 0.0)。
如果零閾值為正 (threshold > 0),則任何落入零原生桶的測量值必須 (MUST) 計入零原生桶,並且不得 (MUST NOT) 計入任何其他原生桶。零閾值應該 (SHOULD) 等於任意原生桶的下限。
如果不允許 NaN 值,則 Count 值必須 (MUST) 等於負向、正向和零原生桶的總和。
如果允許 NaN 值,則不得 (MUST NOT) 將其計入任何原生桶中,並且必須 (MUST) 計入 Count 中。Count 與負向、正向和零原生桶總和之間的差值必須 (MUST) 是 NaN 觀測值的數量。理由是 NaN 在數學上不屬於任何桶。
儀表盤直方圖
GaugeHistograms(儀表盤直方圖)測量當前的分佈。常見的例子是專案在佇列中等待了多長時間,或者佇列中請求的大小。
GaugeHistogram 樣本必須 (MUST) 包含 Gcount、Gsum 值。
Gcount 值必須 (MUST) 等於 GaugeHistogram 中當前的測量數量。Gcount 在語義上是一個 gauge。Gcount 應該 (SHOULD) 是一個整數。Gcount 不應該 (SHOULD NOT) 是 -Inf、+Inf、NAN 或負數。
允許浮點和負數 Gcount,以便暴露 GaugeHistogram 算術運算的結果,例如直方圖隨時間的變化率。
Gsum 值必須 (MUST) 等於 GaugeHistogram 中當前所有測量值的總和。Gsum 在語義上是一個 gauge。
GaugeHistogram 必須 (MUST) 在經典桶或原生桶或兩者中測量非 NaN 值。測量 NaN 對於經典桶和原生桶有所不同,請參閱各自的部分。
如果 GaugeHistogram 停止測量經典桶或原生桶中的值,並繼續測量另一種桶中的值,它必須 (MUST) 清除並不得暴露其停止測量的桶。這可以避免同時暴露兩種桶的不同分佈。
每個桶必須 (MUST) 具有明確定義的邊界和值。桶的邊界不得 (MUST NOT) 為 NaN。桶值應該 (SHOULD) 是整數。在語義上,桶值是 gauge,不應該 (SHOULD NOT) 是 -Inf、+Inf、NaN 或負數。
允許浮點和負數桶值,以便暴露 GaugeHistogram 算術運算的結果,例如直方圖隨時間的變化率。
GaugeHistogram 不應該 (SHOULD NOT) 包含 NaN 測量值。如果 GaugeHistogram 包含 NaN 測量值,則 NaN 測量值必須 (MUST) 計入 Gcount 中,並且 Gsum 必須 (MUST) 為 NaN。
如果 GaugeHistogram 包含 +Inf 或 -Inf 測量值,則 +Inf 或 -Inf 必須 (MUST) 計入 Gcount 中,並且必須 (MUST) 新增到 Gsum 中,這可能會導致 Gsum 為 +Inf、-Inf 或 NaN,例如將 +Inf 新增到 -Inf 的情況。
如果 GaugeHistogram 指標的樣本具有經典桶,則 GaugeHistogram 的指標的 LabelSet 不得 (MUST NOT) 具有“le”標籤名稱,因為如果樣本以帶有 _bucket 字尾的經典直方圖序列儲存,則 GaugeHistogram 中的“le”標籤將與從桶閾值生成的“le”標籤衝突。
GaugeHistogram 的經典桶和原生桶遵循與 Histogram 相同的所有規則,Gcount 和 Gsum 扮演著與 Count 和 Sum 相同的角色。
GaugeHistogram 的 exemplars 遵循與 Histogram 相同的所有規則。
總結
摘要也測量離散事件的分佈,當 Histogram 過於昂貴且少量預計算的分位數足夠時,可以 (MAY) 使用。
不應該 (SHOULD NOT) 使用摘要,因為分位數不可聚合,並且使用者通常無法推斷它們涵蓋的時間範圍。可以 (MAY) 使用它們以實現向後相容,因為一些現有的埋點庫暴露預計算的分位數而不支援 Histogram。
Summary 樣本必須 (MUST) 包含 Count、Sum 和一組分位數。
在語義上,Count 和 Sum 的值是計數器,因此不能是 NaN 或負數。Count 必須是整數。
Summary 應該 (SHOULD) 具有開始時間戳。
開始時間戳不得 (MUST NOT) 基於分位數值的收集週期。
分位數是從分位數到值的對映。例如,在名為 myapp_http_request_duration_seconds 的指標中,分位數 0.95 的值為 0.2,這意味著在未知時間範圍內,第 95 百分位延遲為 200ms。如果在相關時間範圍內沒有事件,則分位數的值必須 (MUST) 為 NaN。分位數的指標的 LabelSet 不得 (MUST NOT) 包含“quantile”標籤名稱。分位數必須 (MUST) 介於 0 和 1 之間(包含)。分位數的值不得 (MUST NOT) 為負。分位數的值應該 (SHOULD) 代表最近的值。通常,這將是過去 5-10 分鐘內的值。
未知
不應使用 Unknown。當無法從第三方系統確定單個指標的型別時,可以使用 Unknown。
未知型別指標中的樣本必須 (MUST) 具有 Number 或 CompositeValue 值。
文字格式
OpenMetrics 格式是正則喬姆斯基文法,使得編寫快速而小的解析器成為可能。
部分或無效的暴露必須 (MUST) 被視為完全錯誤。
注意OpenMetrics 的先前版本曾指定一種 OpenMetric protobuf 格式 。OpenMetrics 2.0 不包括 protobuf 表示。有關可用格式,包括官方的 Prometheus protobuf 線上傳輸格式,請參閱暴露格式文件。
協議協商
所有攝取器實現必須 (MUST) 能夠攝取使用 TLS 1.2 或更高版本保護的資料,並且應該 (SHOULD) 支援 TLS 1.3 或更高版本。所有暴露器應該 (SHOULD) 能夠發出使用 TLS 1.3 或更高版本保護的資料。攝取器實現應該 (SHOULD) 能夠從沒有 TLS 的 HTTP 攝取資料。所有實現應該 (SHOULD) 使用 TLS 傳輸資料。
協商使用哪個版本的 OpenMetrics 格式是帶外的。例如,對於透過 HTTP 的拉取式公開,使用標準的 HTTP 內容型別協商,如果未請求更新版本,則必須預設為標準的最舊版本(即 1.0.0)。
基於推送的協商本質上更復雜,因為通常是公開者發起連線。生產者必須使用標準的最舊版本(即 1.0.0),除非接收者另有要求。
ABNF
ABNF 遵從 RFC 7405。
RFC 7405 基於 RFC 5234 構建,但為字串字面量增加了顯式的區分大小寫表示法。字面量 %s"text" 表示 text 區分大小寫,而 %i"text" 表示不區分大小寫。
"exposition" 是 ABNF 的頂層令牌。
exposition = metricset HASH SP %s"EOF" [ LF ]
metricset = *metricfamily
metricfamily = *metric-descriptor *sample
metric-descriptor = HASH SP %s"TYPE" SP (metricname / metricname-utf8) SP metric-type LF
metric-descriptor =/ HASH SP %s"HELP" SP (metricname / metricname-utf8) SP escaped-string LF
metric-descriptor =/ HASH SP %s"UNIT" SP (metricname / metricname-utf8) SP *metricname-char LF
metric-type = %s"counter" / %s"gauge" / %s"histogram" / %s"gaugehistogram" / %s"stateset"
metric-type =/ %s"info" / %s"summary" / %s"unknown"
sample = metricname-and-labels SP value [SP timestamp] [SP start-timestamp] *exemplar LF
value = number / "{" composite-value "}"
timestamp = realnumber
; Lowercase st @ timestamp
start-timestamp = %s"st" "@" timestamp
exemplar = SP HASH SP labels-in-braces SP number SP timestamp
metricname-and-labels = metricname [labels-in-braces] / name-and-labels-in-braces
labels-in-braces = "{" [label *(COMMA label)] "}"
name-and-labels-in-braces = "{" metricname-utf8 *(COMMA label) "}"
label = label-key "=" DQUOTE escaped-string DQUOTE
; Number value
number = realnumber
; Case insensitive
number =/ [SIGN] (%i"inf" / %i"infinity")
number =/ %i"nan"
; Real floats
; Leading 0s explicitly okay
realnumber = [SIGN] 1*DIGIT ["." *DIGIT] [ "e" [SIGN] 1*DIGIT ]
realnumber =/ [SIGN] *DIGIT "." 1*DIGIT [ "e" [SIGN] 1*DIGIT ]
; Integers
; Leading 0s explicitly okay
integer = [SIGN] 1*"0" / [SIGN] positive-integer
non-negative-integer = ["+"] 1*"0" / ["+"] positive-integer
positive-integer = *"0" positive-digit *DIGIT
positive-digit = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9"
BS = "\"
COMMA = ","
HASH = "#"
SIGN = "-" / "+"
metricname = metricname-initial-char 0*metricname-char
metricname-char = metricname-initial-char / DIGIT
metricname-initial-char = ALPHA / "_" / ":"
metricname-utf8 = DQUOTE escaped-string-non-empty DQUOTE
label-key = label-name / DQUOTE escaped-string-non-empty DQUOTE
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-string-non-empty = 1*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
; Composite values
composite-value = histogram-value / gauge-histogram-value / summary-value
; Histograms
histogram-value = h-count "," h-sum "," histogram-buckets
gauge-histogram-value = %s"g" h-count "," %s"g" h-sum "," histogram-buckets
; count:x
h-count = %s"count" ":" number
; sum:f allows real numbers and +-Inf and NaN
h-sum = %s"sum" ":" number
histogram-buckets = classic-buckets / native-buckets [ "," classic-buckets ]
; bucket:[...,+Inf:v] The +Inf bucket is required.
classic-buckets = %s"bucket" ":" "[" [ ch-le-counts "," ] ch-pos-inf-bucket "]"
ch-le-counts = (ch-neg-inf-bucket / ch-le-bucket) *("," ch-le-bucket)
ch-pos-inf-bucket = "+" %s"Inf" ":" number
ch-neg-inf-bucket = "-" %s"Inf" ":" number
ch-le-bucket = realnumber ":" number
; schema:3,zero_threshold:1e-128,zero_count:2,negative_spans:[1:1],negative_buckets:[2],positive_spanes:[-3:1,2:2],positive_buckets:[3,1,0]
native-buckets = nh-schema "," nh-zero-threshold "," nh-zero-count [ "," nh-negative-spans "," nh-negative-buckets ] [ "," nh-positive-spans "," nh-positive-buckets ]
; schema:i
nh-schema = %s"schema" ":" integer
; zero_threshold:f
nh-zero-threshold = %s"zero_threshold" ":" realnumber
; zero_count:x
nh-zero-count = %s"zero_count" ":" number
; negative_spans:[1:2,3:4] and positive_spans:[-3:1,2:2]
nh-negative-spans = %s"negative_spans" ":" "[" [nh-spans] "]"
nh-positive-spans = %s"positive_spans" ":" "[" [nh-spans] "]"
; Spans hold offset and length. The offset can start from any index, even
; negative, however subsequent spans can only advance the index, not decrease it.
nh-spans = nh-start-span *("," nh-span)
nh-start-span = integer ":" non-negative-integer
nh-span = non-negative-integer ":" non-negative-integer
; negative_buckets:[1,2,3] and positive_buckets:[1,2,3]
nh-negative-buckets = %s"negative_buckets" ":" "[" [nh-buckets] "]"
nh-positive-buckets = %s"positive_buckets" ":" "[" [nh-buckets] "]"
nh-buckets = number *("," number)
; Summary
; count:12.0,sum:100.0,quantile:[0.9:2.0,0.95:3.0,0.99:20.0]
summary-value = cs-count "," cs-sum "," cs-quantile
; count:x where x is a number
cs-count = %s"count" ":" number
; sum:x where x is a real number or +-Inf or NaN
cs-sum = %s"sum" ":" number
; quantile:[...]
cs-quantile = %s"quantile" ":" "[" [ cs-q-counts ] "]"
cs-q-counts = cs-q-count *("," cs-q-count)
cs-q-count = realnumber ":" number
總體結構
必須 (MUST) 使用 UTF-8。不得 (MUST NOT) 使用位元組順序標記 (BOM)。請注意,NULL(位元組 0x00)是一個有效的 UTF-8 位元組,而位元組 0xFF(例如)則不是。
內容型別必須是
application/openmetrics-text; version=2.0.0; charset=utf-8
行尾必須 (MUST) 用換行符 (\n) 表示,並且不得 (MUST NOT) 包含回車符 (\r)。暴露必須 (MUST) 以 EOF 結尾,並且應該 (SHOULD) 以 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{path="/api/v1",method="GET"} {count:807283,sum:9036.32,quantile:[0.95:2,0.99:20]} [email protected]
acme_http_router_request_seconds{path="/api/v2",method="GET"} {count:34,sum:479.3,quantile:[0.95:2.5,0.99:2.9]} [email protected]
# TYPE go_goroutines gauge
# HELP go_goroutines Number of goroutines that currently exist.
go_goroutines 69
# TYPE process_cpu_seconds_total counter
# UNIT process_cpu_seconds_total seconds
# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.
process_cpu_seconds_total 4.20072246e+06
# TYPE acme_http_request_seconds histogram
# UNIT acme_http_request_seconds seconds
# HELP acme_http_request_seconds Latency histogram of all of ACME's HTTP requests.
acme_http_request_seconds{path="/api/v1",method="GET"} {count:2,sum:1.2e2,schema:0,zero_threshold:1e-4,zero_count:0,positive_spans:[1:2],positive_buckets:[1,1],bucket:[0.5:1,1:2,+Inf:2]} [email protected]
# TYPE acme_http_request_seconds:rate5m gaugehistogram
acme_http_request_seconds:rate5m{path="/api/v1",method="GET"} {gcount:0.01,gsum:2.0,schema:0,zero_threshold:1e-4,zero_count:0.0,positive_spans:[1:2],positive_buckets:[0.005,0.005]}
# TYPE "foodb.read.errors" counter
# HELP "foodb.read.errors" The number of errors in the read path for fooDb.
{"foodb.read.errors","service.name"="my_service"} 3482
# EOF
UTF-8 引用
不符合 ABNF metricname 定義的指標名稱必須 (MUST) 用雙引號括起來,並且必須 (MUST) 使用替代的 UTF-8 語法。在這些指標中,帶引號的指標名稱必須 (MUST) 按照 ABNF 移到括號內作為第一個專案,沒有標籤名稱和等號。指標名稱必須 (MUST) 在 TYPE、UNIT 和 HELP 行中用雙引號括起來。引用和替代指標語法可以 (MAY) 用於任何指標名稱,無論該名稱是否需要引用。
不符合 label-name ABNF 定義的標籤名稱必須 (MUST) 用雙引號括起來。任何標籤名稱都可以 (MAY) 用雙引號括起來。
用正則表示式表示,不需要用引號括起來的指標名稱匹配:^[a-zA-Z_:][a-zA-Z0-9_:]*$。對於標籤名稱,字串匹配:^[a-zA-Z_][a-zA-Z0-9_]*$。
完整示例
# 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","node.name"="my_node"} 4.20072246e+06
# TYPE "quoting_example" gauge
# HELP "quoting_example" Number of goroutines that currently exist.
{"quoting_example","foo"="bar"} 4.5
# EOF
轉義
在 ABNF 註明轉義的地方,必須 (MUST) 應用以下轉義:
- 換行符,
\n(0x0A) -> 字面量\n(位元組碼 0x5c 0x6e) - 雙引號 ->
\"(位元組碼 0x5c 0x22) - 反斜槓 ->
\\(位元組碼 0x5c 0x5c)
應該 (SHOULD) 使用雙反斜槓來表示反斜槓字元。不應該 (SHOULD NOT) 將單個反斜槓用於未定義的轉義序列。例如,\\a 等效於 \a 並且更可取。
轉義也必須 (MUST) 應用於帶引號的 UTF-8 字串。
數字
整數不得包含小數點。例如 23、0042 和 1341298465647914。
浮點數必須用小數點或科學記數法表示。例如 8903.123421 和 1.89e-7。浮點數必須在 IEEE 754 定義的 64 位浮點值範圍內,但可能會因為尾數位數過多而導致精度損失。這可以用於編碼納秒級解析度的時間戳。
複合值 (CompositeValues)
CompositeValue 表示為帶欄位的結構化資料。欄位周圍不得 (MUST NOT) 有任何空格。有關格式和可能值的詳細資訊,請參閱 ABNF。
時間戳
如果需要納秒級精度,時間戳不應該使用浮點數的指數表示法,因為 float64 的表示沒有足夠的精度,例如 1604676851.123456789。
Exemplar
沒有標籤的 Exemplar 必須用 {} 表示一個空的 LabelSet。
MetricFamily
MetricFamily 之間不得 (MUST NOT) 有明確的分隔符。下一個 MetricFamily 必須 (MUST) 透過元資料或新 MetricFamily 的新指標名稱來表示。
MetricFamily 不得交錯出現。
同一個 MetricFamily 的名稱和 Metric 的名稱應該 (SHOULD) 具有相同的引用方式。
違反此規則的示例
# TYPE "read_errors" counter
# HELP read_errors The number of errors in the read path for fooDb.
{"read_errors","service.name"="my_service"} 3482
read_errors{"service.name"="my_service2"} 123
MetricFamily 元資料
元資料有四個部分:MetricFamily 名稱、TYPE、UNIT 和 HELP。一個名為 foo_total 的計數器指標的元資料示例如下:
# TYPE foo_total counter
如果未暴露 TYPE,MetricFamily 必須 (MUST) 被解釋為 Unknown 型別。
如果指定了單位,則必須 (MUST) 在 UNIT 元資料行中提供。此外,下劃線和單位應該 (SHOULD) 是 MetricFamily 名稱的字尾(或計數器 _total 前的中間部分)。
請注意,直接向終端使用者暴露沒有單位作為 MetricFamily 名稱字尾(或中間部分)的指標,可能會因對指標單位的混淆而降低可用性。
一個有效的示例,一個單位為“秒”的 foo_seconds_total 指標
# TYPE foo_seconds_total counter
# UNIT foo_seconds_total seconds
一個有效但(不推薦的)示例,其中單位不是名稱的字尾也不是中間部分
# TYPE foo_total counter
# UNIT foo_total seconds
以下也是有效的:
# TYPE foo_seconds_total counter
如果單位已知,則應該提供。
UNIT 或 HELP 元資料行可以 (MAY) 在換行符前有一個空值字串。這必須 (MUST) 被視為元資料行不存在。
完整示例
# TYPE foo_seconds_total counter
# UNIT foo_seconds_total seconds
# HELP foo_seconds_total Some text and \n some \" escaping
有關指標名稱必須 (MUST) 用雙引號括起來的情況,請參閱UTF-8 引用部分。
對於一個 MetricFamily,每種型別的元資料行不得超過一個。順序應該是 TYPE、UNIT、HELP。
除了這些元資料和訊息末尾的 EOF 行之外,不得暴露以 # 開頭的行。
未知元資料
攝取器必須 (MUST) 支援沒有元資料行的 Metric Family。
一個有效但(不推薦的)示例,用於 foo_seconds_total 計數器和一組不相關的、未知型別的且沒有元資料行的指標
# TYPE foo_seconds_total counter
foo_seconds_total 1
foo_milliseconds_total 2
foo_count 3
指標
指標不得 (MUST NOT) 交錯。請參閱下面的 StateSet 示例。
沒有標籤或時間戳且值為 0 的樣本必須 (MUST) 呈現為以下形式之一:
bar_seconds_count 0
或
bar_seconds_count{} 0
標籤值可以是任何有效的 UTF-8 值,因此必須按照 ABNF 的規定進行轉義。一個帶有兩個標籤的有效示例:
bar_seconds_count{a="x",b="escaping\" example \n "} 0
指標名稱和標籤名稱也可以 (MAY) 是任何有效的 UTF-8 值,並且在某些情況下,它們必須 (MUST) 根據 ABNF 進行引用和轉義。有關具體資訊,請參閱UTF-8 引用部分。
{"\"bar\".seconds.count","b\\"="escaping\" example \n "} 0
指標型別
Gauge
樣本的值必須 (MUST) 是 Number。
對於 Gauge 型別的 MetricFamily,其 MetricFamily 名稱沒有推薦的字尾。
一個 MetricFamily 的示例,其中包含一個沒有標籤的指標和一個沒有時間戳的樣本
# TYPE foo gauge
foo 17.0
一個 MetricFamily 的示例,其中包含兩個帶標籤的指標和沒有時間戳的樣本
# TYPE foo gauge
foo{a="bb"} 17.0
foo{a="ccc"} 17.0
一個 MetricFamily 示例,不含任何 Metric:
# TYPE foo gauge
一個帶標籤的指標和一個帶時間戳的樣本的示例
# TYPE foo gauge
foo{a="b"} 17.0 1520879607.789
一個沒有標籤的指標和一個帶時間戳的樣本的示例
# TYPE foo gauge
foo 17.0 1520879607.789
一個沒有標籤的指標和兩個帶時間戳的樣本的示例
# TYPE foo gauge
foo 17.0 123
foo 18.0 456
Counter
樣本的值必須 (MUST) 是 Number。
如果存在,樣本的開始時間戳必須 (MUST) 以 st@ 字首與樣本內聯。如果值的時間戳存在,開始時間戳必須 (MUST) 緊隨其後。如果 exemplar 存在,開始時間戳必須 (MUST) 在其之前新增。
一個沒有標籤的指標,以及一個沒有時間戳和沒有開始時間戳的樣本的示例
# TYPE foo_total counter
foo_total 17.0
一個沒有標籤的指標,以及一個帶時間戳但沒有開始時間戳的樣本的示例
# TYPE foo_total counter
foo_total 17.0 1520879607.789
一個沒有標籤的指標,以及一個沒有時間戳但帶開始時間戳的樣本的示例
# TYPE foo_total counter
foo_total 17.0 [email protected]
一個沒有標籤的指標,以及一個帶時間戳和帶開始時間戳的樣本的示例
# TYPE foo_total counter
foo_total 17.0 1520879607.789 [email protected]
一個沒有標籤的指標,沒有 _total 字尾,以及一個帶時間戳和帶開始時間戳的樣本的示例
# TYPE foo counter
foo 17.0 1520879607.789 [email protected]
請注意,直接向終端使用者暴露 MetricFamily 名稱中不帶 _total 字尾的指標,可能會因對指標型別的混淆而降低可用性。
樣本可以 (MAY) 具有 Exemplar。
一個沒有標籤的指標,以及一個帶時間戳、帶開始時間戳和帶 Exemplar 的樣本的示例
# TYPE foo_total counter
foo_total 17.0 1520879607.789 [email protected] # {trace_id="KOO5S4vxi0o"} 0.67 1520879606.1
StateSet
對於 StateSet 型別的 MetricFamily,其 MetricFamily 名稱沒有推薦的字尾。
StateSet MetricGroup 中的每個狀態必須 (MUST) 有一個指標。每個狀態的指標必須 (MUST) 有一個標籤,其標籤名稱為 MetricFamily 名稱,標籤值為狀態名稱。如果狀態為真,指標樣本的值必須 (MUST) 為 1;如果狀態為假,則必須 (MUST) 為 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
StateSet MetricGroup 不得 (MUST NOT) 交錯。
一個正確的示例,其中一個 MetricFamily 中有多個 MetricGroup,每個 MetricGroup 中有多個指標,每個指標中有多個樣本
# TYPE foo stateset
foo{entity="controller",foo="a"} 1.0 1000000000.000
foo{entity="controller",foo="a"} 0.0 1000000001.000
foo{entity="controller",foo="bb"} 0.0 1000000000.000
foo{entity="controller",foo="bb"} 1.0 1000000001.000
foo{entity="controller",foo="ccc"} 0.0 1000000000.000
foo{entity="controller",foo="ccc"} 0.0 1000000001.000
foo{entity="replica",foo="a"} 1.0 1000000000.000
foo{entity="replica",foo="a"} 1.0 1000000001.000
foo{entity="replica",foo="bb"} 0.0 1000000000.000
foo{entity="replica",foo="bb"} 1.0 1000000001.000
foo{entity="replica",foo="ccc"} 0.0 1000000000.000
foo{entity="replica",foo="ccc"} 0.0 1000000001.000
一個 MetricGroup 交錯的錯誤示例
# TYPE foo stateset
foo{entity="controller",env="dev",foo="a"} 1.0
foo{entity="controller",env="dev",foo="bb"} 0.0
foo{entity="controller",env="dev",foo="ccc"} 0.0
foo{entity="replica",env="dev",foo="a"} 1.0
foo{entity="replica",env="dev",foo="bb"} 0.0
foo{entity="replica",env="dev",foo="ccc"} 1.0
foo{entity="controller",env="prod",foo="a"} 1.0
foo{entity="controller",env="prod",foo="bb"} 0.0
foo{entity="controller",env="prod",foo="ccc"} 0.0
一個不正確的示例,其中 Metrics 交錯出現:
# TYPE foo_seconds summary
# UNIT foo_seconds seconds
# TYPE foo stateset
foo{entity="controller",env="dev",foo="a"} 1.0
foo{entity="controller",env="dev",foo="bb"} 0.0
foo{entity="controller",env="prod",foo="a"} 1.0
foo{entity="controller",env="dev",foo="ccc"} 0.0
foo{entity="controller",env="prod",foo="bb"} 0.0
foo{entity="controller",env="prod",foo="ccc"} 0.0
Info
樣本值必須 (MUST) 始終為 1。
一個沒有標籤的指標,以及一個帶“name”和“version”標籤的樣本值的示例
# TYPE foo_info info
foo_info{name="pretty name",version="8.2.7"} 1
一個帶標籤“entity”的指標,以及一個帶“name”和“version”標籤的樣本值的示例
# TYPE foo_info 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
指標標籤和樣本值標籤可以 (MAY) 以任何順序出現。
總結
樣本的值必須 (MUST) 是 CompositeValue。
CompositeValue 必須 (MUST) 包含 Count、Sum 和分位數作為欄位 count、sum、quantile,按此順序排列。
如果存在,樣本的開始時間戳必須 (MUST) 以 st@ 字首與樣本內聯。如果值的時間戳存在,開始時間戳必須 (MUST) 緊隨其後。如果 exemplars 存在,開始時間戳必須 (MUST) 在其之前新增。
分位數必須 (MUST) 按分位數遞增的順序排序。
一個沒有標籤的指標和帶有 Sum、Count 和開始時間戳的樣本的示例
# TYPE foo summary
foo {count:17,sum:324789.3,quantile:[]} [email protected]
一個沒有標籤的指標和帶有兩個分位數和開始時間戳的樣本的示例
# TYPE foo summary
foo {count:0,sum:0.0,quantile:[0.95:123.7,0.99:150]} [email protected]
帶有經典桶的 Histogram
樣本的值必須 (MUST) 是 CompositeValue。
CompositeValue 必須 (MUST) 包含 Count、Sum 和經典桶值作為欄位 count、sum、bucket,按此順序排列。
如果存在,樣本的開始時間戳必須 (MUST) 以 st@ 字首與樣本內聯。如果值的時間戳存在,開始時間戳必須 (MUST) 緊隨其後。如果 exemplars 存在,開始時間戳必須 (MUST) 在其之前新增。
經典桶必須 (MUST) 按閾值的數字遞增順序排序。
所有經典桶必須 (MUST) 存在,即使值是 0 的桶也必須存在。
一個沒有標籤的指標的示例,以及一個帶有 Sum、Count 和開始時間戳值以及 12 個經典桶的樣本。特意展示了各種廣泛且非典型但有效的桶閾值。
# TYPE foo histogram
foo {count:17,sum:324789.3,bucket:[0.0:0,1e-05:0,0.0001:5,0.1:8,1.0:10,10.0:11,100000.0:11,1e+06:15,1e+23:16,1.1e+23:17,+Inf:17]} [email protected]
帶有原生桶的 Histogram
樣本的值必須 (MUST) 是 CompositeValue。
CompositeValue 必須 (MUST) 包含 Count、Sum、Schema、零閾值、零原生桶值作為欄位 count、sum、schema、zero_threshold、zero_count,按此順序排列。
如果沒有負向原生桶,則應該 (SHOULD) 省略欄位 negative_spans 和 negative_buckets。如果沒有正向原生桶,則應該 (SHOULD) 省略欄位 positive_spans 和 positive_buckets。
如果存在負值(和/或正值)原生桶,則欄位 negative_spans、negative_buckets(和/或 positive_spans、positive_buckets)必須按此順序出現在 zero_count 欄位之後。
原生桶的值必須按其索引排序,並且其值必須放置在 negative_buckets(和/或 positive_buckets)欄位中。
注意桶值是絕對計數,而不是某些將桶值儲存為相對於前一個桶的增量的實現。
值為 0 的原生桶不應存在。
為了將 negative_buckets(和/或 positive_buckets)映射回其索引,必須按以下方式構建 negative_spans(和/或 positive_spans)欄位:每個跨度由一對數字組成,一個稱為偏移量 (offset) 的整數和一個稱為長度 (length) 的非負整數。每個列表中的第一個跨度可以具有負偏移量。它定義了其相應 negative_buckets(和/或 positive_buckets)中第一個桶的索引。長度定義了桶列表開始時連續桶的數量。後續跨度的偏移量定義了排除(因此未填充)桶的數量。長度定義了排除桶之後列表中連續桶的數量。
保留空的正面或負面原生桶的一個示例是減少表示兩個跨度之間偏移量僅為 1 的情況所需的跨度數量,這意味著透過包含一個空桶,跨度數量減少了一個。
每個跨度列表中所有長度值的總和必須等於相應桶列表的長度。
一個包含所有欄位的示例
# TYPE acme_http_request_seconds histogram
acme_http_request_seconds{path="/api/v1",method="GET"} {count:59,sum:1.2e2,schema:7,zero_threshold:1e-4,zero_count:0,negative_spans:[1:2],negative_buckets:[5,7],positive_spans:[-1:2,3:4],positive_buckets:[5,7,10,9,8,8]} [email protected]
一個沒有使用任何桶的示例
# TYPE acme_http_request_seconds histogram
acme_http_request_seconds{path="/api/v1",method="GET"} {count:0,sum:0,schema:3,zero_threshold:1e-4,zero_count:0} [email protected]
同時包含經典桶和原生桶的直方圖
直方圖樣本的值必須是 CompositeValue。
CompositeValue 必須按此順序包含 Count 和 Sum 作為欄位 count、sum。
在 count 和 sum 之後,必須包含原生桶的其餘欄位,然後必須包含經典桶的其餘欄位(即 bucket 欄位)。
這種順序確保如果原生桶是首選,實現可以輕鬆跳過經典桶。
# TYPE acme_http_request_seconds histogram
# UNIT acme_http_request_seconds seconds
# HELP acme_http_request_seconds Latency histogram of all of ACME's HTTP requests.
acme_http_request_seconds{path="/api/v1",method="GET"} {count:2,sum:1.2e2,schema:0,zero_threshold:1e-4,zero_count:0,positive_spans:[1:2],positive_buckets:[1,1],bucket:[0.5:1,1:2,+Inf:2]}
範例和開始時間戳
範例可以附加到直方圖樣本。
如果暴露器為經典桶和原生桶保留了單獨的範例集,那麼出於效能和向後相容性原因,暴露器可以只附加一個集,並且該集應為與經典桶關聯的範例。
如果存在,樣本的開始時間戳必須使用 st@ 字首與樣本內聯。如果值的時間戳存在,則開始時間戳必須緊隨其後新增。如果存在範例,則開始時間戳必須在其之前新增。
一個帶有原生桶和開始時間戳,幷包含多個範例的直方圖示例
# TYPE foo histogram
foo {count:17,sum:324789.3,schema:0,zero_threshold:1e-4,zero_count:0,positive_spans:[0:2],positive_buckets:[5,12]} [email protected] # {trace_id="shaZ8oxi"} 0.67 1520879607.789 # {trace_id="ookahn0M"} 1.2 1520879608.589
一個帶有經典桶和開始時間戳的直方圖示例,其中沒有範例落入“0.01”桶和“+Inf”桶。一個沒有標籤的範例落入“0.1”桶。一個帶有一個標籤的範例落入“1”桶,另一個落入“10”桶。
# TYPE foo histogram
foo {count:17,sum:324789.3,bucket:[0.01:0,0.1:8,1.0:11,10.0:17,+Inf:17]} [email protected] # {} 0.054 1520879607.7 # {trace_id="KOO5S4vxi0o"} 1.67 1520879602.890 # {trace_id="oHg5SJYRHA0"} 9.8 1520879607.789
一個同時包含經典桶、原生桶和開始時間戳的直方圖示例。
# TYPE foo histogram
foo {count:17,sum:324789.3,schema:0,zero_threshold:1e-4,zero_count:0,positive_spans:[0:2],positive_buckets:[5,12],bucket:[0.01:0,0.1:8,1.0:11,10.0:17,+Inf:17]} [email protected] # {} 0.054 1520879607.7 # {trace_id="KOO5S4vxi0o"} 1.67 1520879602.890 # {trace_id="oHg5SJYRHA0"} 9.8 1520879607.789
帶有經典桶的 GaugeHistogram
帶有經典桶的 GaugeHistogram 樣本遵循與帶有經典桶的 Histogram 樣本相同的語法,不同之處在於 Count 和 Sum 以欄位 gcount 和 gsum 的形式暴露,並且 GaugeHistogram 沒有開始時間戳。
一個沒有標籤、一個沒有時間戳、也沒有範例的度量樣本示例
# TYPE foo gaugehistogram
foo {gcount:42,gsum:3289.3,bucket:[0.01:20,0.1:25,1:34,+Inf:42]}
帶有原生桶的 GaugeHistogram
帶有原生桶的 GaugeHistogram 樣本遵循與帶有原生桶的 Histogram 樣本相同的語法,不同之處在於 Count 和 Sum 以欄位 gcount 和 gsum 的形式暴露,並且 GaugeHistogram 沒有開始時間戳。
一個沒有標籤、一個沒有時間戳、也沒有範例的度量樣本示例
# TYPE acme_http_request_seconds gaugehistogram
acme_http_request_seconds{path="/api/v1",method="GET"} {gcount:59,gsum:1.2e2,schema:7,zero_threshold:1e-4,zero_count:0,negative_spans:[1:2],negative_buckets:[5,7],positive_spans:[-1:2,3:4],positive_buckets:[5,7,10,9,8,8]}
同時包含經典桶和原生桶的 GaugeHistogram
同時包含經典桶和原生桶的 GaugeHistogram 樣本遵循與同時包含經典桶和原生桶的 Histogram 樣本相同的語法,不同之處在於 Count 和 Sum 以欄位 gcount 和 gsum 的形式暴露,並且 GaugeHistogram 沒有開始時間戳。
Unknown
樣本的值必須是 Number 或 CompositeValue。
對於型別為 Unknown 的 MetricFamily,MetricFamily 名稱沒有推薦的字尾。
一個沒有標籤的度量和沒有時間戳的樣本示例
# TYPE foo unknown
foo 42.23
一個沒有 MetricFamily 元資料和沒有時間戳的樣本的度量示例
foo 42.23
設計考慮
範圍
OpenMetrics 旨在為線上系統提供遙測資料。它執行在不提供硬即時或軟即時保證的協議之上,因此它自身也無法做出任何即時保證。OpenMetrics 的延遲和抖動屬性與底層網路、作業系統、CPU 等一樣不精確。它足夠精確,可以用於聚合以作為決策依據,但不能反映單個事件。
應支援各種規模的系統,從每小時接收幾次請求的應用程式到監控 400Gb 網路埠的頻寬使用情況。應能對傳輸的遙測資料進行任意時間段的聚合和分析。
它旨在以固定的節奏傳輸資料傳輸時刻的狀態快照。
範圍之外
攝取方如何發現哪些暴露方存在,反之亦然,這超出了本標準的範圍,因此未在本標準中定義。
故障模式
本規範提倡事務性處理:任何編碼、解碼或驗證錯誤都必須拒絕整個 MetricSet 攝取。失敗的抓取優於不準確的抓取或破壞事務性的部分度量檢視(例如,抓取 StateSet MetricGroup 的一部分,或從單個警報表達式中聚合的兩個計數器中僅抓取一個)。
此規則有一個例外:特定於範例的故障不應導致整個暴露失敗。如果範例格式錯誤或無效,則應將其丟棄或忽略,從而允許有效的度量資料被攝取。
擴充套件與改進
OpenMetrics 的第二個版本基於成熟的實際標準 Prometheus 暴露格式,例如 Prometheus 文字格式 0.0.4、Prometheus Protobuf 格式和 OpenMetrics 1.0。
此版本對第一版進行了重大更改,以提高可靠性、效能,並與 Prometheus Protobuf 格式以及 OpenTelemetry 資料模型和命名約定相容。同時,該格式保留了以簡單方式暴露遙測資料並使其易於閱讀的能力。該格式與前一版本、Prometheus 查詢語言和資料模型足夠接近,以方便過渡。
它還確保有一個易於實現的基本標準。這可以在標準的未來版本中作為基礎。其目的是,標準的未來次要版本將始終要求在語法和語義上支援此 2.0 版本。
我們希望允許監控系統在不過度負擔的情況下從 OpenMetrics 暴露中獲取可用資訊。如果剝離所有元資料和結構,僅將 OpenMetrics 暴露視為無序的樣本集,它也應該能夠獨立使用。
本原則在整個標準中始終貫徹。例如,鼓勵將 MetricFamily 的單位在名稱中重複,以便不理解單位元資料的系統也能獲得該單位。然而,與以前的版本不同,為了促進與 OpenTelemetry 的相容性,不再強制要求為計數器重複單位名稱並新增 _total 字尾。
與以前版本相比的另一個變化是,現在要求度量名稱必須嚴格匹配其 MetricFamily 名稱。在 OpenMetrics 1.0 中,MetricFamily 名稱是度量名稱,不帶型別特定的字尾,例如 _total 或 _bucket。此更改透過使度量與其 MetricFamily 之間的關聯明確無歧義來提高解析器的可靠性。
透過此格式暴露的每一行都是自包含的,即從中獲取的資訊是完整的,可以以有意義的方式儲存。這是透過引入複合型別並將開始時間戳(以前是 Created 值)內聯來實現的。這些是與第一版相比的重大更改,是由於 Prometheus 中引入了原生直方圖以及以前版本中解析 _created 行的效能而變得必要。
OpenMetrics 1.0 的顯著變化總結
- 開始時間戳以內聯方式使用
st@表示法,取代了單獨的_created樣本。 - 度量名稱必須與其 MetricFamily 名稱完全匹配;隱式字尾剝離已移除。
- 計數器的
_total字尾和單位字尾從 MUST 變為 SHOULD。 - Histogram、GaugeHistogram 和 Summary 值合併為單個 CompositeValue 行;Count 和 Sum 現在是必需的。
- 引入了具有指數桶模式的原生直方圖。
- 允許使用引號的 UTF-8 度量和標籤名稱。
- 範例時間戳現在是強制性的;每個樣本允許多個範例。
- Protobuf 格式規範已移除。
單位和基本單位
為了在系統間保持一致性並避免混淆,單位主要基於國際單位制(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 Metric 新增標籤不會造成破壞性影響。這樣做是為了您可以在現有 Info MetricFamily 中新增額外資訊,而無需被迫建立一個帶有額外標籤值的新資訊度量。攝取系統應確保它們對此類新增具有彈性。
更改 MetricFamily 的 Help 不是破壞性變更。對於可能的值,在浮點數和整數之間切換不是破壞性變更。向狀態集新增新狀態不是破壞性變更。在不更改指標名稱的情況下新增單位元資料不是破壞性變更。
直方圖的桶不應該在不同的 exposition 之間發生變化,因為這很可能導致效能問題並破壞攝取方。同樣,來自任何一致的應用程式二進位制檔案和環境的所有 exposition 都應該對給定的直方圖 MetricFamily 使用相同的桶,以便所有攝取方都可以對它們進行聚合,而無需攝取方實現異構桶的直方圖合併邏輯。一個例外可能是偶爾手動更改桶,這被認為是破壞性變更,但當效能特徵因新軟體釋出而改變時,這可能是一個有效的權衡。
即使更改在技術上不是破壞性的,它們仍然會帶來成本。例如,頻繁的更改可能會給攝取方帶來效能問題。一個在不同 exposition 之間變化的 Help 字串可能會導致每個 Help 值都被儲存。頻繁地在整數和浮點數值之間切換可能會妨礙高效壓縮。
NaN
在 OpenMetrics 中,NaN 和其他任何數字一樣,通常是除以零的結果,例如,如果最近沒有觀測資料,摘要分位數就會出現這種情況。NaN 在 OpenMetrics 中沒有特殊含義,尤其不得用作缺失或其他壞資料的標記。
缺失資料
在某些有效的情況下,資料會停止存在。例如,一個檔案系統可以被解除安裝,因此其表示可用磁碟空間的 Gauge 指標就不再存在了。對於這種情況,沒有特殊的標記或訊號。後續的 exposition 只是不再包含這個指標。
資料暴露效能
指標只有在合理的時間範圍內能夠被收集時才有用。需要數分鐘才能暴露的指標被認為是沒有用的。
根據經驗,資料暴露不應超過一秒。
透過 OpenMetrics 序列化的舊系統指標可能需要更長時間。因此,不能做硬性的效能假設。
Exposition 應該是最新狀態的。例如,處理 exposition 請求的執行緒不應該依賴於快取的值,應儘可能繞過任何此類快取。
併發性
為了實現高可用性和即時訪問,一種常見的方法是使用多個攝取方。為了支援這一點,必須支援併發 exposition。所有併發系統的最佳實踐(BCP)都應該被遵循,常見的陷阱包括死鎖、競爭條件以及過於粗粒度的鎖定,這些都會妨礙 exposition 的併發進行。
指標命名與名稱空間
我們在度量和標籤名稱的命名中力求在可理解性、避免衝突和簡潔性之間取得平衡。名稱透過下劃線分隔,因此度量名稱最終採用“snake_case”格式。雖然我們強烈推薦本文件中建議的實踐,但其他度量系統在命名約定方面有不同的理念。OpenMetrics 允許暴露這些度量,但如果沒有此處推薦的約定和字尾,則度量系統服務鏈中衝突和不相容的風險會增加。希望使用替代約定的使用者需要特別注意並付出額外努力,以確保整個系統的一致性。
舉個例子,“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 標頭。雖然這可以為推送式攝取方傳輸目標元資料,並且本標準也不禁止這樣做,但它的缺點是,即使拉取式攝取方應該使用自己的目標元資料,能夠訪問暴露方自身知道的元資料通常仍然很有用。
首選的解決方案是將此目標元資料作為暴露的一部分提供,但其方式不會影響整個暴露。Info MetricFamily 就是為此設計的。暴露器可以包含一個名為“target_info”的 Info MetricFamily,其中包含一個沒有標籤且帶有元資料的單個度量。文字格式的一個示例如下:
# TYPE target_info info
# HELP target_info 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 暴露暴露方元資料。
上述討論是在單個暴露器的背景下進行的。來自通用監控系統的暴露可能包含來自許多單獨目標的度量,因此可能會暴露多個 target_info 度量。這些度量可能已經在攝取時將目標元資料作為標籤新增到其中。度量名稱不得根據目標元資料而變化。例如,如果所有度量都來自 staging 環境中的目標,但所有度量都以 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 之間何時消失。然而,對於不帶時間戳的指標,攝取方可以在指標不再出現的 exposition 中使用自己的時間戳。
所有這些都表明,一般來說,不應暴露樣本時間戳,因為應該由攝取器來為其攝取的樣本應用自己的時間戳。
跟蹤指標最後變更時間
假設你有一個計數器 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
相反,對於範例時間戳沒有最佳實踐限制。請記住,由於競態條件或裝置之間時間未完全同步,範例時間戳相對於攝取器的系統時鐘或來自同一暴露的其他度量可能會顯得稍晚。同樣,樣本的“st@”也可能出現在同一樣本的範例或樣本時間戳之後。
請記住,目前常用的監控系統支援從納秒到秒的各種時間解析度,因此當截斷為秒級解析度時,具有相同時間戳的兩個樣本可能會在攝取器中導致明顯的重複。在這種情況下,必須使用具有最早時間戳的樣本。
閾值
暴露系統的期望邊界可能是有意義的,但需要謹慎處理。對於普遍適用的值,為這些閾值發出 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的唯一字串可能表明該使用場景更像是事件日誌記錄,而不是指標時間序列。
安全性
實現者可以選擇(MAY)提供身份驗證、授權和計費;如果選擇這樣做,這應該(SHOULD)在OpenMetrics之外處理。
所有暴露器實現都應能夠使用 TLS 1.3 或更高版本保護其 HTTP 流量。如果暴露器實現不支援加密,操作員應在可行的情況下使用反向代理、防火牆和/或 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-text