diff --git a/.chloggen/owilliams_newconfig.yaml b/.chloggen/owilliams_newconfig.yaml new file mode 100644 index 0000000000000..9b66200561171 --- /dev/null +++ b/.chloggen/owilliams_newconfig.yaml @@ -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: prometheusexporter, prometheusremotewriteexporter + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Added new configurations options to restore old name translation behavior for those users whose names are broken by the new default behavior. + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [43077] + +# (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: + +# 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: [] diff --git a/exporter/prometheusexporter/collector.go b/exporter/prometheusexporter/collector.go index d7034a52b4a0d..b7e8d96d80556 100644 --- a/exporter/prometheusexporter/collector.go +++ b/exporter/prometheusexporter/collector.go @@ -92,7 +92,9 @@ func configureMetricNamer(config *Config) otlptranslator.MetricNamer { func configureLabelNamer(config *Config) otlptranslator.LabelNamer { _, utf8Allowed := getTranslationConfiguration(config) return otlptranslator.LabelNamer{ - UTF8Allowed: utf8Allowed, + UTF8Allowed: utf8Allowed, + UnderscoreLabelSanitization: config.UnderscoreLabelSanitization, + PreserveMultipleUnderscores: config.PreserveMultipleUnderscores, } } diff --git a/exporter/prometheusexporter/config.go b/exporter/prometheusexporter/config.go index 7019e2f737195..31da79015287e 100644 --- a/exporter/prometheusexporter/config.go +++ b/exporter/prometheusexporter/config.go @@ -44,6 +44,20 @@ type Config struct { // TranslationStrategy controls how OTLP metric and attribute names are translated into Prometheus metric and label names. // When set, this takes precedence over AddMetricSuffixes. TranslationStrategy translationStrategy `mapstructure:"translation_strategy"` + + // UnderscoreLabelSanitization, if true, enables prepending 'key' to labels + // starting with '_'. Reserved labels starting with `__` are not modified. + // Included for compatibility with default previous behavior. + // + // Deprecated: This will be removed in a future version. + UnderscoreLabelSanitization bool `mapstructure:"underscore_label_sanitization"` + + // PreserveMultipleUnderscores enables preserving of multiple + // consecutive underscores in label names when UTF8Allowed is false. + // This option is discouraged as it violates the OpenTelemetry to Prometheus + // specification https://github.com/open-telemetry/opentelemetry-specification/blob/v1.38.0/specification/compatibility/prometheus_and_openmetrics.md#otlp-metric-points-to-prometheus), + // but may be needed for compatibility with legacy systems that rely on the old behavior. + PreserveMultipleUnderscores bool `mapstructure:"preserve_multiple_underscores"` } var _ component.Config = (*Config)(nil) diff --git a/exporter/prometheusremotewriteexporter/config.go b/exporter/prometheusremotewriteexporter/config.go index 93e2fc9e92246..a6e69e207f8da 100644 --- a/exporter/prometheusremotewriteexporter/config.go +++ b/exporter/prometheusremotewriteexporter/config.go @@ -60,6 +60,20 @@ type Config struct { // RemoteWriteProtoMsg controls whether prometheus remote write v1 or v2 is sent. RemoteWriteProtoMsg config.RemoteWriteProtoMsg `mapstructure:"protobuf_message,omitempty"` + + // UnderscoreLabelSanitization, if true, enables prepending 'key' to labels + // starting with '_'. Reserved labels starting with `__` are not modified. + // Included for compatibility with default previous behavior. + // + // Deprecated: This will be removed in a future version. + UnderscoreLabelSanitization bool `mapstructure:"underscore_label_sanitization"` + + // PreserveMultipleUnderscores enables preserving of multiple + // consecutive underscores in label names when UTF8Allowed is false. + // This option is discouraged as it violates the OpenTelemetry to Prometheus + // specification https://github.com/open-telemetry/opentelemetry-specification/blob/v1.38.0/specification/compatibility/prometheus_and_openmetrics.md#otlp-metric-points-to-prometheus), + // but may be needed for compatibility with legacy systems that rely on the old behavior. + PreserveMultipleUnderscores bool `mapstructure:"preserve_multiple_underscores"` } type TargetInfo struct { diff --git a/exporter/prometheusremotewriteexporter/exporter.go b/exporter/prometheusremotewriteexporter/exporter.go index c1f8fc6eee3f9..814e5e2a35675 100644 --- a/exporter/prometheusremotewriteexporter/exporter.go +++ b/exporter/prometheusremotewriteexporter/exporter.go @@ -305,7 +305,10 @@ func (prwe *prwExporter) PushMetrics(ctx context.Context, md pmetric.Metrics) er } func validateAndSanitizeExternalLabels(cfg *Config) (map[string]string, error) { - namer := otlptranslator.LabelNamer{} + namer := otlptranslator.LabelNamer{ + UnderscoreLabelSanitization: cfg.UnderscoreLabelSanitization, + PreserveMultipleUnderscores: cfg.PreserveMultipleUnderscores, + } sanitizedLabels := make(map[string]string) for key, value := range cfg.ExternalLabels { if key == "" || value == "" { diff --git a/pkg/translator/prometheusremotewrite/helper.go b/pkg/translator/prometheusremotewrite/helper.go index e489eda50698b..2e0faf7f37fc8 100644 --- a/pkg/translator/prometheusremotewrite/helper.go +++ b/pkg/translator/prometheusremotewrite/helper.go @@ -529,7 +529,7 @@ func addResourceTargetInfo(resource pcommon.Resource, settings Settings, timesta name = settings.Namespace + "_" + name } - labels, err := createAttributes(resource, attributes, settings.ExternalLabels, identifyingAttrs, false, otlptranslator.LabelNamer{}, model.MetricNameLabel, name) + labels, err := createAttributes(resource, attributes, settings.ExternalLabels, identifyingAttrs, false, otlptranslator.LabelNamer{UnderscoreLabelSanitization: settings.UnderscoreLabelSanitization, PreserveMultipleUnderscores: settings.PreserveMultipleUnderscores}, model.MetricNameLabel, name) if err != nil { return err } diff --git a/pkg/translator/prometheusremotewrite/helper_test.go b/pkg/translator/prometheusremotewrite/helper_test.go index 61b2b6d3ff81a..1966424d09dae 100644 --- a/pkg/translator/prometheusremotewrite/helper_test.go +++ b/pkg/translator/prometheusremotewrite/helper_test.go @@ -244,13 +244,15 @@ func Test_timeSeriesSignature(t *testing.T) { // collision happens. It does not check whether labels are not sorted func Test_createLabelSet(t *testing.T) { tests := []struct { - name string - resource pcommon.Resource - orig pcommon.Map - externalLabels map[string]string - extras []string - want []prompb.Label - expectErr bool + name string + resource pcommon.Resource + orig pcommon.Map + externalLabels map[string]string + extras []string + want []prompb.Label + expectErr bool + underscoreLabelSanitization bool + preserveMultipleUnderscores bool }{ { name: "labels_clean", @@ -302,6 +304,32 @@ func Test_createLabelSet(t *testing.T) { extras: []string{label31 + dirty1, value31, label32, value32}, want: getPromLabels(label11+"_", value11, "_"+label12, value12, label31+"_", value31, label32, value32), }, + { + name: "labels_dirty_with_sanitization", + resource: pcommon.NewResource(), + orig: lbs1Dirty, + externalLabels: map[string]string{}, + extras: []string{label31 + dirty1, value31, label32, value32}, + want: getPromLabels(label11+"_", value11, "key_"+label12, value12, label31+"_", value31, label32, value32), + underscoreLabelSanitization: true, + }, + { + name: "labels_dont_preserve_underscores", + resource: pcommon.NewResource(), + orig: lbs1, + externalLabels: map[string]string{}, + extras: []string{label61, value61}, + want: getPromLabels(label11, value11, label12, value12, "test_label61", value61), + }, + { + name: "labels_preserve_underscores", + resource: pcommon.NewResource(), + orig: lbs1, + externalLabels: map[string]string{}, + extras: []string{label61, value61}, + want: getPromLabels(label11, value11, label12, value12, "test____label61", value61), + preserveMultipleUnderscores: true, + }, { name: "no_original_case", resource: pcommon.NewResource(), @@ -367,11 +395,23 @@ func Test_createLabelSet(t *testing.T) { extras: []string{label31, value31, label32, value32}, want: getPromLabels(label11, value11, label12, value12, label51, value51, label41, value41, label31, value31, label32, value32), }, + { + name: "sanitize_labels_starts_with_underscore_with_sanitization", + resource: pcommon.NewResource(), + orig: lbs3, + externalLabels: exlbs1, + extras: []string{label31, value31, label32, value32}, + want: getPromLabels(label11, value11, label12, value12, "key"+label51, value51, label41, value41, label31, value31, label32, value32), + underscoreLabelSanitization: true, + }, } - // run tests for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := createAttributes(tt.resource, tt.orig, tt.externalLabels, nil, true, otlptranslator.LabelNamer{}, tt.extras...) + labelNamer := otlptranslator.LabelNamer{ + UnderscoreLabelSanitization: tt.underscoreLabelSanitization, + PreserveMultipleUnderscores: tt.preserveMultipleUnderscores, + } + got, err := createAttributes(tt.resource, tt.orig, tt.externalLabels, nil, true, labelNamer, tt.extras...) if tt.expectErr { require.Error(t, err) return diff --git a/pkg/translator/prometheusremotewrite/metrics_to_prw.go b/pkg/translator/prometheusremotewrite/metrics_to_prw.go index 43bf1cfe7fce4..5a1a70c66e786 100644 --- a/pkg/translator/prometheusremotewrite/metrics_to_prw.go +++ b/pkg/translator/prometheusremotewrite/metrics_to_prw.go @@ -18,11 +18,13 @@ import ( ) type Settings struct { - Namespace string - ExternalLabels map[string]string - DisableTargetInfo bool - AddMetricSuffixes bool - SendMetadata bool + Namespace string + ExternalLabels map[string]string + DisableTargetInfo bool + AddMetricSuffixes bool + SendMetadata bool + UnderscoreLabelSanitization bool + PreserveMultipleUnderscores bool } // FromMetrics converts pmetric.Metrics to Prometheus remote write format. @@ -53,7 +55,7 @@ func newPrometheusConverter(settings Settings) *prometheusConverter { unique: map[uint64]*prompb.TimeSeries{}, conflicts: map[uint64][]*prompb.TimeSeries{}, metricNamer: otlptranslator.MetricNamer{WithMetricSuffixes: settings.AddMetricSuffixes, Namespace: settings.Namespace}, - labelNamer: otlptranslator.LabelNamer{}, + labelNamer: otlptranslator.LabelNamer{UnderscoreLabelSanitization: settings.UnderscoreLabelSanitization, PreserveMultipleUnderscores: settings.PreserveMultipleUnderscores}, unitNamer: otlptranslator.UnitNamer{}, } } diff --git a/pkg/translator/prometheusremotewrite/metrics_to_prw_v2.go b/pkg/translator/prometheusremotewrite/metrics_to_prw_v2.go index 3dffe1a9dced8..8855463be7a20 100644 --- a/pkg/translator/prometheusremotewrite/metrics_to_prw_v2.go +++ b/pkg/translator/prometheusremotewrite/metrics_to_prw_v2.go @@ -57,7 +57,7 @@ func newPrometheusConverterV2(settings Settings) *prometheusConverterV2 { conflicts: map[uint64][]*writev2.TimeSeries{}, symbolTable: writev2.NewSymbolTable(), metricNamer: otlptranslator.MetricNamer{WithMetricSuffixes: settings.AddMetricSuffixes, Namespace: settings.Namespace}, - labelNamer: otlptranslator.LabelNamer{}, + labelNamer: otlptranslator.LabelNamer{UnderscoreLabelSanitization: settings.UnderscoreLabelSanitization, PreserveMultipleUnderscores: settings.PreserveMultipleUnderscores}, unitNamer: otlptranslator.UnitNamer{}, } } diff --git a/pkg/translator/prometheusremotewrite/testutils_test.go b/pkg/translator/prometheusremotewrite/testutils_test.go index 78e31a132d5b9..fe989ba6fd8d2 100644 --- a/pkg/translator/prometheusremotewrite/testutils_test.go +++ b/pkg/translator/prometheusremotewrite/testutils_test.go @@ -39,6 +39,8 @@ var ( value41 = "test_value41" label51 = "_test_label51" value51 = "test_value51" + label61 = "test#$%^label61" + value61 = "test_value61" dirty1 = "%" dirty2 = "?" traceIDValue1 = "4303853f086f4f8c86cf198b6551df84"