mirror of
https://github.com/hanzoai/metrics.git
synced 2026-10-30 20:03:39 +00:00
1727 lines
46 KiB
Go
1727 lines
46 KiB
Go
package pb
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/VictoriaMetrics/easyproto"
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompb"
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutil"
|
|
)
|
|
|
|
// MetricPusher must push the parsed samples and metric metadata to the underlying storage.
|
|
type MetricPusher interface {
|
|
// PushSample must store a sample with the given args.
|
|
//
|
|
// The PushSample must copy labels, since they become invalid after returning from the func.
|
|
PushSample(mm *MetricMetadata, suffix string, ls *promutil.Labels, timestampNsecs uint64, value float64, flags uint32)
|
|
|
|
// PushMetricMetadata must store mm.
|
|
//
|
|
// The PushMetricMetadata must copy mm contents, since it becomes invalid after returning from the func.
|
|
PushMetricMetadata(mm *MetricMetadata)
|
|
}
|
|
|
|
// MetricMetadata contains metric metadata
|
|
type MetricMetadata struct {
|
|
// Name is metric name
|
|
Name string
|
|
|
|
// Unit is metric unit
|
|
Unit string
|
|
|
|
// Description is metric description
|
|
Description string
|
|
|
|
// Type is metric type
|
|
Type prompb.MetricType
|
|
}
|
|
|
|
func (mm *MetricMetadata) reset() {
|
|
mm.Name = ""
|
|
mm.Unit = ""
|
|
mm.Type = 0
|
|
}
|
|
|
|
// MetricsData represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L56
|
|
type MetricsData struct {
|
|
ResourceMetrics []*ResourceMetrics
|
|
}
|
|
|
|
// MarshalProtobuf marshals r to protobuf message, appends it to dst and returns the result.
|
|
func (r *MetricsData) MarshalProtobuf(dst []byte) []byte {
|
|
m := mp.Get()
|
|
r.marshalProtobuf(m.MessageMarshaler())
|
|
dst = m.Marshal(dst)
|
|
mp.Put(m)
|
|
return dst
|
|
}
|
|
|
|
var mp easyproto.MarshalerPool
|
|
|
|
func (r *MetricsData) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
for _, rm := range r.ResourceMetrics {
|
|
rm.marshalProtobuf(mm.AppendMessage(1))
|
|
}
|
|
}
|
|
|
|
// DecodeMetricsData decodes metricsData from src and sends the decoded data to mp.
|
|
func DecodeMetricsData(src []byte, mp MetricPusher) (err error) {
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L56
|
|
//
|
|
// message MetricsData {
|
|
// repeated ResourceMetrics resource_metrics = 1;
|
|
// }
|
|
|
|
dctx := getDecoderContext(mp)
|
|
defer putDecoderContext(dctx)
|
|
|
|
var fc easyproto.FieldContext
|
|
for len(src) > 0 {
|
|
src, err = fc.NextField(src)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read the next field: %w", err)
|
|
}
|
|
switch fc.FieldNum {
|
|
case 1:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read ResourceMetrics data")
|
|
}
|
|
if err := dctx.decodeResourceMetrics(data); err != nil {
|
|
return fmt.Errorf("cannot unmarshal ResourceMetrics: %w", err)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ResourceMetrics represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L66
|
|
type ResourceMetrics struct {
|
|
Resource *Resource
|
|
ScopeMetrics []*ScopeMetrics
|
|
}
|
|
|
|
func (rm *ResourceMetrics) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
if rm.Resource != nil {
|
|
rm.Resource.marshalProtobuf(mm.AppendMessage(1))
|
|
}
|
|
for _, sm := range rm.ScopeMetrics {
|
|
sm.marshalProtobuf(mm.AppendMessage(2))
|
|
}
|
|
}
|
|
|
|
func (dctx *decoderContext) decodeResourceMetrics(src []byte) error {
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L66
|
|
//
|
|
// message ResourceMetrics {
|
|
// Resource resource = 1;
|
|
// repeated ScopeMetrics scope_metrics = 2;
|
|
// }
|
|
|
|
dctx.ls.Reset()
|
|
dctx.fb.reset()
|
|
|
|
resourceData, ok, err := easyproto.GetMessageData(src, 1)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read Resource data: %w", err)
|
|
}
|
|
if ok {
|
|
if err := dctx.decodeResource(resourceData); err != nil {
|
|
return fmt.Errorf("cannot decode Resource: %w", err)
|
|
}
|
|
}
|
|
|
|
dctxSnapshot := dctx.getSnapshot()
|
|
|
|
var fc easyproto.FieldContext
|
|
for len(src) > 0 {
|
|
src, err = fc.NextField(src)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read the next field: %w", err)
|
|
}
|
|
switch fc.FieldNum {
|
|
case 2:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read ScopeMetrics data")
|
|
}
|
|
|
|
if err := dctx.decodeScopeMetrics(data); err != nil {
|
|
return fmt.Errorf("cannot unmarshal ScopeMetrics: %w", err)
|
|
}
|
|
|
|
dctx.restoreFromSnapshot(dctxSnapshot)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Resource represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/resource/v1/resource.proto#L28
|
|
type Resource struct {
|
|
Attributes []*KeyValue
|
|
}
|
|
|
|
func (r *Resource) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
for _, a := range r.Attributes {
|
|
a.marshalProtobuf(mm.AppendMessage(1))
|
|
}
|
|
}
|
|
|
|
func (dctx *decoderContext) decodeResource(src []byte) (err error) {
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/resource/v1/resource.proto#L28
|
|
//
|
|
// message Resource {
|
|
// repeated KeyValue attributes = 1;
|
|
// }
|
|
|
|
var fc easyproto.FieldContext
|
|
for len(src) > 0 {
|
|
src, err = fc.NextField(src)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read the next field: %w", err)
|
|
}
|
|
switch fc.FieldNum {
|
|
case 1:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Attributes")
|
|
}
|
|
if err := decodeKeyValue(data, &dctx.ls, &dctx.fb, ""); err != nil {
|
|
return fmt.Errorf("cannot unmarshal Attributes: %w", err)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// KeyValue represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/common/v1/common.proto#L66
|
|
type KeyValue struct {
|
|
Key string
|
|
Value *AnyValue
|
|
}
|
|
|
|
func (kv *KeyValue) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
if kv.Key != "" {
|
|
mm.AppendString(1, kv.Key)
|
|
}
|
|
if kv.Value != nil {
|
|
kv.Value.marshalProtobuf(mm.AppendMessage(2))
|
|
}
|
|
}
|
|
|
|
func decodeKeyValue(src []byte, ls *promutil.Labels, fb *fmtBuffer, keyPrefix string) error {
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/common/v1/common.proto#L66
|
|
//
|
|
// message KeyValue {
|
|
// string key = 1;
|
|
// AnyValue value = 2;
|
|
// }
|
|
|
|
// Decode key
|
|
keySuffix, ok, err := easyproto.GetString(src, 1)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot find Key in KeyValue: %w", err)
|
|
}
|
|
if !ok {
|
|
// Key is missing, skip it.
|
|
// See https://github.com/VictoriaMetrics/VictoriaLogs/issues/869#issuecomment-3631307996
|
|
return nil
|
|
}
|
|
key := fb.formatSubFieldName(keyPrefix, keySuffix)
|
|
|
|
// Decode value
|
|
valueData, ok, err := easyproto.GetMessageData(src, 2)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot find Value in KeyValue: %w", err)
|
|
}
|
|
if !ok {
|
|
// Value is null, skip it.
|
|
return nil
|
|
}
|
|
|
|
if err := decodeAnyValue(valueData, ls, fb, key); err != nil {
|
|
return fmt.Errorf("cannot decode AnyValue: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// AnyValue represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/common/v1/common.proto#L28
|
|
type AnyValue struct {
|
|
StringValue *string
|
|
BoolValue *bool
|
|
IntValue *int64
|
|
DoubleValue *float64
|
|
ArrayValue *ArrayValue
|
|
KeyValueList *KeyValueList
|
|
BytesValue *[]byte
|
|
}
|
|
|
|
func (av *AnyValue) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
if av == nil {
|
|
return
|
|
}
|
|
switch {
|
|
case av.StringValue != nil:
|
|
mm.AppendString(1, *av.StringValue)
|
|
case av.BoolValue != nil:
|
|
mm.AppendBool(2, *av.BoolValue)
|
|
case av.IntValue != nil:
|
|
mm.AppendInt64(3, *av.IntValue)
|
|
case av.DoubleValue != nil:
|
|
mm.AppendDouble(4, *av.DoubleValue)
|
|
case av.ArrayValue != nil:
|
|
av.ArrayValue.marshalProtobuf(mm.AppendMessage(5))
|
|
case av.KeyValueList != nil:
|
|
av.KeyValueList.marshalProtobuf(mm.AppendMessage(6))
|
|
case av.BytesValue != nil:
|
|
mm.AppendBytes(7, *av.BytesValue)
|
|
}
|
|
}
|
|
|
|
func decodeAnyValue(src []byte, ls *promutil.Labels, fb *fmtBuffer, key string) (err error) {
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/common/v1/common.proto#L28
|
|
//
|
|
// message AnyValue {
|
|
// oneof value {
|
|
// string string_value = 1;
|
|
// bool bool_value = 2;
|
|
// int64 int_value = 3;
|
|
// double double_value = 4;
|
|
// ArrayValue array_value = 5;
|
|
// KeyValueList kvlist_value = 6;
|
|
// bytes bytes_value = 7;
|
|
// }
|
|
// }
|
|
|
|
var fc easyproto.FieldContext
|
|
for len(src) > 0 {
|
|
src, err = fc.NextField(src)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read the next field: %w", err)
|
|
}
|
|
switch fc.FieldNum {
|
|
case 1:
|
|
stringValue, ok := fc.String()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read StringValue")
|
|
}
|
|
ls.Add(key, stringValue)
|
|
case 2:
|
|
boolValue, ok := fc.Bool()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read BoolValue")
|
|
}
|
|
boolValueStr := strconv.FormatBool(boolValue)
|
|
ls.Add(key, boolValueStr)
|
|
case 3:
|
|
intValue, ok := fc.Int64()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read IntValue")
|
|
}
|
|
intValueStr := fb.formatInt(intValue)
|
|
ls.Add(key, intValueStr)
|
|
case 4:
|
|
doubleValue, ok := fc.Double()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read DoubleValue")
|
|
}
|
|
doubleValueStr := fb.formatFloat(doubleValue)
|
|
ls.Add(key, doubleValueStr)
|
|
case 5:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read ArrayValue")
|
|
}
|
|
a := jsonArenaPool.Get()
|
|
// Encode arrays as JSON to match the behavior of /insert/jsonline
|
|
arr, err := decodeArrayValueToJSON(data, a, fb)
|
|
if err != nil {
|
|
jsonArenaPool.Put(a)
|
|
return fmt.Errorf("cannot decode ArrayValue: %w", err)
|
|
}
|
|
|
|
encodedArr := fb.encodeJSONValue(arr)
|
|
jsonArenaPool.Put(a)
|
|
|
|
ls.Add(key, encodedArr)
|
|
case 6:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read KeyValueList")
|
|
}
|
|
if err := decodeKeyValueList(data, ls, fb, key); err != nil {
|
|
return fmt.Errorf("cannot decode KeyValueList: %w", err)
|
|
}
|
|
case 7:
|
|
bytesValue, ok := fc.Bytes()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read BytesValue")
|
|
}
|
|
v := fb.formatBase64(bytesValue)
|
|
ls.Add(key, v)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ArrayValue represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/common/v1/common.proto#L44
|
|
type ArrayValue struct {
|
|
Values []*AnyValue
|
|
}
|
|
|
|
func (av *ArrayValue) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
for _, v := range av.Values {
|
|
v.marshalProtobuf(mm.AppendMessage(1))
|
|
}
|
|
}
|
|
|
|
// KeyValueList represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/common/v1/common.proto#L54
|
|
type KeyValueList struct {
|
|
Values []*KeyValue
|
|
}
|
|
|
|
func (kvl *KeyValueList) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
for _, v := range kvl.Values {
|
|
v.marshalProtobuf(mm.AppendMessage(1))
|
|
}
|
|
}
|
|
|
|
func decodeKeyValueList(src []byte, ls *promutil.Labels, fb *fmtBuffer, keyPrefix string) (err error) {
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/common/v1/common.proto#L54
|
|
//
|
|
// message KeyValueList {
|
|
// repeated KeyValue values = 1;
|
|
// }
|
|
|
|
var fc easyproto.FieldContext
|
|
for len(src) > 0 {
|
|
src, err = fc.NextField(src)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read the next field: %w", err)
|
|
}
|
|
switch fc.FieldNum {
|
|
case 1:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read KeyValue data")
|
|
}
|
|
if err := decodeKeyValue(data, ls, fb, keyPrefix); err != nil {
|
|
return fmt.Errorf("cannot decode KeyValue: %w", err)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ScopeMetrics represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L86
|
|
type ScopeMetrics struct {
|
|
Scope *InstrumentationScope
|
|
Metrics []*Metric
|
|
}
|
|
|
|
func (sm *ScopeMetrics) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
if sm.Scope != nil {
|
|
sm.Scope.marshalProtobuf(mm.AppendMessage(1))
|
|
}
|
|
for _, m := range sm.Metrics {
|
|
m.marshalProtobuf(mm.AppendMessage(2))
|
|
}
|
|
}
|
|
|
|
func (dctx *decoderContext) decodeScopeMetrics(src []byte) error {
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L86
|
|
//
|
|
// message ScopeMetrics {
|
|
// InstrumentationScope scope = 1;
|
|
// repeated Metric metrics = 2;
|
|
// }
|
|
|
|
scopeData, ok, err := easyproto.GetMessageData(src, 1)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read InstrumentationScope: %w", err)
|
|
}
|
|
if ok {
|
|
if err := dctx.decodeInstrumentationScope(scopeData); err != nil {
|
|
return fmt.Errorf("cannot decode InstrumentationScope: %w", err)
|
|
}
|
|
}
|
|
|
|
dctxSnapshot := dctx.getSnapshot()
|
|
|
|
var fc easyproto.FieldContext
|
|
for len(src) > 0 {
|
|
src, err = fc.NextField(src)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read the next field: %w", err)
|
|
}
|
|
switch fc.FieldNum {
|
|
case 2:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Metric data")
|
|
}
|
|
|
|
if err := dctx.decodeMetric(data); err != nil {
|
|
return fmt.Errorf("cannot unmarshal Metric: %w", err)
|
|
}
|
|
|
|
dctx.restoreFromSnapshot(dctxSnapshot)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// InstrumentationScope represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/common/v1/common.proto#L76
|
|
type InstrumentationScope struct {
|
|
Name *string
|
|
Version *string
|
|
Attributes []*KeyValue
|
|
}
|
|
|
|
func (is *InstrumentationScope) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
if is.Name != nil {
|
|
mm.AppendString(1, *is.Name)
|
|
}
|
|
if is.Version != nil {
|
|
mm.AppendString(2, *is.Version)
|
|
}
|
|
for _, a := range is.Attributes {
|
|
a.marshalProtobuf(mm.AppendMessage(3))
|
|
}
|
|
}
|
|
|
|
func (dctx *decoderContext) decodeInstrumentationScope(src []byte) error {
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/a5f0eac5b802f7ae51dfe41e5116fe5548955e64/opentelemetry/proto/common/v1/common.proto#L76
|
|
//
|
|
// message InstrumentationScope {
|
|
// string name = 1;
|
|
// string version = 2;
|
|
// repeated KeyValue attributes = 3;
|
|
// }
|
|
|
|
nameStr, ok, err := easyproto.GetString(src, 1)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read name: %w", err)
|
|
}
|
|
name := "unknown"
|
|
if ok {
|
|
name = nameStr
|
|
}
|
|
dctx.ls.Add("scope.name", name)
|
|
|
|
versionStr, ok, err := easyproto.GetString(src, 2)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read version: %w", err)
|
|
}
|
|
version := "unknown"
|
|
if ok {
|
|
version = versionStr
|
|
}
|
|
dctx.ls.Add("scope.version", version)
|
|
|
|
var fc easyproto.FieldContext
|
|
for len(src) > 0 {
|
|
src, err = fc.NextField(src)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read the next field: %w", err)
|
|
}
|
|
switch fc.FieldNum {
|
|
case 3:
|
|
attributesData, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Attributes data")
|
|
}
|
|
if err := decodeKeyValue(attributesData, &dctx.ls, &dctx.fb, "scope.attributes"); err != nil {
|
|
return fmt.Errorf("cannot decode Attributes: %w", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Metric represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L188
|
|
type Metric struct {
|
|
Name string
|
|
Description string
|
|
Unit string
|
|
Gauge *Gauge
|
|
Sum *Sum
|
|
Histogram *Histogram
|
|
ExponentialHistogram *ExponentialHistogram
|
|
Summary *Summary
|
|
|
|
Metadata []*KeyValue
|
|
}
|
|
|
|
func (m *Metric) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
mm.AppendString(1, m.Name)
|
|
mm.AppendString(2, m.Description)
|
|
mm.AppendString(3, m.Unit)
|
|
switch {
|
|
case m.Gauge != nil:
|
|
m.Gauge.marshalProtobuf(mm.AppendMessage(5))
|
|
case m.Sum != nil:
|
|
m.Sum.marshalProtobuf(mm.AppendMessage(7))
|
|
case m.Histogram != nil:
|
|
m.Histogram.marshalProtobuf(mm.AppendMessage(9))
|
|
case m.ExponentialHistogram != nil:
|
|
m.ExponentialHistogram.marshalProtobuf(mm.AppendMessage(10))
|
|
case m.Summary != nil:
|
|
m.Summary.marshalProtobuf(mm.AppendMessage(11))
|
|
}
|
|
for _, md := range m.Metadata {
|
|
md.marshalProtobuf(mm.AppendMessage(12))
|
|
}
|
|
}
|
|
|
|
func (dctx *decoderContext) decodeMetric(src []byte) error {
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L188
|
|
//
|
|
// message Metric {
|
|
// string name = 1;
|
|
// string description = 2;
|
|
// string unit = 3;
|
|
// oneof data {
|
|
// Gauge gauge = 5;
|
|
// Sum sum = 7;
|
|
// Histogram histogram = 9;
|
|
// ExponentialHistogram exponential_histogram = 10;
|
|
// Summary summary = 11;
|
|
// }
|
|
// repeated opentelemetry.proto.common.v1.KeyValue metadata = 12;
|
|
// }
|
|
|
|
metricName, ok, err := easyproto.GetString(src, 1)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read metric name: %w", err)
|
|
}
|
|
if !ok {
|
|
return fmt.Errorf("missing metric name")
|
|
}
|
|
dctx.mm.Name = metricName
|
|
|
|
unit, ok, err := easyproto.GetString(src, 3)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read metric unit: %w", err)
|
|
}
|
|
if ok {
|
|
dctx.mm.Unit = unit
|
|
}
|
|
|
|
dctx.mm.Type = prompb.MetricTypeUnknown
|
|
|
|
lsMetadata := promutil.GetLabels()
|
|
defer promutil.PutLabels(lsMetadata)
|
|
|
|
var fc easyproto.FieldContext
|
|
for len(src) > 0 {
|
|
src, err = fc.NextField(src)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read the next field: %w", err)
|
|
}
|
|
var ok bool
|
|
switch fc.FieldNum {
|
|
case 2:
|
|
dctx.mm.Description, ok = fc.String()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read metric description")
|
|
}
|
|
case 5:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Gauge data")
|
|
}
|
|
if err := dctx.decodeGauge(data); err != nil {
|
|
return fmt.Errorf("cannot unmarshal Gauge: %w", err)
|
|
}
|
|
case 7:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Sum data")
|
|
}
|
|
if err := dctx.decodeSum(data); err != nil {
|
|
return fmt.Errorf("cannot unmarshal Sum: %w", err)
|
|
}
|
|
case 9:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Histogram data")
|
|
}
|
|
if err := dctx.decodeHistogram(data); err != nil {
|
|
return fmt.Errorf("cannot unmarshal Histogram: %w", err)
|
|
}
|
|
case 10:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read ExponentialHistogram data")
|
|
}
|
|
if err := dctx.decodeExponentialHistogram(data); err != nil {
|
|
return fmt.Errorf("cannot unmarshal ExponentialHistogram: %w", err)
|
|
}
|
|
case 11:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Summary data")
|
|
}
|
|
if err := dctx.decodeSummary(data); err != nil {
|
|
return fmt.Errorf("cannot unmarshal Summary: %w", err)
|
|
}
|
|
case 12:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read metadata attributes")
|
|
}
|
|
if err := decodeKeyValue(data, lsMetadata, &dctx.fb, ""); err != nil {
|
|
return fmt.Errorf("cannot unmarshal metadata attributes: %w", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
switch lsMetadata.Get("prometheus.type") {
|
|
case "unknown":
|
|
dctx.mm.Type = prompb.MetricTypeUnknown
|
|
case "info":
|
|
dctx.mm.Type = prompb.MetricTypeInfo
|
|
case "stateset":
|
|
dctx.mm.Type = prompb.MetricTypeStateset
|
|
}
|
|
|
|
dctx.mp.PushMetricMetadata(&dctx.mm)
|
|
|
|
return nil
|
|
}
|
|
|
|
// Gauge represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L232
|
|
type Gauge struct {
|
|
DataPoints []*NumberDataPoint
|
|
}
|
|
|
|
func (g *Gauge) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
for _, dp := range g.DataPoints {
|
|
dp.marshalProtobuf(mm.AppendMessage(1))
|
|
}
|
|
}
|
|
|
|
func (dctx *decoderContext) decodeGauge(src []byte) (err error) {
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L232
|
|
//
|
|
// message Gauge {
|
|
// repeated NumberDataPoint data_points = 1;
|
|
// }
|
|
|
|
dctx.mm.Type = prompb.MetricTypeGauge
|
|
|
|
dctxSnapshot := dctx.getSnapshot()
|
|
|
|
var fc easyproto.FieldContext
|
|
for len(src) > 0 {
|
|
src, err = fc.NextField(src)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read the next field: %w", err)
|
|
}
|
|
switch fc.FieldNum {
|
|
case 1:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read NumberDataPoint data")
|
|
}
|
|
if err := dctx.decodeNumberDataPoint(data); err != nil {
|
|
return fmt.Errorf("cannot unmarshal NumberDataPoint: %w", err)
|
|
}
|
|
|
|
dctx.restoreFromSnapshot(dctxSnapshot)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// NumberDataPoint represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L385
|
|
type NumberDataPoint struct {
|
|
Attributes []*KeyValue
|
|
TimeUnixNano uint64
|
|
DoubleValue *float64
|
|
IntValue *int64
|
|
Flags uint32
|
|
}
|
|
|
|
func (ndp *NumberDataPoint) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
for _, a := range ndp.Attributes {
|
|
a.marshalProtobuf(mm.AppendMessage(7))
|
|
}
|
|
mm.AppendFixed64(3, ndp.TimeUnixNano)
|
|
switch {
|
|
case ndp.DoubleValue != nil:
|
|
mm.AppendDouble(4, *ndp.DoubleValue)
|
|
case ndp.IntValue != nil:
|
|
mm.AppendSfixed64(6, *ndp.IntValue)
|
|
}
|
|
mm.AppendUint32(8, ndp.Flags)
|
|
}
|
|
|
|
func (dctx *decoderContext) decodeNumberDataPoint(src []byte) (err error) {
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L385
|
|
//
|
|
// message NumberDataPoint {
|
|
// repeated KeyValue attributes = 7;
|
|
// fixed64 time_unix_nano = 3;
|
|
// oneof value {
|
|
// double as_double = 4;
|
|
// sfixed64 as_int = 6;
|
|
// }
|
|
// uint32 flags = 8;
|
|
// }
|
|
|
|
var (
|
|
timestamp uint64
|
|
value float64
|
|
flags uint32
|
|
)
|
|
|
|
var fc easyproto.FieldContext
|
|
for len(src) > 0 {
|
|
src, err = fc.NextField(src)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read the next field: %w", err)
|
|
}
|
|
var ok bool
|
|
switch fc.FieldNum {
|
|
case 7:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Attributes")
|
|
}
|
|
if err := decodeKeyValue(data, &dctx.ls, &dctx.fb, ""); err != nil {
|
|
return fmt.Errorf("cannot unmarshal Attributes: %w", err)
|
|
}
|
|
case 3:
|
|
timestamp, ok = fc.Fixed64()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read TimeUnixNano")
|
|
}
|
|
case 4:
|
|
value, ok = fc.Double()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read DoubleValue")
|
|
}
|
|
case 6:
|
|
intValue, ok := fc.Sfixed64()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read IntValue")
|
|
}
|
|
value = float64(intValue)
|
|
case 8:
|
|
flags, ok = fc.Uint32()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Flags")
|
|
}
|
|
}
|
|
}
|
|
|
|
dctx.mp.PushSample(&dctx.mm, "", &dctx.ls, timestamp, value, flags)
|
|
|
|
return nil
|
|
}
|
|
|
|
// Sum represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L240
|
|
type Sum struct {
|
|
DataPoints []*NumberDataPoint
|
|
IsMonotonic bool
|
|
}
|
|
|
|
func (s *Sum) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
for _, dp := range s.DataPoints {
|
|
dp.marshalProtobuf(mm.AppendMessage(1))
|
|
}
|
|
mm.AppendBool(3, s.IsMonotonic)
|
|
}
|
|
|
|
func (dctx *decoderContext) decodeSum(src []byte) (err error) {
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L240
|
|
//
|
|
// message Sum {
|
|
// repeated NumberDataPoint data_points = 1;
|
|
// bool is_monotonic = 3;
|
|
// }
|
|
|
|
isMonotonic, _, err := easyproto.GetBool(src, 3)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot obtain isMonotonic: %w", err)
|
|
}
|
|
if isMonotonic {
|
|
dctx.mm.Type = prompb.MetricTypeCounter
|
|
} else {
|
|
dctx.mm.Type = prompb.MetricTypeGauge
|
|
}
|
|
|
|
dctxSnapshot := dctx.getSnapshot()
|
|
|
|
var fc easyproto.FieldContext
|
|
for len(src) > 0 {
|
|
src, err = fc.NextField(src)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read the next field: %w", err)
|
|
}
|
|
switch fc.FieldNum {
|
|
case 1:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read NumberDataPoint data")
|
|
}
|
|
if err := dctx.decodeNumberDataPoint(data); err != nil {
|
|
return fmt.Errorf("cannot unmarshal NumberDataPoint: %w", err)
|
|
}
|
|
|
|
dctx.restoreFromSnapshot(dctxSnapshot)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Histogram represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L255
|
|
type Histogram struct {
|
|
DataPoints []*HistogramDataPoint
|
|
}
|
|
|
|
func (h *Histogram) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
for _, dp := range h.DataPoints {
|
|
dp.marshalProtobuf(mm.AppendMessage(1))
|
|
}
|
|
}
|
|
|
|
func (dctx *decoderContext) decodeHistogram(src []byte) (err error) {
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L255
|
|
//
|
|
// message Histogram {
|
|
// repeated HistogramDataPoint data_points = 1;
|
|
// }
|
|
|
|
dctx.mm.Type = prompb.MetricTypeHistogram
|
|
|
|
dctxSnapshot := dctx.getSnapshot()
|
|
|
|
var fc easyproto.FieldContext
|
|
for len(src) > 0 {
|
|
src, err = fc.NextField(src)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read the next field: %w", err)
|
|
}
|
|
switch fc.FieldNum {
|
|
case 1:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read HistogramDataPoint")
|
|
}
|
|
if err := dctx.decodeHistogramDataPoint(data); err != nil {
|
|
return fmt.Errorf("cannot unmarshal HistogramDataPoint: %w", err)
|
|
}
|
|
|
|
dctx.restoreFromSnapshot(dctxSnapshot)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ExponentialHistogram represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L267
|
|
type ExponentialHistogram struct {
|
|
DataPoints []*ExponentialHistogramDataPoint
|
|
}
|
|
|
|
func (h *ExponentialHistogram) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
for _, dp := range h.DataPoints {
|
|
dp.marshalProtobuf(mm.AppendMessage(1))
|
|
}
|
|
}
|
|
|
|
func (dctx *decoderContext) decodeExponentialHistogram(src []byte) (err error) {
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L267
|
|
//
|
|
// message ExponentialHistogram {
|
|
// repeated ExponentialHistogramDataPoint data_points = 1;
|
|
// }
|
|
|
|
dctx.mm.Type = prompb.MetricTypeHistogram
|
|
|
|
dctxSnapshot := dctx.getSnapshot()
|
|
|
|
var fc easyproto.FieldContext
|
|
for len(src) > 0 {
|
|
src, err = fc.NextField(src)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read the next field: %w", err)
|
|
}
|
|
switch fc.FieldNum {
|
|
case 1:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read ExponentialHistogramDataPoint")
|
|
}
|
|
if err := dctx.decodeExponentialHistogramDataPoint(data); err != nil {
|
|
return fmt.Errorf("cannot unmarshal ExponentialHistogramDataPoint: %w", err)
|
|
}
|
|
|
|
dctx.restoreFromSnapshot(dctxSnapshot)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Summary represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L286
|
|
type Summary struct {
|
|
DataPoints []*SummaryDataPoint
|
|
}
|
|
|
|
func (s *Summary) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
for _, dp := range s.DataPoints {
|
|
dp.marshalProtobuf(mm.AppendMessage(1))
|
|
}
|
|
}
|
|
|
|
func (dctx *decoderContext) decodeSummary(src []byte) (err error) {
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L286
|
|
//
|
|
// message Summary {
|
|
// repeated SummaryDataPoint data_points = 1;
|
|
// }
|
|
|
|
dctx.mm.Type = prompb.MetricTypeSummary
|
|
|
|
dctxSnapshot := dctx.getSnapshot()
|
|
|
|
var fc easyproto.FieldContext
|
|
for len(src) > 0 {
|
|
src, err = fc.NextField(src)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read the next field: %w", err)
|
|
}
|
|
switch fc.FieldNum {
|
|
case 1:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read SummaryDataPoint")
|
|
}
|
|
if err := dctx.decodeSummaryDataPoint(data); err != nil {
|
|
return fmt.Errorf("cannot unmarshal SummaryDataPoint: %w", err)
|
|
}
|
|
|
|
dctx.restoreFromSnapshot(dctxSnapshot)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// HistogramDataPoint represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L434
|
|
type HistogramDataPoint struct {
|
|
Attributes []*KeyValue
|
|
TimeUnixNano uint64
|
|
Count uint64
|
|
Sum *float64
|
|
BucketCounts []uint64
|
|
ExplicitBounds []float64
|
|
Flags uint32
|
|
}
|
|
|
|
func (dp *HistogramDataPoint) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
for _, a := range dp.Attributes {
|
|
a.marshalProtobuf(mm.AppendMessage(9))
|
|
}
|
|
mm.AppendFixed64(3, dp.TimeUnixNano)
|
|
mm.AppendFixed64(4, dp.Count)
|
|
if dp.Sum != nil {
|
|
mm.AppendDouble(5, *dp.Sum)
|
|
}
|
|
mm.AppendFixed64s(6, dp.BucketCounts)
|
|
mm.AppendDoubles(7, dp.ExplicitBounds)
|
|
mm.AppendUint32(10, dp.Flags)
|
|
}
|
|
|
|
func (dctx *decoderContext) decodeHistogramDataPoint(src []byte) (err error) {
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L434
|
|
//
|
|
// message HistogramDataPoint {
|
|
// repeated KeyValue attributes = 9;
|
|
// fixed64 time_unix_nano = 3;
|
|
// fixed64 count = 4;
|
|
// optional double sum = 5;
|
|
// repeated fixed64 bucket_counts = 6;
|
|
// repeated double explicit_bounds = 7;
|
|
// uint32 flags = 10;
|
|
// }
|
|
|
|
hctx := getHistogramDataPointContext()
|
|
defer putHistogramDataPointContext(hctx)
|
|
|
|
var fc easyproto.FieldContext
|
|
for len(src) > 0 {
|
|
src, err = fc.NextField(src)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read the next field: %w", err)
|
|
}
|
|
|
|
var ok bool
|
|
switch fc.FieldNum {
|
|
case 9:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Attributes")
|
|
}
|
|
if err := decodeKeyValue(data, &dctx.ls, &dctx.fb, ""); err != nil {
|
|
return fmt.Errorf("cannot unmarshal Attributes: %w", err)
|
|
}
|
|
case 3:
|
|
hctx.timestamp, ok = fc.Fixed64()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read TimeUnixNano")
|
|
}
|
|
case 4:
|
|
hctx.count, ok = fc.Fixed64()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Count")
|
|
}
|
|
case 5:
|
|
hctx.sum, ok = fc.Double()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Sum")
|
|
}
|
|
hctx.hasSum = true
|
|
case 6:
|
|
hctx.bucketCounts, ok = fc.UnpackFixed64s(hctx.bucketCounts)
|
|
if !ok {
|
|
return fmt.Errorf("cannot read BucketCounts")
|
|
}
|
|
case 7:
|
|
hctx.explicitBounds, ok = fc.UnpackDoubles(hctx.explicitBounds)
|
|
if !ok {
|
|
return fmt.Errorf("cannot read ExplicitBounds")
|
|
}
|
|
case 10:
|
|
hctx.flags, ok = fc.Uint32()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Flags")
|
|
}
|
|
}
|
|
}
|
|
|
|
hctx.pushSamples(dctx)
|
|
|
|
return nil
|
|
}
|
|
|
|
type histogramDataPointContext struct {
|
|
timestamp uint64
|
|
count uint64
|
|
sum float64
|
|
hasSum bool
|
|
bucketCounts []uint64
|
|
explicitBounds []float64
|
|
flags uint32
|
|
}
|
|
|
|
func (hctx *histogramDataPointContext) reset() {
|
|
hctx.timestamp = 0
|
|
hctx.count = 0
|
|
hctx.sum = 0
|
|
hctx.hasSum = false
|
|
hctx.bucketCounts = hctx.bucketCounts[:0]
|
|
hctx.explicitBounds = hctx.explicitBounds[:0]
|
|
hctx.flags = 0
|
|
}
|
|
|
|
func (hctx *histogramDataPointContext) pushSamples(dctx *decoderContext) {
|
|
if len(hctx.bucketCounts) == 0 {
|
|
return
|
|
}
|
|
|
|
if len(hctx.bucketCounts) != len(hctx.explicitBounds)+1 {
|
|
skippedSampleLogger.Warnf("opentelemetry: unexpected number of buckets for %s; got %d; want %d", &dctx.ls, len(hctx.bucketCounts), len(hctx.explicitBounds)+1)
|
|
return
|
|
}
|
|
|
|
dctx.mp.PushSample(&dctx.mm, "_count", &dctx.ls, hctx.timestamp, float64(hctx.count), hctx.flags)
|
|
|
|
if hctx.hasSum {
|
|
dctx.mp.PushSample(&dctx.mm, "_sum", &dctx.ls, hctx.timestamp, float64(hctx.sum), hctx.flags)
|
|
}
|
|
|
|
dctx.ls.Add("le", "")
|
|
leValueP := &dctx.ls.Labels[len(dctx.ls.Labels)-1].Value
|
|
|
|
var cumulative uint64
|
|
for index, bound := range hctx.explicitBounds {
|
|
cumulative += hctx.bucketCounts[index]
|
|
*leValueP = dctx.fb.formatFloat(bound)
|
|
dctx.mp.PushSample(&dctx.mm, "_bucket", &dctx.ls, hctx.timestamp, float64(cumulative), hctx.flags)
|
|
}
|
|
cumulative += hctx.bucketCounts[len(hctx.bucketCounts)-1]
|
|
*leValueP = "+Inf"
|
|
dctx.mp.PushSample(&dctx.mm, "_bucket", &dctx.ls, hctx.timestamp, float64(cumulative), hctx.flags)
|
|
}
|
|
|
|
var skippedSampleLogger = logger.WithThrottler("otlp_skipped_sample", 5*time.Second)
|
|
|
|
func getHistogramDataPointContext() *histogramDataPointContext {
|
|
v := hctxPool.Get()
|
|
if v == nil {
|
|
return &histogramDataPointContext{}
|
|
}
|
|
return v.(*histogramDataPointContext)
|
|
}
|
|
|
|
func putHistogramDataPointContext(hctx *histogramDataPointContext) {
|
|
hctx.reset()
|
|
hctxPool.Put(hctx)
|
|
}
|
|
|
|
var hctxPool sync.Pool
|
|
|
|
// ExponentialHistogramDataPoint represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L521
|
|
type ExponentialHistogramDataPoint struct {
|
|
Attributes []*KeyValue
|
|
TimeUnixNano uint64
|
|
Count uint64
|
|
Sum *float64
|
|
Scale int32
|
|
ZeroCount uint64
|
|
Positive *Buckets
|
|
Flags uint32
|
|
Min *float64
|
|
Max *float64
|
|
ZeroThreshold float64
|
|
}
|
|
|
|
func (dp *ExponentialHistogramDataPoint) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
for _, a := range dp.Attributes {
|
|
a.marshalProtobuf(mm.AppendMessage(1))
|
|
}
|
|
mm.AppendFixed64(3, dp.TimeUnixNano)
|
|
mm.AppendFixed64(4, dp.Count)
|
|
if dp.Sum != nil {
|
|
mm.AppendDouble(5, *dp.Sum)
|
|
}
|
|
mm.AppendSint32(6, dp.Scale)
|
|
mm.AppendFixed64(7, dp.ZeroCount)
|
|
if dp.Positive != nil {
|
|
dp.Positive.marshalProtobuf(mm.AppendMessage(8))
|
|
}
|
|
mm.AppendUint32(10, dp.Flags)
|
|
if dp.Min != nil {
|
|
mm.AppendDouble(12, *dp.Min)
|
|
}
|
|
if dp.Max != nil {
|
|
mm.AppendDouble(13, *dp.Max)
|
|
}
|
|
mm.AppendDouble(14, dp.ZeroThreshold)
|
|
}
|
|
|
|
func (dctx *decoderContext) decodeExponentialHistogramDataPoint(src []byte) (err error) {
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L521
|
|
//
|
|
// message ExponentialHistogramDataPoint {
|
|
// repeated KeyValue attributes = 1;
|
|
// fixed64 time_unix_nano = 3;
|
|
// fixed64 count = 4;
|
|
// optional double sum = 5;
|
|
// sint32 scale = 6;
|
|
// fixed64 zero_count = 7;
|
|
// Buckets positive = 8;
|
|
// uint32 flags = 10;
|
|
// optional double min = 12;
|
|
// optional double max = 13;
|
|
// double zero_threshold = 14;
|
|
// }
|
|
|
|
ehctx := getExponentialHistogramDataPointContext()
|
|
defer putExponentialHistogramDataPointContext(ehctx)
|
|
|
|
var fc easyproto.FieldContext
|
|
for len(src) > 0 {
|
|
src, err = fc.NextField(src)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read the next field: %w", err)
|
|
}
|
|
var ok bool
|
|
switch fc.FieldNum {
|
|
case 1:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Attributes")
|
|
}
|
|
if err := decodeKeyValue(data, &dctx.ls, &dctx.fb, ""); err != nil {
|
|
return fmt.Errorf("cannot unmarshal Attributes: %w", err)
|
|
}
|
|
case 3:
|
|
ehctx.timestamp, ok = fc.Fixed64()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read TimeUnixNano")
|
|
}
|
|
case 4:
|
|
ehctx.count, ok = fc.Fixed64()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Count")
|
|
}
|
|
case 5:
|
|
ehctx.sum, ok = fc.Double()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Sum")
|
|
}
|
|
case 6:
|
|
ehctx.scale, ok = fc.Sint32()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Scale")
|
|
}
|
|
case 7:
|
|
ehctx.zeroCount, ok = fc.Fixed64()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read ZeroCount")
|
|
}
|
|
case 8:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Positive buckets")
|
|
}
|
|
if err := ehctx.positive.decodeBuckets(data); err != nil {
|
|
return fmt.Errorf("cannot unmarshal Positive: %w", err)
|
|
}
|
|
case 10:
|
|
ehctx.flags, ok = fc.Uint32()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Flags")
|
|
}
|
|
case 12:
|
|
ehctx.min, ok = fc.Double()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Min")
|
|
}
|
|
case 13:
|
|
ehctx.max, ok = fc.Double()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Max")
|
|
}
|
|
case 14:
|
|
ehctx.zeroThreshold, ok = fc.Double()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read ZeroThreshold")
|
|
}
|
|
}
|
|
}
|
|
|
|
ehctx.pushSamples(dctx)
|
|
|
|
return nil
|
|
}
|
|
|
|
type exponentialHistogramDataPointContext struct {
|
|
timestamp uint64
|
|
count uint64
|
|
sum float64
|
|
scale int32
|
|
zeroCount uint64
|
|
positive buckets
|
|
flags uint32
|
|
min float64
|
|
max float64
|
|
zeroThreshold float64
|
|
}
|
|
|
|
func (ehctx *exponentialHistogramDataPointContext) reset() {
|
|
ehctx.timestamp = 0
|
|
ehctx.count = 0
|
|
ehctx.sum = 0
|
|
ehctx.scale = 0
|
|
ehctx.zeroCount = 0
|
|
ehctx.positive.reset()
|
|
ehctx.flags = 0
|
|
ehctx.min = 0
|
|
ehctx.max = 0
|
|
ehctx.zeroThreshold = 0
|
|
}
|
|
|
|
type buckets struct {
|
|
offset int32
|
|
bucketCounts []uint64
|
|
}
|
|
|
|
func (b *buckets) reset() {
|
|
b.offset = 0
|
|
b.bucketCounts = b.bucketCounts[:0]
|
|
}
|
|
|
|
func (ehctx *exponentialHistogramDataPointContext) pushSamples(dctx *decoderContext) {
|
|
dctx.mp.PushSample(&dctx.mm, "_count", &dctx.ls, ehctx.timestamp, float64(ehctx.count), ehctx.flags)
|
|
dctx.mp.PushSample(&dctx.mm, "_sum", &dctx.ls, ehctx.timestamp, float64(ehctx.sum), ehctx.flags)
|
|
|
|
dctx.ls.Add("vmrange", "")
|
|
vmrangeValueP := &dctx.ls.Labels[len(dctx.ls.Labels)-1].Value
|
|
|
|
if ehctx.zeroCount > 0 {
|
|
*vmrangeValueP = dctx.fb.formatVmrange(0.0, ehctx.zeroThreshold)
|
|
dctx.mp.PushSample(&dctx.mm, "_bucket", &dctx.ls, ehctx.timestamp, float64(ehctx.zeroCount), ehctx.flags)
|
|
}
|
|
|
|
ratio := math.Pow(2, -float64(ehctx.scale))
|
|
base := math.Pow(2, ratio)
|
|
bound := math.Pow(2, float64(ehctx.positive.offset)*ratio)
|
|
for i, count := range ehctx.positive.bucketCounts {
|
|
if count <= 0 {
|
|
continue
|
|
}
|
|
|
|
lowerBound := bound * math.Pow(base, float64(i))
|
|
upperBound := lowerBound * base
|
|
*vmrangeValueP = dctx.fb.formatVmrange(lowerBound, upperBound)
|
|
dctx.mp.PushSample(&dctx.mm, "_bucket", &dctx.ls, ehctx.timestamp, float64(count), ehctx.flags)
|
|
}
|
|
}
|
|
|
|
func getExponentialHistogramDataPointContext() *exponentialHistogramDataPointContext {
|
|
v := ehctxPool.Get()
|
|
if v == nil {
|
|
return &exponentialHistogramDataPointContext{}
|
|
}
|
|
return v.(*exponentialHistogramDataPointContext)
|
|
}
|
|
|
|
func putExponentialHistogramDataPointContext(ehctx *exponentialHistogramDataPointContext) {
|
|
ehctx.reset()
|
|
ehctxPool.Put(ehctx)
|
|
}
|
|
|
|
var ehctxPool sync.Pool
|
|
|
|
// Buckets represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L592
|
|
type Buckets struct {
|
|
Offset int32
|
|
BucketCounts []uint64
|
|
}
|
|
|
|
func (b *Buckets) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
mm.AppendSint32(1, b.Offset)
|
|
for _, bc := range b.BucketCounts {
|
|
mm.AppendUint64(2, bc)
|
|
}
|
|
}
|
|
|
|
func (b *buckets) decodeBuckets(src []byte) (err error) {
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L592
|
|
//
|
|
// message Buckets {
|
|
// sint32 offset = 1;
|
|
// repeated uint64 bucket_counts = 2;
|
|
// }
|
|
|
|
var fc easyproto.FieldContext
|
|
for len(src) > 0 {
|
|
src, err = fc.NextField(src)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read the next field: %w", err)
|
|
}
|
|
var ok bool
|
|
switch fc.FieldNum {
|
|
case 1:
|
|
b.offset, ok = fc.Sint32()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Offset")
|
|
}
|
|
case 2:
|
|
b.bucketCounts, ok = fc.UnpackUint64s(b.bucketCounts)
|
|
if !ok {
|
|
return fmt.Errorf("cannot read BucketCounts")
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SummaryDataPoint represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L636
|
|
type SummaryDataPoint struct {
|
|
Attributes []*KeyValue
|
|
TimeUnixNano uint64
|
|
Count uint64
|
|
Sum float64
|
|
QuantileValues []*ValueAtQuantile
|
|
Flags uint32
|
|
}
|
|
|
|
func (dp *SummaryDataPoint) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
for _, a := range dp.Attributes {
|
|
a.marshalProtobuf(mm.AppendMessage(7))
|
|
}
|
|
mm.AppendFixed64(3, dp.TimeUnixNano)
|
|
mm.AppendFixed64(4, dp.Count)
|
|
mm.AppendDouble(5, dp.Sum)
|
|
for _, v := range dp.QuantileValues {
|
|
v.marshalProtobuf(mm.AppendMessage(6))
|
|
}
|
|
mm.AppendUint32(8, dp.Flags)
|
|
}
|
|
|
|
func (dctx *decoderContext) decodeSummaryDataPoint(src []byte) (err error) {
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L636
|
|
//
|
|
// message SummaryDataPoint {
|
|
// repeated KeyValue attributes = 7;
|
|
// fixed64 time_unix_nano = 3;
|
|
// fixed64 count = 4;
|
|
// double sum = 5;
|
|
// repeated ValueAtQuantile quantile_values = 6;
|
|
// uint32 flags = 8;
|
|
// }
|
|
|
|
sctx := getSummaryDataPointContext()
|
|
defer putSummaryDataPointContext(sctx)
|
|
|
|
var fc easyproto.FieldContext
|
|
for len(src) > 0 {
|
|
src, err = fc.NextField(src)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot read the next field: %w", err)
|
|
}
|
|
var ok bool
|
|
switch fc.FieldNum {
|
|
case 7:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Attributes")
|
|
}
|
|
if err := decodeKeyValue(data, &dctx.ls, &dctx.fb, ""); err != nil {
|
|
return fmt.Errorf("cannot unmarshal Attributes: %w", err)
|
|
}
|
|
case 3:
|
|
sctx.timestamp, ok = fc.Fixed64()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read TimeUnixNano")
|
|
}
|
|
case 4:
|
|
sctx.count, ok = fc.Fixed64()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Count")
|
|
}
|
|
case 5:
|
|
sctx.sum, ok = fc.Double()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Sum")
|
|
}
|
|
case 6:
|
|
data, ok := fc.MessageData()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read ValueAtQuantile")
|
|
}
|
|
quantile, value, err := decodeValueAtQuantile(data)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot unmarshal ValueAtQuantile: %w", err)
|
|
}
|
|
sctx.quantileValues = append(sctx.quantileValues, quantileValue{
|
|
quantile: quantile,
|
|
value: value,
|
|
})
|
|
case 8:
|
|
sctx.flags, ok = fc.Uint32()
|
|
if !ok {
|
|
return fmt.Errorf("cannot read Flags")
|
|
}
|
|
}
|
|
}
|
|
|
|
sctx.pushSamples(dctx)
|
|
|
|
return nil
|
|
}
|
|
|
|
type summaryDataPointContext struct {
|
|
timestamp uint64
|
|
count uint64
|
|
sum float64
|
|
quantileValues []quantileValue
|
|
flags uint32
|
|
}
|
|
|
|
type quantileValue struct {
|
|
quantile float64
|
|
value float64
|
|
}
|
|
|
|
func (sctx *summaryDataPointContext) reset() {
|
|
sctx.timestamp = 0
|
|
sctx.count = 0
|
|
sctx.sum = 0
|
|
sctx.quantileValues = sctx.quantileValues[:0]
|
|
sctx.flags = 0
|
|
}
|
|
|
|
func getSummaryDataPointContext() *summaryDataPointContext {
|
|
v := sctxPool.Get()
|
|
if v == nil {
|
|
return &summaryDataPointContext{}
|
|
}
|
|
return v.(*summaryDataPointContext)
|
|
}
|
|
|
|
func putSummaryDataPointContext(sctx *summaryDataPointContext) {
|
|
sctx.reset()
|
|
sctxPool.Put(sctx)
|
|
}
|
|
|
|
var sctxPool sync.Pool
|
|
|
|
func (sctx *summaryDataPointContext) pushSamples(dctx *decoderContext) {
|
|
dctx.mp.PushSample(&dctx.mm, "_count", &dctx.ls, sctx.timestamp, float64(sctx.count), sctx.flags)
|
|
dctx.mp.PushSample(&dctx.mm, "_sum", &dctx.ls, sctx.timestamp, sctx.sum, sctx.flags)
|
|
|
|
dctx.ls.Add("quantile", "")
|
|
quantileValueP := &dctx.ls.Labels[len(dctx.ls.Labels)-1].Value
|
|
|
|
for _, qv := range sctx.quantileValues {
|
|
*quantileValueP = dctx.fb.formatFloat(qv.quantile)
|
|
dctx.mp.PushSample(&dctx.mm, "", &dctx.ls, sctx.timestamp, qv.value, sctx.flags)
|
|
}
|
|
}
|
|
|
|
// ValueAtQuantile represents the corresponding OTEL protobuf message
|
|
//
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L680
|
|
type ValueAtQuantile struct {
|
|
Quantile float64
|
|
Value float64
|
|
}
|
|
|
|
func (v *ValueAtQuantile) marshalProtobuf(mm *easyproto.MessageMarshaler) {
|
|
mm.AppendDouble(1, v.Quantile)
|
|
mm.AppendDouble(2, v.Value)
|
|
}
|
|
|
|
func decodeValueAtQuantile(src []byte) (quantile float64, value float64, err error) {
|
|
// See https://github.com/open-telemetry/opentelemetry-proto/blob/049d4332834935792fd4dbd392ecd31904f99ba2/opentelemetry/proto/metrics/v1/metrics.proto#L680
|
|
//
|
|
// message ValueAtQuantile {
|
|
// double quantile = 1;
|
|
// double value = 2;
|
|
// }
|
|
|
|
var fc easyproto.FieldContext
|
|
for len(src) > 0 {
|
|
src, err = fc.NextField(src)
|
|
if err != nil {
|
|
return 0, 0, fmt.Errorf("cannot read the next field: %w", err)
|
|
}
|
|
var ok bool
|
|
switch fc.FieldNum {
|
|
case 1:
|
|
quantile, ok = fc.Double()
|
|
if !ok {
|
|
return 0, 0, fmt.Errorf("cannot read Quantile")
|
|
}
|
|
case 2:
|
|
value, ok = fc.Double()
|
|
if !ok {
|
|
return 0, 0, fmt.Errorf("cannot read Value")
|
|
}
|
|
}
|
|
}
|
|
return quantile, value, nil
|
|
}
|
|
|
|
type decoderContext struct {
|
|
ls promutil.Labels
|
|
fb fmtBuffer
|
|
|
|
mm MetricMetadata
|
|
|
|
mp MetricPusher
|
|
}
|
|
|
|
func (dctx *decoderContext) reset() {
|
|
// Explicitly clear all the ls.Labels up to its' capacity in order to remove possible references
|
|
// to the original byte slices, so they could be cleared by Go GC.
|
|
clear(dctx.ls.Labels[:cap(dctx.ls.Labels)])
|
|
dctx.ls.Labels = dctx.ls.Labels[:0]
|
|
|
|
dctx.ls.Reset()
|
|
dctx.fb.reset()
|
|
|
|
dctx.mm.reset()
|
|
|
|
dctx.mp = nil
|
|
}
|
|
|
|
func (dctx *decoderContext) getSnapshot() decoderContextSnapshot {
|
|
return decoderContextSnapshot{
|
|
labelsLen: len(dctx.ls.Labels),
|
|
fbLen: len(dctx.fb.buf),
|
|
}
|
|
}
|
|
|
|
func (dctx *decoderContext) restoreFromSnapshot(snapshot decoderContextSnapshot) {
|
|
dctx.ls.Labels = dctx.ls.Labels[:snapshot.labelsLen]
|
|
dctx.fb.buf = dctx.fb.buf[:snapshot.fbLen]
|
|
}
|
|
|
|
type decoderContextSnapshot struct {
|
|
labelsLen int
|
|
fbLen int
|
|
}
|
|
|
|
func getDecoderContext(mp MetricPusher) *decoderContext {
|
|
v := dctxPool.Get()
|
|
if v == nil {
|
|
v = &decoderContext{}
|
|
}
|
|
dctx := v.(*decoderContext)
|
|
dctx.mp = mp
|
|
|
|
return dctx
|
|
}
|
|
|
|
func putDecoderContext(dctx *decoderContext) {
|
|
dctx.reset()
|
|
dctxPool.Put(dctx)
|
|
}
|
|
|
|
var dctxPool sync.Pool
|