diff --git a/CHANGELOG.md b/CHANGELOG.md index 67cd308e4ab..33c487724d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ * [ENHANCEMENT] Query-frontend: add experimental support for sharding active series queries via `-query-frontend.shard-active-series-queries`. #6784 * [ENHANCEMENT] Distributor: set `-distributor.reusable-ingester-push-workers=2000` by default and mark feature as `advanced`. #7128 * [ENHANCEMENT] All: set `-server.grpc.num-workers=100` by default and mark feature as `advanced`. #7131 +* [ENHANCEMENT] Distributor: invalid metric name error message gets cleaned up to not include non-ascii strings. #7146 * [BUGFIX] Ingester: don't ignore errors encountered while iterating through chunks or samples in response to a query request. #6451 * [BUGFIX] Fix issue where queries can fail or omit OOO samples if OOO head compaction occurs between creating a querier and reading chunks #6766 * [BUGFIX] Fix issue where concatenatingChunkIterator can obscure errors #6766 diff --git a/pkg/distributor/validate.go b/pkg/distributor/validate.go index 731559d87bd..15b6a2660e5 100644 --- a/pkg/distributor/validate.go +++ b/pkg/distributor/validate.go @@ -8,7 +8,9 @@ package distributor import ( "errors" "fmt" + "strings" "time" + "unicode" "unicode/utf8" "github.com/prometheus/client_golang/prometheus" @@ -310,6 +312,25 @@ type labelValidationConfig interface { MaxLabelValueLength(userID string) int } +func removeNonASCIIChars(in string) (out string) { + foundNonASCII := false + + out = strings.Map(func(r rune) rune { + if r <= unicode.MaxASCII { + return r + } + + foundNonASCII = true + return -1 + }, in) + + if foundNonASCII { + out = out + " (non-ascii characters removed)" + } + + return out +} + // validateLabels returns an err if the labels are invalid. // The returned error may retain the provided series labels. func validateLabels(m *sampleValidationMetrics, cfg labelValidationConfig, userID, group string, ls []mimirpb.LabelAdapter, skipLabelNameValidation bool) error { @@ -321,7 +342,7 @@ func validateLabels(m *sampleValidationMetrics, cfg labelValidationConfig, userI if !model.IsValidMetricName(model.LabelValue(unsafeMetricName)) { m.invalidMetricName.WithLabelValues(userID, group).Inc() - return fmt.Errorf(invalidMetricNameMsgFormat, unsafeMetricName) + return fmt.Errorf(invalidMetricNameMsgFormat, removeNonASCIIChars(unsafeMetricName)) } numLabelNames := len(ls) diff --git a/pkg/distributor/validate_test.go b/pkg/distributor/validate_test.go index 47ce1a2b49b..32f2877a8ce 100644 --- a/pkg/distributor/validate_test.go +++ b/pkg/distributor/validate_test.go @@ -80,6 +80,11 @@ func TestValidateLabels(t *testing.T) { false, fmt.Errorf(invalidMetricNameMsgFormat, " "), }, + { + map[model.LabelName]model.LabelValue{model.MetricNameLabel: "metric_name_with_\xb0_invalid_utf8_\xb0"}, + false, + fmt.Errorf(invalidMetricNameMsgFormat, "metric_name_with__invalid_utf8_ (non-ascii characters removed)"), + }, { map[model.LabelName]model.LabelValue{model.MetricNameLabel: "valid", "foo ": "bar"}, false, @@ -162,7 +167,7 @@ func TestValidateLabels(t *testing.T) { cortex_discarded_samples_total{group="custom label",reason="label_name_too_long",user="testUser"} 1 cortex_discarded_samples_total{group="custom label",reason="label_value_too_long",user="testUser"} 1 cortex_discarded_samples_total{group="custom label",reason="max_label_names_per_series",user="testUser"} 1 - cortex_discarded_samples_total{group="custom label",reason="metric_name_invalid",user="testUser"} 1 + cortex_discarded_samples_total{group="custom label",reason="metric_name_invalid",user="testUser"} 2 cortex_discarded_samples_total{group="custom label",reason="missing_metric_name",user="testUser"} 1 cortex_discarded_samples_total{group="custom label",reason="random reason",user="different user"} 1