Metrics SDK
目標
本文檔包含兩個部分,在第一部分中列出了實現OpenTelemetry Metric SDK的默認需求。實現者需要根據這些規則來實現OpenTelemetry API。
在第二部分中,以OpenTelemetry-Go Metric SDK為例描述了SDK模型的架構細節,給出所需要實現的內容,以此作為對實現者的指導,而不需要強制跨語言精確地去復制這種模型體系結構。
期望
SDK實現者在實現OpenTelemetry API時應該遵守該語言的最佳實踐和運行時環境。實現者應該遵循 OpenTelemetry library guidelines給出的有關安全和性能的規定。
SDK 術語
Metrics SDK提供了一種Metrics API的實現,包含如下術語(本文后續將直接采用如下術語,不作翻譯):
- Meter:支持OpenTelemetry Metric API的接口,與Resources 和Instrumentation Library綁定在一起
- MeterProvider:通過給定的Instrumentation Library獲取Meter實例的接口
這些術語用於描述API和SDK的邊界,但它們都是API級別的結構。我們可以使用API級別的術語來描述SDK和API之間的邊界,但從API的角度看,SDK是不透明的,且沒有結構。
本文給出了默認OpenTelemetry SDK的主要組件的內部結構,通過術語來解釋每個組件在將metric數據從輸入(API級別的事件)導出到輸出(metric呈現格式)中所扮演的角色。我們使用Export Pipeline來描述SDK級別的功能。
export pipeline中有三個數據流會經過的主要組件,按順序為:
- Accumulator:接收API通過Instrument獲取的metric事件,並根據活動的Instrument和Label Set對計算出一個Accumulation
- Processor:從Accumulator接收Accumulations ,並轉換為ExportRecordSet
- Exporter:接收ExportRecordSet,並轉換為某種協議(如grpc),將其發送出去
Controller 組件在export pipeline中協調Accumulator、Processor和Exporter。
Metrics API規范定義了如下術語:
- Metric Instrument:開發者用於操作工具的API對象
- Synchronous Instrument:用戶通過應用程序上下文調用的metric Instrument
- Asynchronous Instrument:通過從SDK的回調調用的metric Instrument
- Metric Descriptor:描述一個metric Instrument
- Metric Event:單個記錄到或觀察到的(Instrument, Label Set, Measurement)
- Collection Interval: Accumulator.Collect()調用的周期
- Label: 描述metric Event屬性的key-value
- Label Set: 包含唯一的keys的key-values集
- Measurement: 來自synchronous instrument的證書或浮點數
- Observation: 來自asynchronous instrument的證書或浮點數
Resource SDK 定義了如下術語:
- Resource: 描述進程的一組具有唯一keys的key-value集
- Instrumentation Library:與一個工具包關聯的名稱和版本
下面為架構中重要的數據類型:
- Aggregator: 以一種有用的方式匯總一個或多個measurements
- Aggregator Snapshot: 在采集期間拷貝synchronous instrument aggregator的副本
- AggregatorSelector: 選擇分配給metric instrument 的Aggregator
- Accumulation: 包含Instrument, Label Set, Resource, 和Aggregator snapshot, 由Accumulator生成
- Aggregation: 由特定的aggregator聚合一個或多個事件產生的結果, 由Processor生成
- AggregationKind: 描述了Aggregation支持的API類型 (e.g., Sum)
- ExportKind: Delta, Cumulative, 或Pass-Through的一種
- ExportKindSelector: 為一個metric Instrument選擇ExportKind
- ExportRecord: 包含Instrument, Label Set, Resource, Timestamp(s), 和Aggregation
- ExportRecordSet: 一些列的export records.
術語SDK instrument 指Instrument的底層實現。
數據流圖表
從外部看, Metrics SDK 實現了Meter
和MeterProvider
接口,從內部看, Metrics SDK為每個metric數據封裝了一個export pipeline,包含四個重要組件。
Accumulator組件是將metric event並發地傳遞給Aggregator的地方,這是負責SDK性能的組件。Accumulator 負責bound 和unbound Instrument,更新和同步拷貝Aggregator 狀態,調用Observer Instrument以及喚醒采集周期。
Processor 組件是exporter pipeline中可定制的組件。Processor 負責通過一個獨立的AggregationSelector
接口為特定的Instrument選擇Aggregators ,用於減少維數,以及用於在DELTA和CUMULATIVE數據表示之間進行轉換。Processor 接口支持任意協議獨立的數據轉換,且可以將Processor連接在一起,形成更復雜的export pipelines。
Exporter 組件將處理的數據轉換為特定的協議,並將其轉發到某處。根據 library guidelines,exporter應該包含最小的功能,定制時最好通過Processors來表示。
Controller 組件協調在一個采集間隔內的采集動作,處理和導出采集間隔內的metric數據,確保對export pipeline的訪問是同步的。
要求
下面列出了針對OpenTelemetry SDK主要組件的要求。
上面展示了一個抽象的數據流圖,將本文檔使用的標准組件名與數據路徑做了映射。上圖並非要說明SDK的整體架構,僅命名了一個export pipeline的過程,並將組件放到了上下文中。
SDK要求這些組件使用標准名稱,這樣做有助於在OpenTelemetry中構建一致性。每個SDK都應該包含這些組件,以及下面列出的接口,盡管每個SDK的實際組織可能會因可用的庫和源語言的性能特征而有所不同。例如,一個SDK可能為每個instrument實現了一個Accumulator,或者可以為每個采集周期使用一個Accumulator(假設支持多采集周期)。
SDK
SDK封裝了OpenTelemetry Metric export pipeline,實現了Meter接口,並管理SDK instrument,Resource和Instrumentation Library 元數據。
MeterProvider
Shutdown
該方法為provider提供了一種清理環境的方法。
每個MeterProvider實例只能調用一次Shutdown
,在調用Shutdown
之后,將不允許獲得Meter
。對於這些調用,SDK應該返回一個有效的無操作Meter。
Shutdown
應該提供一種方式來讓調用者知道是否成功,失敗或超時。
當出現超時時,Shutdown
應該結束或中止。Shutdown
可以被實現為一個阻塞API,或異步API,通過回調或事件通知調用者。語言庫作者可以決定是否配置shutdown的超時時間。
SDK:Instrument注冊
OpenTelemetry SDK負責確保單個Meter的實現不會報告具有相同名稱但不同定義的多個Instrument。為了實現該要求,如果一個Meter已經注冊了一個相同名稱的metric,則SDK必須拒絕在該Meter重復注冊該Instrument。該要求甚至適用於嘗試注冊一個具有相同定義的Instrument。我們假設單個instrumentation library可以使用單個Instrument定義,而不是依賴SDK的重復注冊。
不同的Meter具有不同的instrumentation library名稱,允許在不同的instrumentation library中注冊相同名稱的Instrument,這種情況下,SDK必須將它們認為是不同的Instruments。
SDK負責實現API規范中包含的metric名稱的語法要求。
SDK: RecordBatch() 函數
TODO: Add functional requirements for RecordBatch().
SDK: Collect() 函數
SDK負責實現Collect()
函數,該函數會調用一個或多個Accumulators。本規范刻意避免在SDK和Accumulator之間建立特定關系;還有一個實現細節,即一個SDK是否會維護一個Accumulator,或每個Instrument對應一個Accumulator,或介於兩者之間的某些配置。
SDK Collect()
函數必須通過活動的synchronous instruments以及所有注冊的asynchronous instruments的Accumulations來調用Processor。
SDK必須允許在評估asynchronous instrument回調期間使用synchronous metric instruments。但通過asynchronous instrument回調來使用synchronous instruments是有副作用的,這種情況下,SDK應該在處理asynchronous instrument之后再處理synchronous instruments,這樣synchronous measurements會作為asynchronous observations采集間隔的一部分進行處理。
Accumulator
下圖展示了API和Accumulator之間的關系,以及synchronous instruments的細節。
對於一個synchronous instrument,Accumulator會:
- 將每個活動的Label Set 映射到一條記錄中,該記錄由兩個相同類型的Aggregator實例組成
- 在映射中輸入新記錄,如果需要,調用AggregationSelector
- 更新當前的Aggregator 實例,用以響應並發API事件
- 在當前Aggregator 實例上調用Aggregator.SynchronizedMove:a)拷貝其值到Aggregator快照實例中;b)將當前的Aggregator重置為0狀態
- 調用Processor.Process。處理每一個生成的Accumulation (即Instrument, Label Set, Resource, 和Aggregator snapshot)
Accumulator必須提供選項來將一個Resource與它生成的Accumulations進行關聯。
Synchronous metric instruments可以同步使用,除非源語言不支持該特性。SDK Accumulator 組件應該注意同步產生的性能壓力。
Accumulator可能會使用排他鎖定來維護synchronous instruments的更新。在調用Aggregator 時,Accumulator不應該持有排他鎖,因為Aggregators可能具有更高的同步期望。
Accumulator: Collect()函數
Accumulator必須實現Collect方法來為活動的instruments構建和處理當前的Accumulation值,即在上一次采集之后會更新這些值。Collect方法必須調用Processor來處理對應調用之前的所有metric events的Accumulations。
必須在Collect期間使用Aggregator上的同步移動操作來計算Accumulations。該操作會同步拷貝當前的Aggregator,並將其重置為0狀態,這樣Aggregator會在當前采集周期處理結束之后,下次采集周期開始時立即開始累加事件。一個Accumulation 定義為同步拷貝的Aggregator與Label Set, Resource, 和metric Descriptor的組合。
TODO: Are there more Accumulator functional requirements?
Processor
TODO Processor functional requirements
Controller
TODO Controller functional requirements
Aggregator
TODO Aggregator functional requirements
如果可能的話,Sum Aggregator應該使用原子操作。
模型實現
本模型實現基於OpenTelemetry-Go SDK,本節為實現者提供指南。
Accumulator: Meter實現
為了構造一個Accumulator,需要提供Processor和options
// NewAccumulator constructs a new Accumulator for the given
// Processor and options.
func NewAccumulator(processor export.Processor, opts ...Option) *Accumulator
// WithResource sets the Resource configuration option of a Config.
func WithResource(res *resource.Resource) Option
Controller會使用Collect()
方法來調用Accumulator,對采集進行協調。
// Collect traverses the list of active instruments and exports
// data. Collect() may not be called concurrently.
//
// During the collection pass, the Processor will receive
// one Process(Accumulation) call per current aggregation.
//
// Returns the number of accumulations that were exported.
func (m *Accumulator) Collect(ctx context.Context) int
實現與用戶級別的Metric API匹配的SDK級別的API
該接口位於SDK和API的邊界處,包含3個方法:
// MeterImpl is the interface an SDK must implement to supply a Meter
// implementation.
type MeterImpl interface {
// RecordBatch atomically records a batch of measurements.
RecordBatch(context.Context, []label.KeyValue, ...Measurement)
// NewSyncInstrument returns a newly constructed
// synchronous instrument implementation or an error, should
// one occur.
NewSyncInstrument(descriptor Descriptor) (SyncImpl, error)
// NewAsyncInstrument returns a newly constructed
// asynchronous instrument implementation or an error, should
// one occur.
NewAsyncInstrument(
descriptor Descriptor,
runner AsyncRunner,
) (AsyncImpl, error)
}
這些方法覆蓋了實現OpenTelemetry Metric API所需的所有入口。
RecordBatch
是一個直接由Accumulator實現的用戶級別的API。另外兩個instrument構造器用於創建同步和異步SDK instruments。
用戶通常對Metric API Meter
接口感興趣,該接口通過Metric API MeterProvider
接口獲得。可以通過封裝SDK Meter
實現來構建Meter
接口。
// WrapMeterImpl constructs a named `Meter` implementation from a
// `MeterImpl` implementation. The `instrumentationName` is the
// name of the instrumentation library.
func WrapMeterImpl(impl MeterImpl, instrumentationName string, opts ...MeterOption) Meter
該方法的選項:
- 可以為命名的Meter添加instrumentation library版本
instrument注冊:
// NewUniqueInstrumentMeterImpl returns a wrapped metric.MeterImpl with
// the addition of uniqueness checking.
func NewUniqueInstrumentMeterImpl(impl metric.MeterImpl) metric.MeterImpl {
訪問instrument descriptor
API Descriptor
方法根據API級別的構造定義了instrument ,包括名稱,instrument類型,描述和度量單位。在實現SDK時必須提供方式來訪問傳遞給constructor的Descriptor。
// InstrumentImpl is a common interface for synchronous and
// asynchronous instruments.
type InstrumentImpl interface {
// Descriptor returns a copy of the instrument's Descriptor.
Descriptor() Descriptor
}
Synchronous SDK instrument
Synchronous SDK instrument 支持直接和綁定調用規范。
// SyncImpl is the implementation-level interface to a generic
// synchronous instrument (e.g., ValueRecorder and Counter instruments).
type SyncImpl interface {
// InstrumentImpl provides Descriptor().
InstrumentImpl
// Bind creates an implementation-level bound instrument,
// binding a label set with this instrument implementation.
Bind(labels []label.KeyValue) BoundSyncImpl
// RecordOne captures a single synchronous metric event.
RecordOne(ctx context.Context, number Number, labels []label.KeyValue)
}
// BoundSyncImpl is the implementation-level interface to a
// generic bound synchronous instrument
type BoundSyncImpl interface {
// RecordOne captures a single synchronous metric event.
RecordOne(ctx context.Context, number Number)
// Unbind frees the resources associated with this bound instrument.
Unbind()
}
Asynchronous SDK instrument
Asynchronous SDK instrument 支持單觀察者和匹配觀測者調用規范。用於執行Observer回調的接口會傳遞給constructor,這樣asynchronous instruments就不需要其他API級別的訪問方法。
// AsyncImpl is an implementation-level interface to an
// asynchronous instrument (e.g., Observer instruments).
type AsyncImpl interface {
// InstrumentImpl provides Descriptor().
InstrumentImpl
}
傳遞給asynchronous SDK instrument constructor 的異步"runner"接口(AsyncRunner)同時支持單個和批量調用規范。
Export pipeline細節
TODO: define AggregatorSelector, Aggregator, Accumulation, ExportKind, ExportKindSelector, Aggregation, AggregationKind ExportRecord, ExportRecordSet
Processor細節
TODO: define the Processor interface
Basic Processor
TODO: define how ExportKind conversion works (delta->cumulative required, cumulative->delta optional), Memory option (to not forget prior collection state).
Reducing Processor
TODO: Label filter, LabelFilterSelector
Controller 細節
TODO: Push, Pull
Aggregator Implementations
TODO: Sum, LastValue, Histogram, MinMaxSumCount, Exact, and Sketch.
Pending issues
ValueRecorder instrument default aggregation
TODO: The default SDK behavior for ValueRecorder
instruments is still in question. Options are: LastValue, Histogram, MinMaxSumCount, and Sketch.
Spec issue 636 OTEP 117 OTEP 118
Standard sketch histogram aggregation
TODO: T.B.D.: DDSketch considered a good choice for ValueRecorder instrument default aggregation.