diff --git a/.chloggen/35003-deprecate-export_created_metric.yaml b/.chloggen/35003-deprecate-export_created_metric.yaml new file mode 100644 index 000000000000..732e78854ae0 --- /dev/null +++ b/.chloggen/35003-deprecate-export_created_metric.yaml @@ -0,0 +1,25 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: deprecation + +# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver) +component: exporter/prometheusremotewrite + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Deprecate configuration option `export_created metric` + +# One or more tracking issues or pull requests related to the change +issues: [35003] + +# (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: Disable the exporter.prometheusremotewriteexporter.deprecateCreatedMetric feature gate to temporarily re-enable the created metric. + +# 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: [user] diff --git a/exporter/prometheusremotewriteexporter/README.md b/exporter/prometheusremotewriteexporter/README.md index ce8be0ee280b..09a0f2feda7d 100644 --- a/exporter/prometheusremotewriteexporter/README.md +++ b/exporter/prometheusremotewriteexporter/README.md @@ -59,7 +59,7 @@ The following settings can be optionally configured: - `enabled` (default = false): If `enabled` is `true`, all the resource attributes will be converted to metric labels by default. - `target_info`: customize `target_info` metric - `enabled` (default = true): If `enabled` is `true`, a `target_info` metric will be generated for each resource metric (see https://github.com/open-telemetry/opentelemetry-specification/pull/2381). -- `export_created_metric`: +- `export_created_metric`: `WARNING` Deprecated and planned for removal in v0.116.0. See [related issue](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/35003) for more information. - `enabled` (default = false): If `enabled` is `true`, a `_created` metric is exported for Summary, Histogram, and Monotonic Sum metric points if `StartTimeUnixNano` is set. diff --git a/exporter/prometheusremotewriteexporter/config.go b/exporter/prometheusremotewriteexporter/config.go index 6a6f449a93dd..23b8c5f1e4e3 100644 --- a/exporter/prometheusremotewriteexporter/config.go +++ b/exporter/prometheusremotewriteexporter/config.go @@ -45,6 +45,8 @@ type Config struct { TargetInfo *TargetInfo `mapstructure:"target_info,omitempty"` // CreatedMetric allows customizing creation of _created metrics + // Deprecated[0.114.0]: The feature doesn't provide the expected behavior. Use Prometheus remote-write v2 to enable sending Created Timestamps. + // This feature is planned to be removed in v0.116.0 CreatedMetric *CreatedMetric `mapstructure:"export_created_metric,omitempty"` // AddMetricSuffixes controls whether unit and type suffixes are added to metrics on export diff --git a/exporter/prometheusremotewriteexporter/exporter.go b/exporter/prometheusremotewriteexporter/exporter.go index f8bbf7755be8..74e1cff42b79 100644 --- a/exporter/prometheusremotewriteexporter/exporter.go +++ b/exporter/prometheusremotewriteexporter/exporter.go @@ -143,6 +143,10 @@ func newPRWExporter(cfg *Config, set exporter.Settings) (*prwExporter, error) { batchTimeSeriesState: newBatchTimeSericesState(), } + if prwe.exporterSettings.ExportCreatedMetric { + prwe.settings.Logger.Warn("export_created_metric is deprecated and will be removed in a future release") + } + prwe.wal = newWAL(cfg.WAL, prwe.export) return prwe, nil } diff --git a/pkg/translator/prometheusremotewrite/go.mod b/pkg/translator/prometheusremotewrite/go.mod index f8ef0fa0e38f..a76d6762f9eb 100644 --- a/pkg/translator/prometheusremotewrite/go.mod +++ b/pkg/translator/prometheusremotewrite/go.mod @@ -5,11 +5,13 @@ go 1.22.0 require ( github.com/cespare/xxhash/v2 v2.3.0 github.com/google/go-cmp v0.6.0 - github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.113.0 - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus v0.113.0 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.113.0 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.112.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus v0.112.0 github.com/prometheus/common v0.60.1 github.com/prometheus/prometheus v0.54.1 github.com/stretchr/testify v1.9.0 + go.opentelemetry.io/collector/featuregate v1.19.0 go.opentelemetry.io/collector/pdata v1.19.0 go.opentelemetry.io/collector/semconv v0.113.0 go.uber.org/goleak v1.3.0 @@ -26,7 +28,6 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.1 // indirect - go.opentelemetry.io/collector/featuregate v1.19.0 // indirect golang.org/x/net v0.29.0 // indirect golang.org/x/sys v0.26.0 // indirect golang.org/x/text v0.19.0 // indirect diff --git a/pkg/translator/prometheusremotewrite/helper.go b/pkg/translator/prometheusremotewrite/helper.go index 704868ecda85..b8c870d62a7d 100644 --- a/pkg/translator/prometheusremotewrite/helper.go +++ b/pkg/translator/prometheusremotewrite/helper.go @@ -19,6 +19,7 @@ import ( "github.com/prometheus/prometheus/model/timestamp" "github.com/prometheus/prometheus/model/value" "github.com/prometheus/prometheus/prompb" + "go.opentelemetry.io/collector/featuregate" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" conventions "go.opentelemetry.io/collector/semconv/v1.25.0" @@ -41,6 +42,13 @@ const ( infoType = "info" ) +var exportCreatedMetricGate = featuregate.GlobalRegistry().MustRegister( + "exporter.prometheusremotewriteexporter.deprecateCreatedMetric", + featuregate.StageAlpha, + featuregate.WithRegisterDescription("Feature gate used to control the deprecation of created metrics."), + featuregate.WithRegisterReferenceURL("https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/35003"), +) + type bucketBoundsData struct { ts *prompb.TimeSeries bound float64 @@ -275,7 +283,7 @@ func (c *prometheusConverter) addHistogramDataPoints(dataPoints pmetric.Histogra c.addExemplars(pt, bucketBounds) startTimestamp := pt.StartTimestamp() - if settings.ExportCreatedMetric && startTimestamp != 0 { + if settings.ExportCreatedMetric && startTimestamp != 0 && !exportCreatedMetricGate.IsEnabled() { labels := createLabels(baseName+createdSuffix, baseLabels) c.addTimeSeriesIfNeeded(labels, startTimestamp, pt.Timestamp()) } @@ -431,7 +439,7 @@ func (c *prometheusConverter) addSummaryDataPoints(dataPoints pmetric.SummaryDat } startTimestamp := pt.StartTimestamp() - if settings.ExportCreatedMetric && startTimestamp != 0 { + if settings.ExportCreatedMetric && startTimestamp != 0 && !exportCreatedMetricGate.IsEnabled() { createdLabels := createLabels(baseName+createdSuffix, baseLabels) c.addTimeSeriesIfNeeded(createdLabels, startTimestamp, pt.Timestamp()) } diff --git a/pkg/translator/prometheusremotewrite/helper_test.go b/pkg/translator/prometheusremotewrite/helper_test.go index 8894c0f7a27d..12c0dcd978be 100644 --- a/pkg/translator/prometheusremotewrite/helper_test.go +++ b/pkg/translator/prometheusremotewrite/helper_test.go @@ -19,6 +19,7 @@ import ( "go.opentelemetry.io/collector/pdata/pmetric" conventions "go.opentelemetry.io/collector/semconv/v1.25.0" + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/testutil" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal/testdata" prometheustranslator "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus" ) @@ -677,12 +678,14 @@ func TestMostRecentTimestampInMetric(t *testing.T) { func TestPrometheusConverter_AddSummaryDataPoints(t *testing.T) { ts := pcommon.Timestamp(time.Now().UnixNano()) tests := []struct { - name string - metric func() pmetric.Metric - want func() map[uint64]*prompb.TimeSeries + name string + isGateEnabled bool + metric func() pmetric.Metric + want func() map[uint64]*prompb.TimeSeries }{ { - name: "summary with start time", + name: "summary with start time", + isGateEnabled: false, metric: func() pmetric.Metric { metric := pmetric.NewMetric() metric.SetName("test_summary") @@ -727,7 +730,44 @@ func TestPrometheusConverter_AddSummaryDataPoints(t *testing.T) { }, }, { - name: "summary without start time", + name: "summary without start time", + isGateEnabled: false, + metric: func() pmetric.Metric { + metric := pmetric.NewMetric() + metric.SetName("test_summary") + metric.SetEmptySummary() + + dp := metric.Summary().DataPoints().AppendEmpty() + dp.SetTimestamp(ts) + + return metric + }, + want: func() map[uint64]*prompb.TimeSeries { + labels := []prompb.Label{ + {Name: model.MetricNameLabel, Value: "test_summary" + countStr}, + } + sumLabels := []prompb.Label{ + {Name: model.MetricNameLabel, Value: "test_summary" + sumStr}, + } + return map[uint64]*prompb.TimeSeries{ + timeSeriesSignature(labels): { + Labels: labels, + Samples: []prompb.Sample{ + {Value: 0, Timestamp: convertTimeStamp(ts)}, + }, + }, + timeSeriesSignature(sumLabels): { + Labels: sumLabels, + Samples: []prompb.Sample{ + {Value: 0, Timestamp: convertTimeStamp(ts)}, + }, + }, + } + }, + }, + { + name: "summary with exportCreatedMetricGate enabled", + isGateEnabled: true, metric: func() pmetric.Metric { metric := pmetric.NewMetric() metric.SetName("test_summary") @@ -735,6 +775,7 @@ func TestPrometheusConverter_AddSummaryDataPoints(t *testing.T) { dp := metric.Summary().DataPoints().AppendEmpty() dp.SetTimestamp(ts) + dp.SetStartTimestamp(ts) return metric }, @@ -764,6 +805,10 @@ func TestPrometheusConverter_AddSummaryDataPoints(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + oldValue := exportCreatedMetricGate.IsEnabled() + testutil.SetFeatureGateForTest(t, exportCreatedMetricGate, tt.isGateEnabled) + defer testutil.SetFeatureGateForTest(t, exportCreatedMetricGate, oldValue) + metric := tt.metric() converter := newPrometheusConverter() @@ -785,12 +830,14 @@ func TestPrometheusConverter_AddSummaryDataPoints(t *testing.T) { func TestPrometheusConverter_AddHistogramDataPoints(t *testing.T) { ts := pcommon.Timestamp(time.Now().UnixNano()) tests := []struct { - name string - metric func() pmetric.Metric - want func() map[uint64]*prompb.TimeSeries + name string + isGateEnabled bool + metric func() pmetric.Metric + want func() map[uint64]*prompb.TimeSeries }{ { - name: "histogram with start time", + name: "histogram with start time", + isGateEnabled: false, metric: func() pmetric.Metric { metric := pmetric.NewMetric() metric.SetName("test_hist") @@ -836,7 +883,8 @@ func TestPrometheusConverter_AddHistogramDataPoints(t *testing.T) { }, }, { - name: "histogram without start time", + name: "histogram without start time", + isGateEnabled: false, metric: func() pmetric.Metric { metric := pmetric.NewMetric() metric.SetName("test_hist") @@ -871,9 +919,51 @@ func TestPrometheusConverter_AddHistogramDataPoints(t *testing.T) { } }, }, + { + name: "histogram with exportCreatedMetricGate enabled", + isGateEnabled: true, + metric: func() pmetric.Metric { + metric := pmetric.NewMetric() + metric.SetName("test_hist") + metric.SetEmptyHistogram().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative) + + pt := metric.Histogram().DataPoints().AppendEmpty() + pt.SetTimestamp(ts) + pt.SetStartTimestamp(ts) + + return metric + }, + want: func() map[uint64]*prompb.TimeSeries { + labels := []prompb.Label{ + {Name: model.MetricNameLabel, Value: "test_hist" + countStr}, + } + infLabels := []prompb.Label{ + {Name: model.MetricNameLabel, Value: "test_hist_bucket"}, + {Name: model.BucketLabel, Value: "+Inf"}, + } + return map[uint64]*prompb.TimeSeries{ + timeSeriesSignature(infLabels): { + Labels: infLabels, + Samples: []prompb.Sample{ + {Value: 0, Timestamp: convertTimeStamp(ts)}, + }, + }, + timeSeriesSignature(labels): { + Labels: labels, + Samples: []prompb.Sample{ + {Value: 0, Timestamp: convertTimeStamp(ts)}, + }, + }, + } + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + oldValue := exportCreatedMetricGate.IsEnabled() + testutil.SetFeatureGateForTest(t, exportCreatedMetricGate, tt.isGateEnabled) + defer testutil.SetFeatureGateForTest(t, exportCreatedMetricGate, oldValue) + metric := tt.metric() converter := newPrometheusConverter()