diff --git a/docs/feature_flags.md b/docs/feature_flags.md index 7a1f2e0727..8b4ac7ceaa 100644 --- a/docs/feature_flags.md +++ b/docs/feature_flags.md @@ -241,7 +241,7 @@ Examples of equivalent durations: When enabled, allows for the native ingestion of delta OTLP metrics, storing the raw sample values without conversion. This cannot be enabled in conjunction with `otlp-deltatocumulative`. It is recommended to enable `type-and-unit-labels`. -Currently, the StartTimeUnixNano field is ignored. Delta metrics are given a `__temporality__` label with a value of "delta", and a `__type__` label with a value of "gauge"/"gaugehistogram" if `type-and-unit-labels` is enabled. OTel sum metrics are additionally given a `__monotonicity__` label with a value of "true"/"false". +Currently, the StartTimeUnixNano field is ignored. Delta metrics are given a `__temporality__` label with a value of "delta", and a `__type__` label with a value of "gauge"/"gaugehistogram" if `type-and-unit-labels` is enabled. OTel sum metrics are additionally given a `__monotonicity__` label with a value of "true"/"false". OTel non-monotonic sum metrics are also given a `__otel_type__` label with a value of "sum". Delta support is in a very early stage of development and the ingestion and querying process my change over time. For the open proposal see [prometheus/proposals#48](https://github.com/prometheus/proposals/pull/48). diff --git a/schema/labels.go b/schema/labels.go index 1b56e77ebf..08b767b13b 100644 --- a/schema/labels.go +++ b/schema/labels.go @@ -21,7 +21,7 @@ import ( const ( // Special label names and selectors for schema.Metadata fields. - // They are currently private to ensure __name__, __type__, __unit__, __temporality__ and __monotonicity__ are used + // They are currently private to ensure __name__, __type__, __unit__, __temporality__, __monotonicity__, and __otel_type__ are used // together and remain extensible in Prometheus. See NewMetadataFromLabels and Metadata // methods for the interactions with the labels package structs. metricName = "__name__" @@ -29,12 +29,13 @@ const ( metricUnit = "__unit__" metricTemporality = "__temporality__" metricMonotonicity = "__monotonicity__" + metricOTelType = "__otel_type__" ) // IsMetadataLabel returns true if the given label name is a special // schema Metadata label. func IsMetadataLabel(name string) bool { - return name == metricName || name == metricType || name == metricUnit || name == metricTemporality || name == metricMonotonicity + return name == metricName || name == metricType || name == metricUnit || name == metricTemporality || name == metricMonotonicity || name == metricOTelType } // Metadata represents the core metric schema/metadata elements that: @@ -82,6 +83,8 @@ type Metadata struct { // Monotonicity represents whether the metric is monotonic. Empty string means monotonicity is not set. // Valid values are "true" and "false". Monotonicity string + // OTelType represents the original OTLP metric type for a metric ingested via the OTLP endpoint. + OTelType string } // NewMetadataFromLabels returns the schema metadata from the labels. @@ -96,6 +99,7 @@ func NewMetadataFromLabels(ls labels.Labels) Metadata { Unit: ls.Get(metricUnit), Temporality: ls.Get(metricTemporality), Monotonicity: ls.Get(metricMonotonicity), + OTelType: ls.Get(metricOTelType), } } @@ -119,6 +123,8 @@ func (m Metadata) IsEmptyFor(labelName string) bool { return m.Temporality == "" case metricMonotonicity: return m.Monotonicity == "" + case metricOTelType: + return m.OTelType == "" default: return true } @@ -142,6 +148,9 @@ func (m Metadata) AddToLabels(b *labels.ScratchBuilder) { if m.Monotonicity != "" { b.Add(metricMonotonicity, m.Monotonicity) } + if m.OTelType != "" { + b.Add(metricOTelType, m.OTelType) + } } // SetToLabels injects metric schema metadata as labels into the labels.Builder. @@ -159,6 +168,7 @@ func (m Metadata) SetToLabels(b *labels.Builder) { b.Set(metricUnit, m.Unit) b.Set(metricTemporality, m.Temporality) b.Set(metricMonotonicity, m.Monotonicity) + b.Set(metricOTelType, m.OTelType) } // IgnoreOverriddenMetadataLabelsScratchBuilder is a wrapper over labels scratch builder diff --git a/schema/labels_test.go b/schema/labels_test.go index 56d812d2cc..257f2b8e31 100644 --- a/schema/labels_test.go +++ b/schema/labels_test.go @@ -31,10 +31,11 @@ func TestMetadata(t *testing.T) { Unit: "seconds", Temporality: "delta", Monotonicity: "true", + OTelType: "sum", } for _, tcase := range []struct { - emptyName, emptyType, emptyUnit, emptyTemporality, emptyMonotonicity bool + emptyName, emptyType, emptyUnit, emptyTemporality, emptyMonotonicity, emptyOTelType bool }{ {}, {emptyName: true}, @@ -42,6 +43,7 @@ func TestMetadata(t *testing.T) { {emptyUnit: true}, {emptyTemporality: true}, {emptyMonotonicity: true}, + {emptyOTelType: true}, {emptyName: true, emptyType: true, emptyUnit: true, emptyTemporality: true}, } { var ( @@ -75,6 +77,10 @@ func TestMetadata(t *testing.T) { lb.Add(metricMonotonicity, testMeta.Monotonicity) expectedMeta.Monotonicity = testMeta.Monotonicity } + if !tcase.emptyOTelType { + lb.Add(metricOTelType, testMeta.OTelType) + expectedMeta.OTelType = testMeta.OTelType + } lb.Sort() expectedLabels = lb.Labels() } @@ -93,6 +99,7 @@ func TestMetadata(t *testing.T) { require.Equal(t, tcase.emptyUnit, expectedMeta.IsEmptyFor(metricUnit)) require.Equal(t, tcase.emptyTemporality, expectedMeta.IsEmptyFor(metricTemporality)) require.Equal(t, tcase.emptyMonotonicity, expectedMeta.IsEmptyFor(metricMonotonicity)) + require.Equal(t, tcase.emptyOTelType, expectedMeta.IsEmptyFor(metricOTelType)) } { // From Metadata to labels for various builders. @@ -129,8 +136,9 @@ func TestIgnoreOverriddenMetadataLabelsScratchBuilder(t *testing.T) { Unit: "seconds", Temporality: "delta", Monotonicity: "true", + OTelType: "sum", }, - expectedLabels: labels.FromStrings(metricName, "metric_total", metricType, string(model.MetricTypeCounter), metricUnit, "seconds", metricTemporality, "delta", metricMonotonicity, "true", "foo", "bar"), + expectedLabels: labels.FromStrings(metricName, "metric_total", metricType, string(model.MetricTypeCounter), metricUnit, "seconds", metricTemporality, "delta", metricMonotonicity, "true", metricOTelType, "sum", "foo", "bar"), }, { highPrioMeta: Metadata{ @@ -178,6 +186,7 @@ func TestIsMetadataLabel(t *testing.T) { {"__unit__", true}, {"__temporality__", true}, {"__monotonicity__", true}, + {"__otel_type__", true}, {"foo", false}, {"bar", false}, {"__other__", false}, diff --git a/storage/remote/otlptranslator/prometheusremotewrite/metrics_to_prw_test.go b/storage/remote/otlptranslator/prometheusremotewrite/metrics_to_prw_test.go index 21b551d4ae..da239df360 100644 --- a/storage/remote/otlptranslator/prometheusremotewrite/metrics_to_prw_test.go +++ b/storage/remote/otlptranslator/prometheusremotewrite/metrics_to_prw_test.go @@ -683,13 +683,19 @@ func createPromFloatSeriesWithTemporality(name, temporality string, ts time.Time } func createPromFloatSeriesWithTemporalityAndMonotonicity(name, temporality, monotonicity string, ts time.Time) prompb.TimeSeries { + labels := []prompb.Label{ + {Name: "__monotonicity__", Value: monotonicity}, + {Name: "__name__", Value: name}, + {Name: "__temporality__", Value: temporality}, + {Name: "test_label", Value: "test_value"}, + } + + if monotonicity == "false" { + labels = append(labels, prompb.Label{Name: "__otel_type__", Value: "sum"}) + } + return prompb.TimeSeries{ - Labels: []prompb.Label{ - {Name: "__monotonicity__", Value: monotonicity}, - {Name: "__name__", Value: name}, - {Name: "__temporality__", Value: temporality}, - {Name: "test_label", Value: "test_value"}, - }, + Labels: labels, Samples: []prompb.Sample{{ Value: 5, Timestamp: ts.UnixMilli(), diff --git a/storage/remote/otlptranslator/prometheusremotewrite/number_data_points.go b/storage/remote/otlptranslator/prometheusremotewrite/number_data_points.go index 753b6136f7..0ea73672c1 100644 --- a/storage/remote/otlptranslator/prometheusremotewrite/number_data_points.go +++ b/storage/remote/otlptranslator/prometheusremotewrite/number_data_points.go @@ -104,6 +104,7 @@ func (c *PrometheusConverter) addSumNumberDataPoints(ctx context.Context, dataPo lbls = append(lbls, prompb.Label{Name: "__monotonicity__", Value: "true"}) } else { lbls = append(lbls, prompb.Label{Name: "__monotonicity__", Value: "false"}) + lbls = append(lbls, prompb.Label{Name: "__otel_type__", Value: "sum"}) } }