From 475b7ff256468dbdfed3ca97074039d083f216fb Mon Sep 17 00:00:00 2001 From: Arve Knudsen Date: Sat, 21 Dec 2024 18:18:31 +0100 Subject: [PATCH] OTLP receiver: Allow colons in non-standard units Signed-off-by: Arve Knudsen --- CHANGELOG.md | 1 + .../remote/otlptranslator/prometheus/normalize_name.go | 8 ++++---- .../otlptranslator/prometheus/normalize_name_test.go | 10 +++++++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 797bf8de0..a75e163ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * [CHANGE] Notifier: Increment the prometheus_notifications_errors_total metric by the number of affected alerts rather than by one per batch of affected alerts. #15428 * [ENHANCEMENT] OTLP receiver: Convert also metric metadata. #15416 +* [BUGFIX] OTLP receiver: Allow colons in non-standard units. #15710 ## 3.0.1 / 2024-11-28 diff --git a/storage/remote/otlptranslator/prometheus/normalize_name.go b/storage/remote/otlptranslator/prometheus/normalize_name.go index d853cb75a..0a48e2821 100644 --- a/storage/remote/otlptranslator/prometheus/normalize_name.go +++ b/storage/remote/otlptranslator/prometheus/normalize_name.go @@ -22,7 +22,6 @@ import ( "strings" "unicode" - "github.com/prometheus/prometheus/util/strutil" "go.opentelemetry.io/collector/pdata/pmetric" ) @@ -120,18 +119,19 @@ func BuildCompliantName(metric pmetric.Metric, namespace string, addMetricSuffix return metricName } +var nonMetricNameCharRE = regexp.MustCompile(`[^a-zA-Z0-9:]`) + // Build a normalized name for the specified metric. func normalizeName(metric pmetric.Metric, namespace string, allowUTF8 bool) string { var nameTokens []string var separators []string if !allowUTF8 { - nonTokenMetricCharRE := regexp.MustCompile(`[^a-zA-Z0-9:]`) // Split metric name into "tokens" (of supported metric name runes). // Note that this has the side effect of replacing multiple consecutive underscores with a single underscore. // This is part of the OTel 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. nameTokens = strings.FieldsFunc( metric.Name(), - func(r rune) bool { return nonTokenMetricCharRE.MatchString(string(r)) }, + func(r rune) bool { return nonMetricNameCharRE.MatchString(string(r)) }, ) } else { translationFunc := func(r rune) bool { return !unicode.IsLetter(r) && !unicode.IsDigit(r) && r != ':' } @@ -229,7 +229,7 @@ func cleanUpUnit(unit string) string { // This is part of the OTel 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. multipleUnderscoresRE := regexp.MustCompile(`__+`) return strings.TrimPrefix(multipleUnderscoresRE.ReplaceAllString( - strutil.SanitizeLabelName(unit), + nonMetricNameCharRE.ReplaceAllString(unit, "_"), "_", ), "_") } diff --git a/storage/remote/otlptranslator/prometheus/normalize_name_test.go b/storage/remote/otlptranslator/prometheus/normalize_name_test.go index 17b755e04..0473f6cbe 100644 --- a/storage/remote/otlptranslator/prometheus/normalize_name_test.go +++ b/storage/remote/otlptranslator/prometheus/normalize_name_test.go @@ -37,6 +37,8 @@ func TestWhiteSpaces(t *testing.T) { func TestNonStandardUnit(t *testing.T) { require.Equal(t, "system_network_dropped", normalizeName(createGauge("system.network.dropped", "{packets}"), "", false)) + // The normal metric name character set is allowed in non-standard units. + require.Equal(t, "system_network_dropped_nonstandard:_1", normalizeName(createGauge("system.network.dropped", "nonstandard:_1"), "", false)) } func TestNonStandardUnitCounter(t *testing.T) { @@ -68,6 +70,12 @@ func TestHertz(t *testing.T) { func TestPer(t *testing.T) { require.Equal(t, "broken_metric_speed_km_per_hour", normalizeName(createGauge("broken.metric.speed", "km/h"), "", false)) require.Equal(t, "astro_light_speed_limit_meters_per_second", normalizeName(createGauge("astro.light.speed_limit", "m/s"), "", false)) + // The normal metric name character set is allowed in non-standard units. + require.Equal(t, "system_network_dropped_non_per_standard:_1", normalizeName(createGauge("system.network.dropped", "non/standard:_1"), "", false)) + + t.Run("invalid per unit", func(t *testing.T) { + require.Equal(t, "broken_metric_speed_km", normalizeName(createGauge("broken.metric.speed", "km/°"), "", false)) + }) } func TestPercent(t *testing.T) { @@ -89,7 +97,7 @@ func TestAllowUTF8(t *testing.T) { }) t.Run("disallow UTF8", func(t *testing.T) { require.Equal(t, "unsupported_metric_temperature_F", normalizeName(createGauge("unsupported.metric.temperature", "°F"), "", false)) - require.Equal(t, "unsupported_metric_weird", normalizeName(createGauge("unsupported.metric.weird", "+=.:,!* & #"), "", false)) + require.Equal(t, "unsupported_metric_weird", normalizeName(createGauge("unsupported.metric.weird", "+=.,!* & #"), "", false)) require.Equal(t, "unsupported_metric_redundant_test_per_C", normalizeName(createGauge("unsupported.metric.redundant", "__test $/°C"), "", false)) require.Equal(t, "metric_with_foreign_characters", normalizeName(createGauge("metric_with_字符_foreign_characters", "ど"), "", false)) })