utf8: enable utf-8 support by default
This change causes Prometheus to allow all UTF-8 characters in metric and label names. This means that names that were previously invalid and would have been previously rejected will be allowed through. Signed-off-by: Owen Williams <owen.williams@grafana.com>
This commit is contained in:
		
							parent
							
								
									bb232ee109
								
							
						
					
					
						commit
						88bb05c3e8
					
				|  | @ -103,6 +103,8 @@ var ( | |||
| ) | ||||
| 
 | ||||
| func init() { | ||||
| 	// This can be removed when the default validation scheme in common is updated.
 | ||||
| 	model.NameValidationScheme = model.UTF8Validation | ||||
| 	prometheus.MustRegister(versioncollector.NewCollector(strings.ReplaceAll(appName, "-", "_"))) | ||||
| 
 | ||||
| 	var err error | ||||
|  | @ -237,9 +239,6 @@ func (c *flagConfig) setFeatureListOptions(logger log.Logger) error { | |||
| 			case "promql-delayed-name-removal": | ||||
| 				c.promqlEnableDelayedNameRemoval = true | ||||
| 				level.Info(logger).Log("msg", "Experimental PromQL delayed name removal enabled.") | ||||
| 			case "utf8-names": | ||||
| 				model.NameValidationScheme = model.UTF8Validation | ||||
| 				level.Info(logger).Log("msg", "Experimental UTF-8 support enabled") | ||||
| 			case "": | ||||
| 				continue | ||||
| 			default: | ||||
|  |  | |||
|  | @ -42,6 +42,11 @@ import ( | |||
| 	"github.com/prometheus/prometheus/rules" | ||||
| ) | ||||
| 
 | ||||
| func init() { | ||||
| 	// This can be removed when the default validation scheme in common is updated.
 | ||||
| 	model.NameValidationScheme = model.UTF8Validation | ||||
| } | ||||
| 
 | ||||
| const startupTime = 10 * time.Second | ||||
| 
 | ||||
| var ( | ||||
|  |  | |||
|  | @ -62,6 +62,11 @@ import ( | |||
| 	"github.com/prometheus/prometheus/util/documentcli" | ||||
| ) | ||||
| 
 | ||||
| func init() { | ||||
| 	// This can be removed when the default validation scheme in common is updated.
 | ||||
| 	model.NameValidationScheme = model.UTF8Validation | ||||
| } | ||||
| 
 | ||||
| const ( | ||||
| 	successExitCode = 0 | ||||
| 	failureExitCode = 1 | ||||
|  |  | |||
|  | @ -31,12 +31,18 @@ import ( | |||
| 	"testing" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/prometheus/common/model" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| 
 | ||||
| 	"github.com/prometheus/prometheus/model/labels" | ||||
| 	"github.com/prometheus/prometheus/model/rulefmt" | ||||
| ) | ||||
| 
 | ||||
| func init() { | ||||
| 	// This can be removed when the default validation scheme in common is updated.
 | ||||
| 	model.NameValidationScheme = model.UTF8Validation | ||||
| } | ||||
| 
 | ||||
| var promtoolPath = os.Args[0] | ||||
| 
 | ||||
| func TestMain(m *testing.M) { | ||||
|  |  | |||
|  | @ -774,10 +774,10 @@ func (c *ScrapeConfig) Validate(globalConfig GlobalConfig) error { | |||
| 	} | ||||
| 
 | ||||
| 	switch globalConfig.MetricNameValidationScheme { | ||||
| 	case "", LegacyValidationConfig: | ||||
| 	case UTF8ValidationConfig: | ||||
| 	case LegacyValidationConfig: | ||||
| 	case "", UTF8ValidationConfig: | ||||
| 		if model.NameValidationScheme != model.UTF8Validation { | ||||
| 			return fmt.Errorf("utf8 name validation requested but feature not enabled via --enable-feature=utf8-names") | ||||
| 			panic("utf8 name validation requested but model.NameValidationScheme is not set to UTF8") | ||||
| 		} | ||||
| 	default: | ||||
| 		return fmt.Errorf("unknown name validation method specified, must be either 'legacy' or 'utf8', got %s", globalConfig.MetricNameValidationScheme) | ||||
|  |  | |||
|  | @ -62,6 +62,11 @@ import ( | |||
| 	"github.com/prometheus/prometheus/util/testutil" | ||||
| ) | ||||
| 
 | ||||
| func init() { | ||||
| 	// This can be removed when the default validation scheme in common is updated.
 | ||||
| 	model.NameValidationScheme = model.UTF8Validation | ||||
| } | ||||
| 
 | ||||
| func mustParseURL(u string) *config.URL { | ||||
| 	parsed, err := url.Parse(u) | ||||
| 	if err != nil { | ||||
|  | @ -2042,6 +2047,10 @@ var expectedErrors = []struct { | |||
| } | ||||
| 
 | ||||
| func TestBadConfigs(t *testing.T) { | ||||
| 	model.NameValidationScheme = model.LegacyValidation | ||||
| 	defer func() { | ||||
| 		model.NameValidationScheme = model.UTF8Validation | ||||
| 	}() | ||||
| 	for _, ee := range expectedErrors { | ||||
| 		_, err := LoadFile("testdata/"+ee.filename, false, false, log.NewNopLogger()) | ||||
| 		require.Error(t, err, "%s", ee.filename) | ||||
|  | @ -2051,6 +2060,10 @@ func TestBadConfigs(t *testing.T) { | |||
| } | ||||
| 
 | ||||
| func TestBadStaticConfigsJSON(t *testing.T) { | ||||
| 	model.NameValidationScheme = model.LegacyValidation | ||||
| 	defer func() { | ||||
| 		model.NameValidationScheme = model.UTF8Validation | ||||
| 	}() | ||||
| 	content, err := os.ReadFile("testdata/static_config.bad.json") | ||||
| 	require.NoError(t, err) | ||||
| 	var tg targetgroup.Group | ||||
|  | @ -2059,6 +2072,10 @@ func TestBadStaticConfigsJSON(t *testing.T) { | |||
| } | ||||
| 
 | ||||
| func TestBadStaticConfigsYML(t *testing.T) { | ||||
| 	model.NameValidationScheme = model.LegacyValidation | ||||
| 	defer func() { | ||||
| 		model.NameValidationScheme = model.UTF8Validation | ||||
| 	}() | ||||
| 	content, err := os.ReadFile("testdata/static_config.bad.yml") | ||||
| 	require.NoError(t, err) | ||||
| 	var tg targetgroup.Group | ||||
|  | @ -2323,17 +2340,17 @@ func TestScrapeConfigNameValidationSettings(t *testing.T) { | |||
| 		{ | ||||
| 			name:         "global setting implies local settings", | ||||
| 			inputFile:    "scrape_config_global_validation_mode", | ||||
| 			expectScheme: "utf8", | ||||
| 			expectScheme: "legacy", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:         "local setting", | ||||
| 			inputFile:    "scrape_config_local_validation_mode", | ||||
| 			expectScheme: "utf8", | ||||
| 			expectScheme: "legacy", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:         "local setting overrides global setting", | ||||
| 			inputFile:    "scrape_config_local_global_validation_mode", | ||||
| 			expectScheme: "legacy", | ||||
| 			expectScheme: "utf8", | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,6 @@ | |||
| # Two scrape configs with the same job names are not allowed. | ||||
| global: | ||||
|   metric_name_validation_scheme: legacy | ||||
| scrape_configs: | ||||
|   - job_name: prometheus | ||||
|   - job_name: service-x | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| global: | ||||
|   metric_name_validation_scheme: legacy | ||||
| scrape_configs: | ||||
|   - job_name: prometheus | ||||
|     relabel_configs: | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| global: | ||||
|   metric_name_validation_scheme: utf8 | ||||
|   metric_name_validation_scheme: legacy | ||||
| scrape_configs: | ||||
|   - job_name: prometheus | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| global: | ||||
|   metric_name_validation_scheme: utf8 | ||||
|   metric_name_validation_scheme: legacy | ||||
| scrape_configs: | ||||
|   - job_name: prometheus | ||||
|     metric_name_validation_scheme: legacy | ||||
|     metric_name_validation_scheme: utf8 | ||||
|  |  | |||
|  | @ -1,3 +1,3 @@ | |||
| scrape_configs: | ||||
|   - job_name: prometheus | ||||
|     metric_name_validation_scheme: utf8 | ||||
|     metric_name_validation_scheme: legacy | ||||
|  |  | |||
|  | @ -122,9 +122,9 @@ global: | |||
|   [ keep_dropped_targets: <int> | default = 0 ] | ||||
| 
 | ||||
|   # Specifies the validation scheme for metric and label names. Either blank or | ||||
|   # "legacy" for letters, numbers, colons, and underscores; or "utf8" for full | ||||
|   # UTF-8 support. | ||||
|   [ metric_name_validation_scheme <string> | default "legacy" ] | ||||
|   # "utf8" for for full UTF-8 support, or "legacy" for letters, numbers, colons, | ||||
|   # and underscores. | ||||
|   [ metric_name_validation_scheme <string> | default "utf8" ] | ||||
| 
 | ||||
| runtime: | ||||
|   # Configure the Go garbage collector GOGC parameter | ||||
|  | @ -477,10 +477,10 @@ metric_relabel_configs: | |||
| # that will be kept in memory. 0 means no limit. | ||||
| [ keep_dropped_targets: <int> | default = 0 ] | ||||
| 
 | ||||
| # Specifies the validation scheme for metric and label names. Either blank or | ||||
| # "legacy" for letters, numbers, colons, and underscores; or "utf8" for full | ||||
| # UTF-8 support. | ||||
| [ metric_name_validation_scheme <string> | default "legacy" ] | ||||
| # Specifies the validation scheme for metric and label names. Either blank or  | ||||
| # "utf8" for full UTF-8 support, or "legacy" for letters, numbers, colons, and | ||||
| # underscores. | ||||
| [ metric_name_validation_scheme <string> | default "utf8" ] | ||||
| 
 | ||||
| # Limit on total number of positive and negative buckets allowed in a single | ||||
| # native histogram. The resolution of a histogram with more buckets will be | ||||
|  |  | |||
|  | @ -250,10 +250,3 @@ When enabled, Prometheus will change the way in which the `__name__` label is re | |||
| 
 | ||||
| This allows optionally preserving the `__name__` label via the `label_replace` and `label_join` functions, and helps prevent the "vector cannot contain metrics with the same labelset" error, which can happen when applying a regex-matcher to the `__name__` label. | ||||
| 
 | ||||
| ## UTF-8 Name Support | ||||
| 
 | ||||
| `--enable-feature=utf8-names` | ||||
| 
 | ||||
| When enabled, changes the metric and label name validation scheme inside Prometheus to allow the full UTF-8 character set. | ||||
| By itself, this flag does not enable the request of UTF-8 names via content negotiation. | ||||
| Users will also have to set `metric_name_validation_scheme` in scrape configs to enable the feature either on the global config or on a per-scrape config basis. | ||||
|  |  | |||
|  | @ -45,6 +45,11 @@ import ( | |||
| 	"github.com/prometheus/prometheus/util/testutil" | ||||
| ) | ||||
| 
 | ||||
| func init() { | ||||
| 	// This can be removed when the default validation scheme in common is updated.
 | ||||
| 	model.NameValidationScheme = model.UTF8Validation | ||||
| } | ||||
| 
 | ||||
| func TestPopulateLabels(t *testing.T) { | ||||
| 	cases := []struct { | ||||
| 		in            labels.Labels | ||||
|  |  | |||
|  | @ -305,9 +305,9 @@ func (sp *scrapePool) restartLoops(reuseCache bool) { | |||
| 		mrc                      = sp.config.MetricRelabelConfigs | ||||
| 	) | ||||
| 
 | ||||
| 	validationScheme := model.LegacyValidation | ||||
| 	if sp.config.MetricNameValidationScheme == config.UTF8ValidationConfig { | ||||
| 		validationScheme = model.UTF8Validation | ||||
| 	validationScheme := model.UTF8Validation | ||||
| 	if sp.config.MetricNameValidationScheme == config.LegacyValidationConfig { | ||||
| 		validationScheme = model.LegacyValidation | ||||
| 	} | ||||
| 
 | ||||
| 	sp.targetMtx.Lock() | ||||
|  | @ -460,9 +460,9 @@ func (sp *scrapePool) sync(targets []*Target) { | |||
| 		scrapeClassicHistograms  = sp.config.ScrapeClassicHistograms | ||||
| 	) | ||||
| 
 | ||||
| 	validationScheme := model.LegacyValidation | ||||
| 	if sp.config.MetricNameValidationScheme == config.UTF8ValidationConfig { | ||||
| 		validationScheme = model.UTF8Validation | ||||
| 	validationScheme := model.UTF8Validation | ||||
| 	if sp.config.MetricNameValidationScheme == config.LegacyValidationConfig { | ||||
| 		validationScheme = model.LegacyValidation | ||||
| 	} | ||||
| 
 | ||||
| 	sp.targetMtx.Lock() | ||||
|  |  | |||
|  | @ -1040,6 +1040,7 @@ func TestScrapeLoopSeriesAdded(t *testing.T) { | |||
| } | ||||
| 
 | ||||
| func TestScrapeLoopFailWithInvalidLabelsAfterRelabel(t *testing.T) { | ||||
| 	model.NameValidationScheme = model.LegacyValidation | ||||
| 	s := teststorage.New(t) | ||||
| 	defer s.Close() | ||||
| 	ctx, cancel := context.WithCancel(context.Background()) | ||||
|  | @ -3768,6 +3769,7 @@ func testNativeHistogramMaxSchemaSet(t *testing.T, minBucketFactor string, expec | |||
| 	// Create a scrape loop with the HTTP server as the target.
 | ||||
| 	configStr := fmt.Sprintf(` | ||||
| global: | ||||
|   metric_name_validation_scheme: legacy | ||||
|   scrape_interval: 1s | ||||
|   scrape_timeout: 1s | ||||
| scrape_configs: | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue