Skip to content

Commit

Permalink
Preserve unknown, info, and stateset metric types from prometheus rec…
Browse files Browse the repository at this point in the history
…eivers to exporters (open-telemetry#32605)
  • Loading branch information
dashpole committed May 2, 2024
1 parent 73cde8d commit fa35c2b
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 4 deletions.
27 changes: 27 additions & 0 deletions .chloggen/prometheus-additional-types.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: prometheusreceiver

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Prometheus receivers and exporters now preserve 'unknown', 'info', and 'stateset' types.

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [16768]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext: It uses the metric.metadata field with the 'prometheus.type' key to store the original type.

# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: []
7 changes: 6 additions & 1 deletion exporter/prometheusexporter/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,12 @@ func (c *collector) convertGauge(metric pmetric.Metric, resourceAttrs pcommon.Ma
case pmetric.NumberDataPointValueTypeDouble:
value = ip.DoubleValue()
}
m, err := prometheus.NewConstMetric(desc, prometheus.GaugeValue, value, attributes...)
metricType := prometheus.GaugeValue
originalType, ok := metric.Metadata().Get(prometheustranslator.MetricMetadataTypeKey)
if ok && originalType.Str() == string(model.MetricTypeUnknown) {
metricType = prometheus.UntypedValue
}
m, err := prometheus.NewConstMetric(desc, metricType, value, attributes...)
if err != nil {
return nil, err
}
Expand Down
20 changes: 20 additions & 0 deletions exporter/prometheusexporter/collector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import (
conventions "go.opentelemetry.io/collector/semconv/v1.6.1"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"

prometheustranslator "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus"
)

type mockAccumulator struct {
Expand Down Expand Up @@ -396,6 +398,24 @@ func TestCollectMetrics(t *testing.T) {
dp.Attributes().PutStr("label_2", "2")
dp.SetTimestamp(pcommon.NewTimestampFromTime(ts))

return
},
},
{
name: "Unknown",
metricType: prometheus.UntypedValue,
value: 42.42,
metric: func(ts time.Time) (metric pmetric.Metric) {
metric = pmetric.NewMetric()
metric.SetName("test_metric")
metric.SetDescription("test description")
metric.Metadata().PutStr(prometheustranslator.MetricMetadataTypeKey, "unknown")
dp := metric.SetEmptyGauge().DataPoints().AppendEmpty()
dp.SetDoubleValue(42.42)
dp.Attributes().PutStr("label_1", "1")
dp.Attributes().PutStr("label_2", "2")
dp.SetTimestamp(pcommon.NewTimestampFromTime(ts))

return
},
},
Expand Down
11 changes: 11 additions & 0 deletions pkg/translator/prometheus/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package prometheus // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus"

const (
// MetricMetadataTypeKey is the key used to store the original Prometheus
// type in metric metadata:
// https://github.com/open-telemetry/opentelemetry-specification/blob/e6eccba97ebaffbbfad6d4358408a2cead0ec2df/specification/compatibility/prometheus_and_openmetrics.md#metric-metadata
MetricMetadataTypeKey = "prometheus.type"
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,34 @@
package prometheusremotewrite // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite"

import (
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/prompb"
"go.opentelemetry.io/collector/pdata/pmetric"

prometheustranslator "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus"
)

func otelMetricTypeToPromMetricType(otelMetric pmetric.Metric) prompb.MetricMetadata_MetricType {
// metric metadata can be used to support Prometheus types that don't exist
// in OpenTelemetry.
typeFromMetadata, hasTypeFromMetadata := otelMetric.Metadata().Get(prometheustranslator.MetricMetadataTypeKey)
switch otelMetric.Type() {
case pmetric.MetricTypeGauge:
if hasTypeFromMetadata && typeFromMetadata.Str() == string(model.MetricTypeUnknown) {
return prompb.MetricMetadata_UNKNOWN
}
return prompb.MetricMetadata_GAUGE
case pmetric.MetricTypeSum:
metricType := prompb.MetricMetadata_GAUGE
if otelMetric.Sum().IsMonotonic() {
metricType = prompb.MetricMetadata_COUNTER
return prompb.MetricMetadata_COUNTER
}
if hasTypeFromMetadata && typeFromMetadata.Str() == string(model.MetricTypeInfo) {
return prompb.MetricMetadata_INFO
}
return metricType
if hasTypeFromMetadata && typeFromMetadata.Str() == string(model.MetricTypeStateset) {
return prompb.MetricMetadata_STATESET
}
return prompb.MetricMetadata_GAUGE
case pmetric.MetricTypeHistogram:
return prompb.MetricMetadata_HISTOGRAM
case pmetric.MetricTypeSummary:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,50 @@ func TestOtelMetricTypeToPromMetricType(t *testing.T) {
},
want: prompb.MetricMetadata_SUMMARY,
},
{
name: "unknown from metadata",
metric: func() pmetric.Metric {
metric := pmetric.NewMetric()
metric.SetName("test_sum")
metric.Metadata().PutStr(prometheustranslator.MetricMetadataTypeKey, "unknown")

dp := metric.SetEmptyGauge().DataPoints().AppendEmpty()
dp.SetDoubleValue(1)

return metric
},
want: prompb.MetricMetadata_UNKNOWN,
},
{
name: "info from metadata",
metric: func() pmetric.Metric {
metric := pmetric.NewMetric()
metric.SetName("test_sum")
metric.Metadata().PutStr(prometheustranslator.MetricMetadataTypeKey, "info")
metric.SetEmptySum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
metric.SetEmptySum().SetIsMonotonic(false)
dp := metric.Sum().DataPoints().AppendEmpty()
dp.SetDoubleValue(1)

return metric
},
want: prompb.MetricMetadata_INFO,
},
{
name: "stateset from metadata",
metric: func() pmetric.Metric {
metric := pmetric.NewMetric()
metric.SetName("test_sum")
metric.Metadata().PutStr(prometheustranslator.MetricMetadataTypeKey, "stateset")
metric.SetEmptySum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
metric.SetEmptySum().SetIsMonotonic(false)
dp := metric.Sum().DataPoints().AppendEmpty()
dp.SetDoubleValue(1)

return metric
},
want: prompb.MetricMetadata_STATESET,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions receiver/prometheusreceiver/internal/metricfamily.go
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,7 @@ func (mf *metricFamily) appendMetric(metrics pmetric.MetricSlice, trimSuffixes b
metric.SetName(name)
metric.SetDescription(mf.metadata.Help)
metric.SetUnit(prometheus.UnitWordToUCUM(mf.metadata.Unit))
metric.Metadata().PutStr(prometheus.MetricMetadataTypeKey, string(mf.metadata.Type))

var pointCount int

Expand Down
5 changes: 5 additions & 0 deletions receiver/prometheusreceiver/internal/metricsutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func histogramPointNoValue(attributes []*kv, startTimestamp, timestamp pcommon.T
func histogramMetric(name string, points ...pmetric.HistogramDataPoint) pmetric.Metric {
metric := pmetric.NewMetric()
metric.SetName(name)
metric.Metadata().PutStr("prometheus.type", "histogram")
histogram := metric.SetEmptyHistogram()
histogram.SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)

Expand All @@ -81,6 +82,7 @@ func histogramMetric(name string, points ...pmetric.HistogramDataPoint) pmetric.
func exponentialHistogramMetric(name string, points ...pmetric.ExponentialHistogramDataPoint) pmetric.Metric {
metric := pmetric.NewMetric()
metric.SetName(name)
metric.Metadata().PutStr("prometheus.type", "histogram")
histogram := metric.SetEmptyExponentialHistogram()
histogram.SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)

Expand Down Expand Up @@ -198,6 +200,7 @@ func doublePointNoValue(attributes []*kv, startTimestamp, timestamp pcommon.Time
func gaugeMetric(name string, points ...pmetric.NumberDataPoint) pmetric.Metric {
metric := pmetric.NewMetric()
metric.SetName(name)
metric.Metadata().PutStr("prometheus.type", "gauge")
destPointL := metric.SetEmptyGauge().DataPoints()
for _, point := range points {
destPoint := destPointL.AppendEmpty()
Expand All @@ -210,6 +213,7 @@ func gaugeMetric(name string, points ...pmetric.NumberDataPoint) pmetric.Metric
func sumMetric(name string, points ...pmetric.NumberDataPoint) pmetric.Metric {
metric := pmetric.NewMetric()
metric.SetName(name)
metric.Metadata().PutStr("prometheus.type", "counter")
sum := metric.SetEmptySum()
sum.SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
sum.SetIsMonotonic(true)
Expand Down Expand Up @@ -260,6 +264,7 @@ func summaryPointNoValue(attributes []*kv, startTimestamp, timestamp pcommon.Tim
func summaryMetric(name string, points ...pmetric.SummaryDataPoint) pmetric.Metric {
metric := pmetric.NewMetric()
metric.SetName(name)
metric.Metadata().PutStr("prometheus.type", "summary")
destPointL := metric.SetEmptySummary().DataPoints()
for _, point := range points {
destPoint := destPointL.AppendEmpty()
Expand Down
Loading

0 comments on commit fa35c2b

Please sign in to comment.