Skip to content

Commit

Permalink
Add feature gate to convert int metrics to double (#844)
Browse files Browse the repository at this point in the history
* add feature gate to convert int metrics to double

* add tests for sum and summary
  • Loading branch information
dashpole committed May 6, 2024
1 parent bda7f02 commit bc259cd
Show file tree
Hide file tree
Showing 2 changed files with 194 additions and 22 deletions.
42 changes: 42 additions & 0 deletions exporter/collector/googlemanagedprometheus/extra_metrics.go
Expand Up @@ -18,17 +18,59 @@ import (
"time"

"github.com/prometheus/common/model"
"go.opentelemetry.io/collector/featuregate"
"go.opentelemetry.io/collector/pdata/pcommon"
"go.opentelemetry.io/collector/pdata/pmetric"
semconv "go.opentelemetry.io/collector/semconv/v1.18.0"
)

var intToDoubleFeatureGate = featuregate.GlobalRegistry().MustRegister(
"exporter.googlemanagedpromethues.intToDouble",
featuregate.StageAlpha,
featuregate.WithRegisterFromVersion("v0.100.0"),
featuregate.WithRegisterDescription("Convert all int metrics to double metrics to avoid incompatible value types."),
featuregate.WithRegisterReferenceURL("https://github.com/GoogleCloudPlatform/opentelemetry-operations-go/issues/798"))

const prometheusMetricMetadataTypeKey = "prometheus.type"

func (c Config) ExtraMetrics(m pmetric.Metrics) {
addUntypedMetrics(m)
c.addTargetInfoMetric(m)
c.addScopeInfoMetric(m)
convertIntToDouble(m)
}

// convertIntToDouble converts all counter and gauge int values to double.
func convertIntToDouble(m pmetric.Metrics) {
if !intToDoubleFeatureGate.IsEnabled() {
return
}
rms := m.ResourceMetrics()
for i := 0; i < rms.Len(); i++ {
rm := rms.At(i)
for j := 0; j < rm.ScopeMetrics().Len(); j++ {
sm := rm.ScopeMetrics().At(j)
for k := 0; k < sm.Metrics().Len(); k++ {
metric := sm.Metrics().At(k)

var points pmetric.NumberDataPointSlice
switch metric.Type() {
case pmetric.MetricTypeSum:
points = metric.Sum().DataPoints()
case pmetric.MetricTypeGauge:
points = metric.Gauge().DataPoints()
default:
continue
}
for x := 0; x < points.Len(); x++ {
point := points.At(x)
if point.ValueType() == pmetric.NumberDataPointValueTypeInt {
point.SetDoubleValue(float64(point.IntValue()))
}
}
}
}
}
}

// addUntypedMetrics looks for any Gauge data point with the special Ops Agent untyped metric
Expand Down
174 changes: 152 additions & 22 deletions exporter/collector/googlemanagedprometheus/extra_metrics_test.go
Expand Up @@ -19,6 +19,8 @@ import (
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/featuregate"
"go.opentelemetry.io/collector/pdata/pcommon"
"go.opentelemetry.io/collector/pdata/pmetric"
semconv "go.opentelemetry.io/collector/semconv/v1.18.0"
Expand All @@ -43,19 +45,28 @@ func appendMetric(metrics pmetric.Metrics, timestamp time.Time) pmetric.Metrics

// other metrics should not be copied to target_info
metric := sm.Metrics().AppendEmpty()
metric.SetName("baz-metric")
metric.SetName("gauge-metric")
metric.SetEmptyGauge().DataPoints().AppendEmpty().SetIntValue(2112)
metric.Gauge().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
metric = sm.Metrics().AppendEmpty()
metric.SetName("sum-metric")
metric.SetEmptySum().DataPoints().AppendEmpty().SetIntValue(2112)
metric.Sum().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
metric = sm.Metrics().AppendEmpty()
metric.SetName("summary-metric")
metric.SetEmptySummary().DataPoints().AppendEmpty().SetCount(2112)
metric.Summary().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
return metrics
}

func TestAddExtraMetrics(t *testing.T) {
timestamp := time.Now()
for _, tc := range []struct {
input pmetric.Metrics
expected pmetric.ResourceMetricsSlice
name string
config Config
input pmetric.Metrics
expected pmetric.ResourceMetricsSlice
name string
config Config
enableDoubleFeatureGate bool
}{
{
name: "add target info from resource metric",
Expand Down Expand Up @@ -114,9 +125,20 @@ func TestAddExtraMetrics(t *testing.T) {
// add otel_scope_* attributes to all metrics in this scope (including otel_scope_info)
for i := 0; i < sm.Metrics().Len(); i++ {
metric := sm.Metrics().At(i)
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
metric.Gauge().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
switch metric.Type() {
case pmetric.MetricTypeGauge:
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
metric.Gauge().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
case pmetric.MetricTypeSum:
metric.Sum().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Sum().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
metric.Sum().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
case pmetric.MetricTypeSummary:
metric.Summary().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Summary().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
metric.Summary().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
}
}
return metrics
}(),
Expand All @@ -143,9 +165,20 @@ func TestAddExtraMetrics(t *testing.T) {
// add otel_scope_* attributes to all metrics in this scope (including otel_scope_info)
for i := 0; i < sm.Metrics().Len(); i++ {
metric := sm.Metrics().At(i)
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
scopeInfoMetric.Gauge().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
switch metric.Type() {
case pmetric.MetricTypeGauge:
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
metric.Gauge().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
case pmetric.MetricTypeSum:
metric.Sum().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Sum().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
metric.Sum().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
case pmetric.MetricTypeSummary:
metric.Summary().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Summary().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
metric.Summary().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
}
}
return metrics
}(),
Expand Down Expand Up @@ -185,18 +218,36 @@ func TestAddExtraMetrics(t *testing.T) {
// add otel_scope_* attributes to all metrics in this scope (including otel_scope_info)
for i := 0; i < sm.Metrics().Len(); i++ {
metric := sm.Metrics().At(i)
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
scopeInfoMetric.Gauge().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
switch metric.Type() {
case pmetric.MetricTypeGauge:
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
scopeInfoMetric.Gauge().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
case pmetric.MetricTypeSum:
metric.Sum().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Sum().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
case pmetric.MetricTypeSummary:
metric.Summary().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Summary().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
}
}

// add otel_scope_* attributes to all metrics in the second scope
sm = metrics.At(1).ScopeMetrics().At(0)
for i := 0; i < sm.Metrics().Len(); i++ {
metric := sm.Metrics().At(i)
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
scopeInfoMetric.Gauge().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
switch metric.Type() {
case pmetric.MetricTypeGauge:
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
scopeInfoMetric.Gauge().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
case pmetric.MetricTypeSum:
metric.Sum().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Sum().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
case pmetric.MetricTypeSummary:
metric.Summary().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Summary().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
}
}
return metrics
}(),
Expand Down Expand Up @@ -231,9 +282,83 @@ func TestAddExtraMetrics(t *testing.T) {
// and target_info (which will have an empty scope)
for i := 0; i < sm.Metrics().Len(); i++ {
metric := sm.Metrics().At(i)
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
metric.Gauge().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
switch metric.Type() {
case pmetric.MetricTypeGauge:
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
metric.Gauge().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
case pmetric.MetricTypeSum:
metric.Sum().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Sum().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
metric.Sum().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
case pmetric.MetricTypeSummary:
metric.Summary().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Summary().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
metric.Summary().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
}
}

return metrics
}(),
},
{
name: "metric as double",
config: Config{ExtraMetricsConfig: ExtraMetricsConfig{
EnableScopeInfo: true,
EnableTargetInfo: true,
}},
input: testMetric(timestamp),
enableDoubleFeatureGate: true,
expected: func() pmetric.ResourceMetricsSlice {
metrics := testMetric(timestamp).ResourceMetrics()
scopeMetrics := metrics.At(0).ScopeMetrics()

// Insert a new, empty ScopeMetricsSlice for this resource that will hold target_info
sm := scopeMetrics.AppendEmpty()
metric := sm.Metrics().AppendEmpty()
metric.SetName("target_info")
// This changes the value to double because of the feature gate.
metric.SetEmptyGauge().DataPoints().AppendEmpty().SetDoubleValue(1)
metric.Gauge().DataPoints().At(0).Attributes().PutStr("foo-label", "bar")
metric.Gauge().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))

// Insert the scope_info metric into the existing ScopeMetricsSlice
sm = scopeMetrics.At(0)
scopeInfoMetric := sm.Metrics().AppendEmpty()
scopeInfoMetric.SetName("otel_scope_info")
// This changes the value to double because of the feature gate.
scopeInfoMetric.SetEmptyGauge().DataPoints().AppendEmpty().SetDoubleValue(1)

// add otel_scope_* attributes to all metrics in all scopes
// this includes otel_scope_info for the existing (input) ScopeMetrics,
// and target_info (which will have an empty scope)
for i := 0; i < sm.Metrics().Len(); i++ {
metric := sm.Metrics().At(i)
switch metric.Type() {
case pmetric.MetricTypeGauge:
dataPoint := metric.Gauge().DataPoints().At(0)
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
metric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
metric.Gauge().DataPoints().At(0).SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
// Change the original value to double
if dataPoint.IntValue() == 2112 {
dataPoint.SetDoubleValue(float64(2112.0))
}
case pmetric.MetricTypeSum:
dataPoint := metric.Sum().DataPoints().At(0)
dataPoint.Attributes().PutStr("otel_scope_name", "myscope")
dataPoint.Attributes().PutStr("otel_scope_version", "v0.0.1")
dataPoint.SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
// Change the original value to double
if dataPoint.IntValue() == 2112 {
dataPoint.SetDoubleValue(float64(2112.0))
}
case pmetric.MetricTypeSummary:
dataPoint := metric.Summary().DataPoints().At(0)
dataPoint.Attributes().PutStr("otel_scope_name", "myscope")
dataPoint.Attributes().PutStr("otel_scope_version", "v0.0.1")
dataPoint.SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
}
}

return metrics
Expand Down Expand Up @@ -339,7 +464,7 @@ func TestAddExtraMetrics(t *testing.T) {
Metadata().PutStr("prometheus.type", "unknown")

metric := metrics.At(0).ScopeMetrics().At(0).Metrics().AppendEmpty()
metric.SetName("baz-metric")
metric.SetName("gauge-metric")
metric.Metadata().PutStr("prometheus.type", "unknown")
metric.SetEmptySum().DataPoints().AppendEmpty().SetIntValue(2112)
metric.Sum().SetIsMonotonic(true)
Expand Down Expand Up @@ -375,7 +500,7 @@ func TestAddExtraMetrics(t *testing.T) {
Gauge().DataPoints().At(0).SetDoubleValue(123.5)

metric := metrics.At(0).ScopeMetrics().At(0).Metrics().AppendEmpty()
metric.SetName("baz-metric")
metric.SetName("gauge-metric")
metric.Metadata().PutStr("prometheus.type", "unknown")
metric.SetEmptySum().DataPoints().AppendEmpty().SetDoubleValue(123.5)
metric.Sum().SetIsMonotonic(true)
Expand Down Expand Up @@ -431,6 +556,11 @@ func TestAddExtraMetrics(t *testing.T) {
},
} {
t.Run(tc.name, func(t *testing.T) {
originalValue := intToDoubleFeatureGate.IsEnabled()
require.NoError(t, featuregate.GlobalRegistry().Set(intToDoubleFeatureGate.ID(), tc.enableDoubleFeatureGate))
defer func() {
require.NoError(t, featuregate.GlobalRegistry().Set(intToDoubleFeatureGate.ID(), originalValue))
}()
m := tc.input
tc.config.ExtraMetrics(m)
assert.EqualValues(t, tc.expected, m.ResourceMetrics())
Expand Down

0 comments on commit bc259cd

Please sign in to comment.